Skip to content

Linux USB gadget / configfs attack surface

USB gadget subsystem'i iki yönlü bir ring0 attack surface'idir: configfs/functionfs ile bir gadget compose etme yolu (lifetime/refcount race'leri, teardown UAF'leri) ve host tarafında kötü niyetli bir device'ın descriptor/setup-packet parsing yolu — her ikisi de authentication'dan önce, düşük ayrıcalıkla erişilebilir.

Mechanism

Linux USB gadget subsystem'i bir host'un gördüğü USB device'ını yazılımda üretir. Bir gadget üç ayrı yerden sürülebilir: configfs üzerinden statik composition (/sys/kernel/config/usb_gadget/...), FunctionFS (f_fs) üzerinden userspace'in ep0'ı ve endpoint'leri beslediği fonksiyonlar ve Raw Gadget / Dummy HCD ile tamamen emüle edilmiş controller. Bir device UDC'ye bind edildiğinde host onu enumerate eder ve descriptor'ları parse eder.

Bu iki yol iki farklı bug sınıfı doğurur:

Note

İhlal edilen invariant iki katmanlıdır. (1) Host parsing tarafı: device'tan gelen bir bLength/wTotalLength/count/index alanına, gerçekte alınan buffer'a karşı asla güvenilmemeli — bozuk descriptor slab-out-of-bounds ve UAF verir (bkz. usb-descriptor-parsing-bug-class). (2) Gadget composition tarafı: configfs/functionfs, kernel object'lerinin lifetime'ını userspace tarafından sürülen mkdir/rmdir/symlink ve open/close olaylarına bağlar. Bir object'in refcount'u veya state machine'i (ör. ffs->opened, gadget config switch, UDC pullup) bu concurrent olaylar altında yanlış yönetilirse, teardown bir başka thread hâlâ pointer'ı tutarken belleği free eder — klasik lifetime UAF. Boundary şu yüzden aşılır: ayrıcalıksız kullanıcı-sürülen bir olay dizisi, ring0 object teardown'ının sıralamasını kontrol edebilir.

Warning

Descriptor-parsing tarafına fiziksel BadUSB, emüle bir gadget veya USB/IP ile ulaşılır; hiçbir credential gerekmez ve auto-bind eden bir victim'de kötü niyetli bir kablo doğrudan parser'a değer. Composition tarafındaki race'ler ise, gadget configfs/functionfs'e erişimi olan (containerize edilmiş ya da ADB/daemon aracılığıyla) local bir aktör için ulaşılabilirdir.

Somut, public ve yamalı örnekler:

  • CVE-2022-50704"USB: gadget: Fix use-after-free during usb config switch": RNDIS'ten başka bir config'e geçerken hardware ->pullup callback'i desteklemiyor ya da düşük olasılıklı bir fault ile fail ediyorsa, composite_disconnectgether_disconnectrndis_close yolu free edilmiş belleğe dokunur ve panic/UAF üretir. Bug, teardown'ın hardware davranışından bağımsız olmasını sağlamamaktan kaynaklanır.
  • FunctionFS ffs->opened open/removal race: ffs->opened == 1 (açık ep0) iken bir thread ffs_data_opened()'dan önce preempt edilir; başka bir thread ep0'ı kapatır, ->opened 0'a düşer, ffs_data_clear()ffs_epfiles_destroy() epfiles array'ini free eder. İlk thread'in file->private_data'sı artık free array'e bakar; open başarılı olur ve sonraki her read()/write() epfile pointer'ı üzerinden UAF olur.

Walkthrough

Yüksek seviyeli, kavramsal iki senaryo — public dökümana dayanır, silahlandırılmış zincir yok.

A. configfs ile bir gadget compose etme (host tarafını fuzz'a maruz bırakma):

# /sys/kernel/config/usb_gadget altında
mkdir g1 && cd g1
echo 0x1d6b > idVendor ; echo 0x0104 > idProduct
mkdir configs/c.1
mkdir functions/ffs.usb0        # ya da rndis.0 vb.
ln -s functions/ffs.usb0 configs/c.1/
echo <udc_name> > UDC           # bind → host enumerate etmeye başlar
  • functionfs seçilirse userspace, mount edilmiş ffs üzerinden ep0'a descriptor + string tablosunu write() eder ve endpoint dosyalarını open eder; ep0'ı kapatmak/mount'u teardown etmek yukarıdaki lifetime race'lerini tetikleyebilen olaylardır.
  • Fuzzing kurulumunda gadget tarafı Raw Gadget + Dummy HCD ile emüle edilir; böylece host parser'ına donanımsız, tek kernel içinde bozuk descriptor sunulabilir.

B. Config switch teardown race'ini kavramsal olarak sergileme (CVE-2022-50704 sınıfı):

  • Gadget'ı RNDIS içeren bir config ile bind et, host'un config'i seçmesini sağla.
  • UDC pullup'ı fail eden ya da yavaşlatan bir durumda config'i switch/unbind et; teardown yolu (rndis_close) devam ederken ilgili object free edilmişse UAF gözlenir.
KASAN'ın gadget teardown yolunda gösterdiği tipik iz
BUG: KASAN: use-after-free in rndis_close+0x..
Call Trace:
  rndis_close
  gether_disconnect
  rndis_disable
  composite_disconnect

Detection

  • Test/CI kernel'lerini CONFIG_KASAN=y (+ CONFIG_KFENCE=y) ile derle; USB reproducer'larını Raw Gadget + Dummy HCD üzerinde çalıştır. KASAN hem parser OOB/UAF'ini hem gadget teardown UAF'ini call trace ile gösterir.
  • Host tarafında USB authorization ve enumeration'ı denetle: /sys/bus/usb/devices/usbN/authorized, beklenmedik device-class bind'leri, çok kısa aralıklı connect/disconnect (BadUSB / gadget spoofing sinyali).
  • Endpoint host'larında (Android/embedded) configfs/functionfs olaylarını izle: kısa süreli tekrar eden gadget bind/unbind ve UDC pullup fail'leri, teardown race'lerini tetiklemeye çalışan davranışın işareti olabilir.
  • syzbot dashboard'unda USB/gadget bug akışını takip et; bu yüzeyden sürekli yeni raporlar gelir.

Mitigation

  • Patch tut: CVE-2022-50704 gibi teardown UAF'leri upstream fix commit'leriyle kapatılır; stable kernel'e güncelle.
  • FunctionFS open/removal race'i için fix, opener'ları ffs->mutex üzerinde serialize eder, dynamic dosyalarda atomic_inc_not_zero() kullanır (0 ise fail) ve open'da state'in FFS_ACTIVE olduğunu ve inode'un removed işaretlenmediğini doğrular — yani refcount/state invariant'ını yeniden kurar.
  • Attack surface'i daralt: gadget subsystem'ini gerektirmeyen host'larda CONFIG_USB_GADGET/functionfs/dummy_hcd modüllerini derleme ya da yükleme; container'lara configfs//dev/raw-gadget erişimi verme.
  • Host tarafı: CONFIG_USB_DEFAULT_AUTHORIZATION_MODE / USBGuard ile bilinmeyen device'ları reddet, kullanılmayan class driver'ları disable et, USB port'larını kilitle; descriptor parser'larını da yamalı tut (bkz. usb-descriptor-parsing-bug-class).
  • Genel hardening: KASAN/KFENCE ile CI, syzkaller USB fuzzing'ini akışta tut.

References