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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
Pi-Zero-2W-Bad-USB — Pi Zero 2W를 BadUSB로 구성하세요 | Kitploit
도구/GitHubGitHub/psycostea/pi-zero-2w-bad-usb
Embedded Systems SecurityExploitationScripting & AutomationHardware HackingCTFPenetration TestingLearning & EducationRed TeamingPayload Development
GitHubpsycostea/pi-zero-2w-bad-usb

Pi-Zero-2W-Bad-USB

Pi Zero 2W를 BadUSB로 구성하세요

3722개월 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
저장소 보기

Raspberry Pi Zero 2 W BadUSB HID 툴킷

Raspberry Pi Zero 2 W를 기반으로 제작된 프로그래밍 가능한 BadUSB / HID 공격 플랫폼입니다. Pi는 USB 복합 장치(키보드 + 선택적인 읽기 전용 대용량 저장 볼륨)로 인식되며, 연결된 호스트를 대상으로 Ducky-Script 스타일의 페이로드를 실행합니다. 공인 레드팀 모의 침투, 침투 테스트, CTF를 위해 설계되었습니다.

⚠️ 법적 고지. 소유하고 있거나 명시적인 서면 허가를 받은 시스템에서만 사용하십시오. 허가되지 않은 접근은 대부분의 국가에서 불법이며, 몰랐다고 해도 변명이 되지 않습니다.


목차

  1. 기능
  2. 작동 방식
  3. 하드웨어
  4. 설치
  5. 일상 운영
  6. 페이로드 작성
  7. 설정 참조
  8. 아키텍처 참고 사항
  9. 테스트
  10. 문제 해결
  11. 저장소 구조
  12. 크레딧

기능

  • 프로그래밍 가능한 키 입력 페이로드 — Ducky-Script 호환 문법 기반.
  • 신뢰할 수 있는 호스트 연결 감지 — UDC configured 상태를 활용하므로 부팅 시 페이로드가 오작동으로 실행되지 않습니다.
  • Pi Zero 2 W 하드웨어에서 실제로 동작하는 "공격 간 재무장(reset)". 분리했다가 다시 꽂으면 다시 실행됩니다 — 전원을 껐다 켤 필요가 없습니다.
  • 루프 과다 실행 방지용 구성 가능한 안전장치 — 장치를 꽂은 채로 두어도 문제없도록 최소 실행 간격과 분당 실행 횟수 제한을 설정할 수 있으며, 둘 다 환경 변수로 덮어쓸 수 있습니다.
  • 선택적 복합 대용량 저장소 가젯 — 플랫 이미지 파일 기반(기본적으로 읽기 전용 — 실제 SD 카드를 노출하던 기존 방식은 디버깅이 어려운 파일시스템 손상 문제를 일으켰습니다).
  • 페이로드별로 선택 가능한 US/UK 키보드 레이아웃 — LAYOUT 지시어 사용.
  • 변수, IF / ELSE, WHILE, HOLD / RELEASE, INJECT_MOD, RANDOM_* 생성기, STRING_BLOCK / STRINGLN_BLOCK, DEFINE 지원.
  • systemd 통합 — ExecStop이 가젯을 깔끔하게 해체합니다. configfs 상태가 남지 않으며, 재부팅 없이도 재시작이 가능합니다.
  • udev로 관리되는 /dev/hidg0 권한 — 전 세계 쓰기 가능한 디바이스 노드가 없습니다.
  • Ducky 파서용 34개 pytest 테스트 스위트 — Pi 없이도 모든 호스트에서 실행 가능.
  • 각 페이로드 실행 후 ACT LED 상태 표시.

작동 방식

