1 of 25

External fuzzing of USB drivers with syzkaller

Andrey Konovalov, xairy.io

SAFACon, Belgrade�May 3rd, 2025

xairy.io

2 of 25

Unlocking Android phones via USB bugs is possible

  • <Redacted> zero-day exploit used to target phone of <redacted> activist
    • Android phone reportedly unlocked via bugs in USB drivers
    • CVE-2024-53104 — OOB write in UVC (USB Video Class) driver
    • CVE-2024-53197 — OOB write in creative Extigy USB sound card driver
    • CVE-2024-50302 — info-leak over USB in Anton Touch Pad HID driver
  • Why locked Android phones allow connecting USB devices is a mystery
    • Lots of drivers enabled, huge attack surface

2

xairy.io

3 of 25

syzkaller and USB fuzzing

  • syzkaller — coverage-guided grammar-based kernel fuzzer
  • Mainly targets Linux kernel but supports other OSes
  • Found thousands of bugs — syzkaller.appspot.com
  • Supports "externally" fuzzing Linux kernel USB drivers
  • 300+ bugs reported publicly
  • Agenda: How USB fuzzing works + How to find more USB bugs

3

xairy.io

4 of 25

USB fuzzing with syzkaller

4

xairy.io

5 of 25

USB is host-driven — Enumeration [simplified]

Host

Device

Device plugged into Host

— What are you?

— I'm a webcam

— What kind of settings do you have?

— These are my settings

(Sends USB descriptors)

(Sends GET_INFO responses)

— Alright! You're now connected

(Sends requests)

(Responds to requests)

(Sends GET_INFO requests)

(Sends GET_DESCRIPTOR requests)

(Sends SET_CONFIGURATION request)

xairy.io

6 of 25

USB is host-driven — Subsequent communication

Host

Device

— What do you see?

— Here's the current frame

— What do you see?

— Here's the current frame

— What do you see?

(Sends requests)

(Responds to requests)

— Here's the current frame

xairy.io

7 of 25

Linux USB Host stack

7

USB Host Controller

USB port

USB Host Controller driver

USB Core

USB HID

USB Mass Storage

...

Userspace

Kernel

To USB Device

⇐ syzkaller allows targeting this

⇐ While (kind of) controlling this

Hardware

Userspace apps

xairy.io

8 of 25

syzkaller uses Raw Gadget and Dummy HCD/UDC

8

USB Gadget Core

Userspace

Kernel

Raw Gadget

syz-executor

USB Core

USB HID

USB Mass Storage

...

Dummy HC driver

Dummy DC driver

No external hardware required!

In-VM or

on-device

Hardware

xairy.io

9 of 25

Fuzzing USB is tricky

  • USB is host-driven protocol
    • Device cannot just send data on its own
    • Host sends requests to Device and Device must respond accordingly

9

xairy.io

10 of 25

Fuzzing USB is tricky but syzkaller can

  • USB is host-driven protocol
    • Device cannot just send data on its own
    • Host sends requests to Device and Device must respond accordingly

  • syzkaller provides pseudo-syscalls that cake care of headache
    • syz_usb_connect enumerates Device by responding to Host requests�based on USB descriptors provided as arguments (defined in syzlang)
    • Other pseudo-syscalls allow post-enumeration communication

10

xairy.io

11 of 25

USB device/config/iface descriptors structure

11

USB device

descriptor

USB configuration

descriptor

USB interface

descriptor #1

USB interface

descriptor #2

Extra descriptors

Extra descriptors

...

There can be more configurations, but syzkaller supports one per device

Descriptors for device type need to be defined in syzlang�to fuzz driver of that device

xairy.io

12 of 25

Trying to rediscover CVE-2024-53104

12

xairy.io

13 of 25

Fix for CVE-2024-53104

13

xairy.io

14 of 25

UVC-specific syz_usb_connect variant

syz_usb_connect$uvc( # Pseudo-syscall for connecting UVC devices

speed flags[usb_device_speed],

dev_len len[dev],

dev ptr[in, usb_device_descriptor_uvc], # UVC-specific descriptors

conn_descs ptr[in, vusb_connect_descriptors]

) fd_usb (timeout[3000], prog_timeout[3000], remote_cover)

14

Define descriptor structure of UVC device,

written based on source of UVC driver

(to define descriptors that driver handles)

xairy.io

15 of 25

USB IDs in Linux kernel UVC driver

static const struct usb_device_id uvc_ids[] = {

...

/* Logitech HD Pro Webcam C920 */

{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,

.idVendor = 0x046d,

.idProduct = 0x082d,

.bInterfaceClass = USB_CLASS_VIDEO,

.bInterfaceSubClass = 1,

.bInterfaceProtocol = 0,

.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT | ... },

...

}

15

Belong to

device descriptor

Belong to

interface descriptor

Used to match driver to device

There are many devices

supported by this driver;

just choose one

Report mentions Chicony device with specific quirk,

but I think that quirk has nothing to do with bug

xairy.io

16 of 25

Descriptions, descriptions [1/6]

usb_device_descriptor_uvc {

inner usb_device_descriptor_t[USB_CLASS_MISC, 0, 0, 0x46d, 0x821, 64,

array[usb_config_descriptor_uvc, 1]]

} [packed]