root@kitploit:~
+----------------+        USB cable        +-------------+
|  Raspberry Pi  | ===================>>>  |  Host PC    |
|  Zero 2 W      |   (Pi emulates HID +    |  (target)   |
|  (this device) |    optional drive)      |             |
+----------------+                         +-------------+
        |
        |  /home/pi/pi-badusb/
        |
        +-- badusb.service ----> monitor_and_run.py
                                   |
                                   |  on `state == configured`:
                                   v
                                 run_payload.py
                                   |
                                   v
                                 /dev/hidg0  (USB HID gadget)
  1. 부팅 시 badusb.service systemd 유닛이 gadget_setup.sh를 실행합니다. 이 스크립트는 configfs/libcomposite를 사용해 HID 키보드 기능과 (선택적으로) /var/badusb/storage.img의 플랫 이미지 파일을 백업으로 하는 대용량 저장소 LUN을 가진 USB 가젯을 구성합니다.
  2. 그런 다음 유닛이 monitor_and_run.py를 실행합니다. 이 스크립트는 /sys/class/udc/<udc>/state를 폴링하며 configured 상태를 기다립니다 — 이는 USB 규격상 호스트가 가젯을 성공적으로 열거(enumeration)했음을 뜻합니다. (/dev/hidg0의 존재 여부를 신호로 사용하지 않습니다: 해당 노드는 부팅 시 가젯이 UDC에 바인딩되는 순간, 케이블 연결 여부와 관계없이 생성되기 때문입니다.)
  3. 호스트가 연결되면 리스너가 run_payload.py를 실행합니다. 이 스크립트는 payload.txt를 파싱하고 HID 리포트를 /dev/hidg0에 기록합니다.
  4. 페이로드가 끝나면 리스너는 가젯을 UDC에서 능동적으로 언바인딩합니다 (Pi Zero 2 W는 물리적 연결 해제를 소프트웨어로 감지할 수 없습니다 — 아키텍처 참고 사항 참조), 쿨다운 시간 동안 대기한 후 다시 바인딩합니다. 이후 장치는 다음 configured 전환을 기다립니다.

하드웨어

구성 요소설명
Raspberry Pi Zero 2 W2026-05 하드웨어 개정판에서 테스트 완료. BCM2835 dwc_otg 드라이버를 사용하는 구형 Pi Zero W도 원리적으로는 동작하지만, 이 README는 dwc2를 사용하는 2 W 기준입니다.
micro-USB to USB-A 케이블또는 Pi를 호스트 포트에 직접 꽂을 수 있는 "USB 스틱" 폼팩터 어댑터.
선택 사항: 별도 충전기타깃에 꽂기 전에 리스너가 부팅될 수 있도록 호스트가 아닌 전원으로 Pi에 전원을 공급하려는 경우 (그렇지 않으면 호스트 포트가 전원을 공급합니다).

Pi Zero 2 W에는 micro-USB 포트가 두 개 있습니다:

  • PWR IN — 전원 전용. USB 데이터 라인이 dwc2 OTG 블록에 노출되지 않습니다.
  • USB — OTG 데이터 포트. 타깃에 연결하는 포트입니다.

소프트웨어


설치

저장소를 Pi에 클론하거나 복사한 후 설치 프로그램을 실행합니다:

root@kitploit:~
git clone http://your-gitea/admin/Pi-Zero-2W-Bad-USB.git /home/pi/pi-badusb
cd /home/pi/pi-badusb
sudo ./install.sh
sudo reboot

재부팅 후 서비스를 활성화하고 시작합니다:

root@kitploit:~
sudo systemctl enable --now badusb.service
journalctl -u badusb -f

install.sh는 멱등(idempotent) 입니다 — 프로젝트 파일을 변경할 때마다 다시 실행하면 됩니다. 이 스크립트는:

  • /boot/firmware(Bookworm+)와 /boot(구형)를 자동 감지합니다.
  • config.txt의 [all] 블록 아래에 dtoverlay=dwc2,dr_mode=otg가 활성화되도록 보장합니다. Raspberry Pi Imager 기본값은 이 줄을 Pi Zero 2 W에 적용되지 않는 [cm5] 필터 안에 넣기 때문에, 설치 프로그램은 재실행 시 중복되지 않도록 표식(sentinel)이 있는 오버라이드를 추가합니다.
  • cmdline.txt에 modules-load=dwc2가 포함되도록 보장합니다.
  • cmdline.txt에 g_ether가 남아 있으면 경고합니다 (g_ether가 libcomposite에서 UDC를 가로채 가젯 모드를 망가뜨립니다).
  • systemd 유닛을 /etc/systemd/system/badusb.service에 설치합니다.
  • /etc/udev/rules.d/99-badusb-hidg.rules에 udev 규칙을 설치하여 가 그룹에 의해 그룹 쓰기 가능하도록 합니다.