usb_config_descriptor_uvc {

inner usb_config_descriptor_t[const[1, int8], const[2, int8],

usb_interface_descriptors_uvc]

} [packed]

16

Vendor/Product IDs to

match driver to device

xairy.io

17 of 25

Descriptions, descriptions [2/6]

usb_interface_descriptors_uvc {

control usb_interface_descriptor_fixed_t[1, 0, 0,

USB_CLASS_VIDEO, 1, 0, uvc_vc_header, void]

streaming usb_interface_descriptor_fixed_t[2, 0, 0,

USB_CLASS_VIDEO, 2, 0, uvc_streaming, void]

# Might have more interfaces� # and other extra descriptors.

} [packed]

17

Control interface used to �match driver to device

(via Class/SubClass/Protocol)

and set up streaming interfaces

Streaming interface used to�stream data from device

(but we never get to this

when triggering bug)

Driver parses extra�descriptors for all handled interfaces

(this is where bug is)

xairy.io

18 of 25

Descriptions, descriptions [3/6]

uvc_vc_header {

bLength len[parent, int8]

bDescriptorType const[USB_DT_CS_INTERFACE, int8]

bDescriptorSubType const[UVC_VC_HEADER, int8]

bcdUVC int16

wTotalLength len[parent, int16]

dwClockFrequency int32

bInCollection len[baInterfaceNr, int8]

baInterfaceNr array[int8, 1:8] # Supposed to be interface numbers,

} [packed] # let syzkaller guess.

18

xairy.io

19 of 25

Descriptions, descriptions [4/6]

uvc_streaming {

# Can have output header instead, but keep it simple.

input_header uvc_vs_input_header

frames_and_formats array[uvc_vs_frame_or_format, 1:8]

} [packed]

19

xairy.io

20 of 25

Descriptions, descriptions [5/6]

uvc_vs_input_header {

bLength len[parent, int8]

bDescriptorType const[USB_DT_CS_INTERFACE, int8]

bDescriptorSubType const[UVC_VS_INPUT_HEADER, int8]

bNumFormats len[bmaControls, int8]

wTotalLength len[uvc_streaming, int16]

bEndpointAddress int8

bmInfo int8

bTerminalLink int8

bStillCaptureMethod int8

bTriggerSupport int8

bTriggerUsage int8

bControlSize const[1, int8] # Fix control size to 1 to simplify defining bmaControls.

bmaControls array[int8, 1:8]

} [packed]

20

xairy.io

21 of 25

Descriptions, descriptions [6/6]

uvc_vs_frames_and_formats_subtypes = UVC_VS_UNDEFINED, UVC_VS_STILL_IMAGE_FRAME, UVC_VS_FORMAT_UNCOMPRESSED, UVC_VS_FRAME_UNCOMPRESSED, UVC_VS_FORMAT_MJPEG, UVC_VS_FRAME_MJPEG, UVC_VS_FORMAT_MPEG2TS, UVC_VS_FORMAT_DV, UVC_VS_COLORFORMAT, UVC_VS_FORMAT_FRAME_BASED, UVC_VS_FRAME_FRAME_BASED, UVC_VS_FORMAT_STREAM_BASED

uvc_vs_frame_or_format {

bLength len[parent, int8]

bDescriptorType const[USB_DT_CS_INTERFACE, int8]

bDescriptorSubType flags[uvc_vs_frames_and_formats_subtypes, int8]

data array[int8] # Data depends on subtype, but keep it simple.

} [packed]

21

xairy.io

22 of 25

22

Bug triggered�after few minutes

of fuzzing on laptop

Is this CVE-2024-53104?

(read here vs write there)

Maybe, needs analysis

Targeting kernel 6.12

xairy.io

23 of 25

Reproducer for found bug

syz_usb_connect$uvc(0x0, 0x4e, &(0x7f0000000000)={{0x12, 0x1, 0x110, 0xef, 0x0, 0x0, 0x8, 0x46d, 0x821, 0x40, 0x1, 0x2, 0x3, 0x1, [{{0x9, 0x2, 0x3c, 0x1, 0x1, 0x1, 0x60, 0x44, {{0x9, 0x4, 0x1, 0x0, 0x0, 0xe, 0x1, 0x0, 0x0, {0xd, 0x24, 0x1, 0x400, 0xd, 0x5, 0x1, "02"}}, {0x9, 0x4, 0x2, 0x0, 0x0, 0xe, 0x2, 0x0, 0x0, {{0xe, 0x24, 0x1, 0x1, 0x14, 0x1, 0x7, 0x1, 0x1, 0x80, 0xb, 0x1, "7f"}, [{0x3, 0x24, 0x11}, {0x3, 0x24, 0xc}]}}}}}]}}, 0x0)

23

xairy.io

24 of 25

More info and offer to action

  • How to find 0-days in USB drivers:
  • Figure out how USB works
  • Figure out how to use syzkaller
  • Write syzlang descriptions to target relevant USB drivers
  • Collect 0-days (there are lots, driver code is bad)

24

xairy.io

25 of 25

💜 Thank you!

25

xairy.io