일상 운영

root@kitploit:~
# 시작 / 중지 / 재시작
sudo systemctl start badusb
sudo systemctl stop badusb
sudo systemctl restart badusb              # 연결된 상태에서도 안전합니다

# 실시간 로그 보기
journalctl -u badusb -f

# 부팅 시 자동 시작 비활성화
sudo systemctl disable badusb

# 타이밍 조정 (override drop-in 생성)
sudo systemctl edit badusb
# (Environment="BADUSB_REARM_COOLDOWN_S=8" 등이 포함된 [Service] 블록을 붙여넣으세요)
sudo systemctl restart badusb

이 서비스는 sys-kernel-config.mount와 UDC의 존재에 의존하므로, 가젯이 실제로 준비되기 전에는 페이로드를 실행할 수 없습니다.

페이로드를 수정해도 재시작할 필요가 없습니다 — payload.txt는 꽂을 때마다 새로 읽습니다.


페이로드 작성

payload.txt는 설치 디렉터리에 있습니다. 전체 명령어 레퍼런스는 payload_commands.md에 있습니다.

최소 예제

root@kitploit:~
REM Open Run dialog and type a greeting via Notepad
LAYOUT US
GUI r
DELAY 1500
STRING notepad
ENTER
DELAY 2500
STRINGLN Hello from the Pi Zero 2 W

변수, 조건문, 반복문

root@kitploit:~
VAR $USER="alice"
VAR $COUNT=0
WHILE $COUNT < 3
  STRINGLN Hello $USER (iteration $COUNT)
  VAR $COUNT = $COUNT + 1
END_WHILE

IF $USER == "alice"
  STRINGLN matched
ELSE
  STRINGLN missed
END_IF

VAR의 수식은 ast 워커 기반 안전 평가기(safe evaluator)를 거칩니다 — 이름, 호출, 속성 접근은 허용되지 않으며 숫자 리터럴과 + - * / // % **만 허용됩니다.

수정자(modifier) 유지

root@kitploit:~
HOLD SHIFT
STRINGLN this line is in capitals
RELEASE SHIFT

임의의 수정자 조합 전송

root@kitploit:~
REM Hold Ctrl+Shift (0x01 + 0x02) and tap A
INJECT_MOD 0x03
STRING a
REM Release all modifiers
INJECT_MOD 0x00

무작위 생성

root@kitploit:~
RANDOM_LETTER 12      # 12 random mixed-case letters
RANDOM_NUMBER 6       # 6 random digits
RANDOM_SPECIAL 4      # 4 random ASCII symbols

키보드 레이아웃

root@kitploit:~
LAYOUT UK             # switch to UK ISO mappings for subsequent STRING/STRINGLN
STRING @ " # ~ £ \ |  # types correctly on a UK-locale host

keymaps/ 디렉터리(us.py, uk.py 옆)에 다른 파일을 추가하면 LAYOUT <name> 지시어가 importlib로 해당 파일을 불러옵니다.


설정 참조

대용량 저장소 가젯 (gadget_setup.sh 상단)

이 값들은 gadget_setup.sh를 수동으로 실행할 때 환경 변수로 설정해 호출별로 덮어쓰거나, 유닛의 Environment= 지시어로 전역 설정할 수 있습니다.

리스너 튜닝 값 (monitor_and_run.py)

다음과 같이 덮어씁니다:

root@kitploit:~
sudo systemctl edit badusb
# In the editor:
# [Service]
# Environment="BADUSB_REARM_COOLDOWN_S=8"
# Environment="BADUSB_MIN_INTER_FIRE_S=20"
sudo systemctl restart badusb

아키텍처 참고 사항

리스너가 /dev/hidg0 대신 UDC state를 사용하는 이유

이전 구현은 /dev/hidg0의 존재 + 쓰기 가능 여부를 "호스트 연결" 신호로 사용했습니다. 그런데 이 디바이스 노드는 부팅 시 가젯이 UDC에 바인딩되는 순간 — 실제로 호스트가 열거하기 훨씬 전에 — 생성됩니다. 그래서 Pi가 어디에 꽂혀 있든 전원이 켜지면 즉시 페이로드가 실행되었습니다.

신뢰할 수 있는 신호는 /sys/class/udc/<udc>/state로, USB 규격의 장치 상태를 보고합니다. configured만이 호스트가 SetConfiguration(1)을 발행했음을 의미합니다 — 이제부터 장치가 HID 리포트를 보낼 자격이 생깁니다.

각 페이로드 후 강제 언바인딩을 하는 이유

Pi Zero 2 W에서 물리적 연결 해제를 감지하는 것은 소프트웨어로는 불가능합니다: 이 보드에는 VBUS 감지가 SoC의 dwc2 OTG 블록에 배선되어 있지 않습니다. 물리적으로 분리한 후에도:

  • /sys/class/udc/<udc>/state는 configured 상태를 유지합니다.
  • current_speed는 high-speed를 유지합니다.
  • dwc2 GOTGCTL 레지스터는 0x000d0000 (BSesVld 비트 설정)을 유지합니다.
  • udev 이벤트가 발생하지 않습니다.

따라서 절대 오지 않을 신호를 기다리는 대신, 리스너가 연결 해제를 능동적으로 유발합니다: 각 페이로드 후 가젯의 UDC configfs 파일에 ""를 기록하여(커널이 이를 언바인딩으로 해석) BADUSB_REARM_COOLDOWN_S 동안 대기한 다음, UDC 이름을 다시 기록해 재바인딩합니다. 다음 호스트 연결 시 리스너가 감지할 수 있는 깨끗한 configured 전환이 발생합니다.

운영자가 페이로드 실행 후 Pi를 계속 꽂아 두면, 재바인딩으로 인해 호스트가 가젯을 다시 열거합니다. 페이로드 무한 실행 루프를 방지하기 위해 두 가지 안전장치가 작동합니다:

  1. BADUSB_MIN_INTER_FIRE_S — 이전 실행 시점부터 이 시간 창 안에 configured 전환이 발생하면 실행을 억제하고 다시 언바인딩합니다. 가젯은 백그라운드에서 조용히 순환합니다.
  2. BADUSB_MAX_FIRES_PER_MINUTE — 슬라이딩 윈도우 하드 상한. 상한을 초과하면 리스너가 BADUSB_RATELIMIT_PAUSE_S 동안 일시 중지하고 경고를 기록합니다.

configfs에 rm -rf를 절대 사용하지 않는 이유

configfs의 커널 관리 속성 파일(bcdUSB, idVendor, webusb/*, os_desc/*, …)은 rm(2)로 제거할 수 없습니다 — 커널이 EPERM을 반환합니다. 이 파일들은 상위 디렉터리가 rmdir될 때만 해제됩니다. gadget_setup.sh와 teardown_gadget.sh는 모두 canonical libcomposite 순서로 configfs 트리를 탐색하며 — 속성 파일에 rm을 절대 사용하지 않고 rmdir만 사용합니다 — 나머지는 커널이 자동으로 정리합니다.

Python 헬퍼가 file.write 대신 os.write를 사용하는 이유

open(path, "w").write("")로 빈 문자열을 쓰는 것은 write(2)를 0바이트로 호출하지 않습니다 — CPython의 TextIOWrapper가 이를 생략합니다. configfs 언바인딩(커널이 개행 제거 후 길이가 0인 쓰기로 해석)의 경우 os.write(fd, b"\n")을 직접 사용하여 시스템 콜이 항상 1바이트 이상으로 발행되도록 합니다.


테스트

Ducky 파서는 MockHIDEngine(실제 HID 라이터의 인메모리 대체 구현)을 대상으로 실행되는 34개 테스트로 구성된 pytest 스위트를 갖추고 있어, Pi도 USB 하드웨어도 필요 없습니다.

root@kitploit:~
cd /home/pi/pi-badusb
python3 -m pytest tests/

테스트 커버리지:

  • safe_eval_math가 산술 연산은 허용하고 이름 / 호출 / 속성 접근 / 문자열 상수는 거부.
  • evaluate_condition의 숫자 및 대소문자 구분 문자열 비교.
  • =, +=, -=, *=, /= 연산을 포함한 VAR.
  • 올바른 분기를 타는 IF / ELSE / END_IF.
  • < 및 <=에 대한 WHILE / END_WHILE 반복 횟수.

문제 해결

"꽂아도 페이로드가 실행되지 않음"

  1. cat /sys/class/udc/*/state — 호스트가 열거할 때 configured에 도달해야 합니다. not attached에 머물러 있다면 호스트가 통신하지 않는 것입니다: 다른 케이블(일부는 충전 전용입니다)이나 다른 호스트 포트를 사용해 보세요.
  2. lsmod | grep dwc2 — 로드되어 있어야 합니다. dwc_otg만 있다면 dtoverlay=dwc2,dr_mode=otg가 적용되지 않는 것입니다. install.sh를 다시 실행하고 재부팅하세요.
  3. 꽂는 동안 journalctl -u badusb -f — 호스트 열거 후 약 2초 이내에 Host attached. Running payload.가 표시되어야 합니다.

"분리했는데도 LED가 깜빡이며 페이로드가 무한 반복됨"

이것은 실제로 있었던 버그이며 현재는 수정되었습니다. 이런 현상이 발생하면 os.write 이전 빌드로 되돌아간 것입니다. monitor_and_run.py가 현재 main 브랜치와 일치하는지 확인하세요 (os.write(fd, payload) 검색).

"서비스 재시작 불가 — Operation not permitted"

수정 전 gadget_setup.sh는 configfs에 rm -rf를 사용했습니다. 현재 버전은 teardown_gadget()을 사용합니다 — 이 오류가 보이면 구버전입니다. main에서 다시 배포하세요.

"/dev/hidg0에 대한 권한 거부"

udev 규칙을 적용하려면 핫플러그가 필요하거나, sudo udevadm trigger 후 pi 사용자가 plugdev 그룹을 적용받도록 다시 로그인해야 합니다.

"설치 중 g_ether 경고"

cmdline.txt에서 g_ether를 제거하세요. g_ether는 libcomposite가 바인딩되기 전에 UDC를 차지합니다.

"호스트에 USB 드라이브가 표시되는데 크기가 예상과 다름"

대용량 저장소 크기는 BACKING_SIZE_MB로 설정되며 백업 이미지가 생성되는 첫 실행에만 적용됩니다. 크기를 변경하려면:

root@kitploit:~
sudo systemctl stop badusb
sudo rm /var/badusb/storage.img
sudo BACKING_SIZE_MB=256 /home/pi/pi-badusb/gadget_setup.sh
sudo systemctl start badusb

"Pi를 꽂아 둔 채로 두고 싶은데 호스트에 스팸을 보내지 않게 하고 싶음"

바로 BADUSB_MIN_INTER_FIRE_S와 BADUSB_MAX_FIRES_PER_MINUTE가 그 용도입니다. systemctl edit badusb로 더 높게 설정하세요. 기본값으로는 꽂아 둔 Pi가 첫 1분 동안 약 15초 간격으로 다시 실행되고, 그다음 60초 동안 일시 중지한 후 재개합니다.


저장소 구조

root@kitploit:~
.
├── README.md                   This file
├── LICENSE                     MIT
├── install.sh                  Idempotent installer (firmware config, systemd, udev, plugdev)
├── gadget_setup.sh             Composes the USB gadget via configfs/libcomposite
├── teardown_gadget.sh          Canonical configfs teardown (wired as ExecStop)
├── reload_gadget.sh            Manual UDC unbind/rebind helper
├── autorun.sh                  Legacy manual-launch wrapper (systemd is preferred)
├── monitor_and_run.py          Listener: waits for host attach, runs payload, forces re-arm
├── run_payload.py              Ducky-Script-style interpreter
├── payload.txt                 Your payload — edit freely; re-read on each plug-in
├── payload_commands.md         Full command reference
├── etc/
│   ├── badusb.service          systemd unit
│   └── 99-badusb-hidg.rules    udev rule for /dev/hidg0 ownership
├── keymaps/
│   ├── __init__.py             Dynamic layout loader
│   ├── us.py                   US ANSI (default)
│   └── uk.py                   UK ISO
└── tests/
    ├── __init__.py
    ├── conftest.py             pytest path setup
    └── test_parser.py          34 parser tests against a MockHIDEngine

크레딧

  • 원본 프로젝트 스크립트 및 영감: Psycostea.
  • USB 가젯 연구: USB Rubber Ducky, Hak5, libcomposite 문서, dwc2 커널 드라이버.
  • 2026-05 정확성 개선 작업: 호스트 연결 감지 재작성, 대용량 저장소를 안전하게 만들기, 여러 파서 버그 수정, 키맵 추상화 추가, systemd로 시작 절차 마이그레이션, 유닛 테스트 하네스 추가, 그리고 물리적 연결 해제를 감지할 수 없는 Pi Zero 2 W 하드웨어 한계 우회.

라이선스

MIT — LICENSE 참조.

도구 다운로드
요구 사항이유
Raspberry Pi OS (Debian Bookworm 또는 Trixie, 64비트 Lite 권장)설치 스크립트는 Bookworm 이상에서는 /boot/firmware/...에 쓰고, 구형 이미지에서는 /boot/...로 대체됩니다.
Python 3Pi OS에 기본 포함되어 있습니다.
mkfs.vfat첫 실행 시 대용량 저장소 백업 이미지를 포맷하는 데 사용. 대용량 저장소를 비활성화하면 건너뜁니다.
설치를 위한 루트 권한systemd, udev, /boot/firmware/config.txt를 수정합니다.
/dev/hidg0
plugdev
  • pi 사용자를 plugdev 그룹에 추가합니다.
  • 대용량 저장소 백업 이미지용 /var/badusb/ 디렉터리를 생성합니다.
  • 변수기본값의미
    ENABLE_MASS_STORAGE10이면 HID 전용 가젯.
    BACKING_FILE/var/badusb/storage.img호스트에 노출되는 플랫 이미지.
    BACKING_SIZE_MB64첫 실행 시 생성됨.
    BACKING_LABELBADUSBFAT 볼륨 레이블.
    MASS_STORAGE_RO1기본적으로 읽기 전용.
    환경 변수기본값의미
    BADUSB_POST_PAYLOAD_FLUSH_S0.5페이로드 종료 후 언바인딩 전에 대기하여 HID 쓰기가 완전히 전송되도록 함.
    BADUSB_REARM_COOLDOWN_S5언바인딩과 재바인딩 사이에 가젯이 호스트에 보이지 않는 시간.
    BADUSB_MIN_INTER_FIRE_S10두 페이로드 실행 사이의 최소 시간(초). 이보다 짧으면 실행이 억제되고 가젯이 다시 언바인딩됨.
    BADUSB_MAX_FIRES_PER_MINUTE6하드 상한. 초과 시 BADUSB_RATELIMIT_PAUSE_S 동안 일시 중지.
    BADUSB_RATELIMIT_PAUSE_S60속도 제한 발동 후 일시 중지 시간.
  • RANDOM_* 길이 정확성; RANDOM_<unknown>은 no-op + 경고.
  • 이후 키 입력에 걸쳐 유지되는 INJECT_MOD 수정자 바이트.
  • STRINGLN abc의 각 문자를 대문자로 만드는 HOLD SHIFT.
  • @ 및 "에 대해 서로 다른 리포트를 생성하는 LAYOUT US vs LAYOUT UK; 알 수 없는 레이아웃은 이전 레이아웃으로 폴백.
  • 줄을 결합하는 STRING_BLOCK; 최소 들여쓰기를 존중하는 STRINGLN_BLOCK.