ctf-agent 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +131 -0
- package/Dockerfile +192 -0
- package/README.md +522 -0
- package/agents/ctf-analyzer.md +58 -0
- package/agents/ctf-controller.md +151 -0
- package/agents/ctf-speedrun.md +74 -0
- package/bin/ctf-agent.js +119 -0
- package/docker-compose.yml +13 -0
- package/mcp_config.json +19 -0
- package/package.json +44 -0
- package/references/ctf-triage-ladder.md +68 -0
- package/references/exploit-databases.md +575 -0
- package/references/llm-safety-and-policy-compliance.md +173 -0
- package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
- package/references/security-events-and-intelligence.md +566 -0
- package/references/version-matrix.md +59 -0
- package/rules/ctf-execution-rules.md +44 -0
- package/rules/ctf-safety-framing-rules.md +140 -0
- package/rules/flag-validation-rules.md +25 -0
- package/scripts/__init__.py +1 -0
- package/scripts/ctf-tools.lock +205 -0
- package/scripts/ctf_init.py +1026 -0
- package/scripts/ctf_update.py +583 -0
- package/scripts/ctfd_client.py +81 -0
- package/scripts/cve_lookup.py +453 -0
- package/scripts/extract_flags.py +155 -0
- package/scripts/install_as_agent.py +278 -0
- package/scripts/install_ctf_tools.sh +603 -0
- package/scripts/parallel_triage.py +395 -0
- package/scripts/prompt_policy_sanitizer.py +283 -0
- package/scripts/scope_guard.py +444 -0
- package/scripts/skill_validator.py +504 -0
- package/scripts/workspace_cleaner.py +141 -0
- package/skills/ctf-ai-ml/SKILL.md +120 -0
- package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
- package/skills/ctf-ai-ml/llm-attacks.md +487 -0
- package/skills/ctf-ai-ml/model-attacks.md +422 -0
- package/skills/ctf-crypto/SKILL.md +320 -0
- package/skills/ctf-crypto/advanced-math.md +798 -0
- package/skills/ctf-crypto/classic-ciphers.md +651 -0
- package/skills/ctf-crypto/ecc-attacks.md +347 -0
- package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
- package/skills/ctf-crypto/exotic-crypto.md +528 -0
- package/skills/ctf-crypto/historical.md +113 -0
- package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
- package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
- package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
- package/skills/ctf-crypto/modern-ciphers.md +649 -0
- package/skills/ctf-crypto/prng-attacks.md +257 -0
- package/skills/ctf-crypto/prng.md +664 -0
- package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
- package/skills/ctf-crypto/rsa-attacks.md +487 -0
- package/skills/ctf-crypto/stream-ciphers.md +390 -0
- package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
- package/skills/ctf-forensics/3d-printing.md +121 -0
- package/skills/ctf-forensics/SKILL.md +379 -0
- package/skills/ctf-forensics/disk-advanced.md +497 -0
- package/skills/ctf-forensics/disk-and-memory.md +491 -0
- package/skills/ctf-forensics/disk-recovery.md +699 -0
- package/skills/ctf-forensics/linux-forensics.md +511 -0
- package/skills/ctf-forensics/network-advanced.md +583 -0
- package/skills/ctf-forensics/network.md +645 -0
- package/skills/ctf-forensics/peripheral-capture.md +287 -0
- package/skills/ctf-forensics/signals-and-hardware.md +713 -0
- package/skills/ctf-forensics/steganography.md +694 -0
- package/skills/ctf-forensics/stego-advanced-2.md +475 -0
- package/skills/ctf-forensics/stego-advanced.md +481 -0
- package/skills/ctf-forensics/stego-image.md +691 -0
- package/skills/ctf-forensics/windows.md +625 -0
- package/skills/ctf-malware/SKILL.md +181 -0
- package/skills/ctf-malware/c2-and-protocols.md +274 -0
- package/skills/ctf-malware/pe-and-dotnet.md +108 -0
- package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
- package/skills/ctf-misc/SKILL.md +498 -0
- package/skills/ctf-misc/bashjails.md +323 -0
- package/skills/ctf-misc/ctfd-navigation.md +465 -0
- package/skills/ctf-misc/dns.md +255 -0
- package/skills/ctf-misc/encodings-advanced.md +504 -0
- package/skills/ctf-misc/encodings.md +431 -0
- package/skills/ctf-misc/games-and-vms-2.md +254 -0
- package/skills/ctf-misc/games-and-vms-3.md +690 -0
- package/skills/ctf-misc/games-and-vms-4.md +229 -0
- package/skills/ctf-misc/games-and-vms.md +529 -0
- package/skills/ctf-misc/linux-privesc.md +333 -0
- package/skills/ctf-misc/pyjails.md +671 -0
- package/skills/ctf-misc/rf-sdr.md +91 -0
- package/skills/ctf-osint/SKILL.md +198 -0
- package/skills/ctf-osint/geolocation-and-media.md +464 -0
- package/skills/ctf-osint/social-media.md +312 -0
- package/skills/ctf-osint/web-and-dns.md +341 -0
- package/skills/ctf-pwn/SKILL.md +214 -0
- package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
- package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
- package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
- package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
- package/skills/ctf-pwn/advanced-exploits.md +773 -0
- package/skills/ctf-pwn/advanced.md +326 -0
- package/skills/ctf-pwn/field-notes.md +245 -0
- package/skills/ctf-pwn/format-string.md +694 -0
- package/skills/ctf-pwn/heap-fsop.md +285 -0
- package/skills/ctf-pwn/heap-techniques-2.md +333 -0
- package/skills/ctf-pwn/heap-techniques.md +513 -0
- package/skills/ctf-pwn/kernel-bypass.md +421 -0
- package/skills/ctf-pwn/kernel-techniques.md +366 -0
- package/skills/ctf-pwn/kernel.md +636 -0
- package/skills/ctf-pwn/overflow-basics.md +611 -0
- package/skills/ctf-pwn/rop-advanced.md +725 -0
- package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
- package/skills/ctf-pwn/sandbox-escape.md +313 -0
- package/skills/ctf-reverse/SKILL.md +163 -0
- package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
- package/skills/ctf-reverse/anti-analysis.md +693 -0
- package/skills/ctf-reverse/field-notes.md +376 -0
- package/skills/ctf-reverse/languages-compiled.md +666 -0
- package/skills/ctf-reverse/languages-platforms.md +592 -0
- package/skills/ctf-reverse/languages.md +553 -0
- package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
- package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
- package/skills/ctf-reverse/patterns-ctf.md +670 -0
- package/skills/ctf-reverse/patterns-runtime.md +274 -0
- package/skills/ctf-reverse/patterns.md +572 -0
- package/skills/ctf-reverse/platforms-hardware.md +387 -0
- package/skills/ctf-reverse/platforms.md +664 -0
- package/skills/ctf-reverse/tools-advanced-2.md +421 -0
- package/skills/ctf-reverse/tools-advanced.md +407 -0
- package/skills/ctf-reverse/tools-dynamic.md +679 -0
- package/skills/ctf-reverse/tools-emulation.md +319 -0
- package/skills/ctf-reverse/tools.md +573 -0
- package/skills/ctf-web/SKILL.md +153 -0
- package/skills/ctf-web/auth-and-access-2.md +82 -0
- package/skills/ctf-web/auth-and-access.md +783 -0
- package/skills/ctf-web/auth-infra.md +321 -0
- package/skills/ctf-web/auth-jwt.md +186 -0
- package/skills/ctf-web/client-side-advanced.md +739 -0
- package/skills/ctf-web/client-side.md +529 -0
- package/skills/ctf-web/cves.md +373 -0
- package/skills/ctf-web/field-notes.md +482 -0
- package/skills/ctf-web/node-and-prototype.md +200 -0
- package/skills/ctf-web/server-side-2.md +337 -0
- package/skills/ctf-web/server-side-advanced-2.md +559 -0
- package/skills/ctf-web/server-side-advanced-3.md +125 -0
- package/skills/ctf-web/server-side-advanced-4.md +480 -0
- package/skills/ctf-web/server-side-advanced.md +378 -0
- package/skills/ctf-web/server-side-deser.md +443 -0
- package/skills/ctf-web/server-side-exec-2.md +799 -0
- package/skills/ctf-web/server-side-exec.md +457 -0
- package/skills/ctf-web/server-side.md +629 -0
- package/skills/ctf-web/sql-injection.md +790 -0
- package/skills/ctf-web/web3.md +374 -0
- package/skills/ctf-writeup/SKILL.md +90 -0
- package/skills/solve-challenge/SKILL.md +269 -0
- package/skills.json +16 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# CTF Forensics - Peripheral Capture Analysis
|
|
2
|
+
|
|
3
|
+
USB, HID, and Bluetooth peripheral traffic reconstruction from packet captures. For general network PCAP forensics (DNS/TCP/ICMP/SMB/RADIUS/RC4), see [network-advanced.md](network-advanced.md). For basic network forensics, see [network.md](network.md).
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [USB HID Mouse/Pen Drawing Recovery (EHAX 2026)](#usb-hid-mousepen-drawing-recovery-ehax-2026)
|
|
7
|
+
- [USB HID Keyboard Capture Decoding (EKOPARTY CTF 2016)](#usb-hid-keyboard-capture-decoding-ekoparty-ctf-2016)
|
|
8
|
+
- [USB Keyboard LED Morse Code Exfiltration (BITSCTF 2017)](#usb-keyboard-led-morse-code-exfiltration-bitsctf-2017)
|
|
9
|
+
- [USB HID Keyboard Arrow Key Navigation Tracking (HackIT 2017)](#usb-hid-keyboard-arrow-key-navigation-tracking-hackit-2017)
|
|
10
|
+
- [Bluetooth RFCOMM Packet Reassembly (HITCON 2018)](#bluetooth-rfcomm-packet-reassembly-hitcon-2018)
|
|
11
|
+
- [GBA USB URB_INTERRUPT Framebuffer Extraction (hxp 2018)](#gba-usb-urb_interrupt-framebuffer-extraction-hxp-2018)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## USB HID Mouse/Pen Drawing Recovery (EHAX 2026)
|
|
16
|
+
|
|
17
|
+
**Pattern (Painter):** PCAP contains USB HID interrupt transfers from a mouse/pen device. Drawing data encoded as relative movements with multiple draw modes.
|
|
18
|
+
|
|
19
|
+
**Packet format (7-byte HID reports):**
|
|
20
|
+
| Byte | Field | Notes |
|
|
21
|
+
|------|-------|-------|
|
|
22
|
+
| 0 | Button state | 0x01 = pressed (may be constant) |
|
|
23
|
+
| 1 | Mode/pad | 0=hover, 1=draw mode 1, 2=draw mode 2 |
|
|
24
|
+
| 2-3 | dx (int16 LE) | Relative X movement |
|
|
25
|
+
| 4-5 | dy (int16 LE) | Relative Y movement |
|
|
26
|
+
| 6 | Wheel | Usually 0 |
|
|
27
|
+
|
|
28
|
+
**Extraction and rendering:**
|
|
29
|
+
```python
|
|
30
|
+
import struct
|
|
31
|
+
from PIL import Image, ImageDraw
|
|
32
|
+
|
|
33
|
+
# Extract HID data
|
|
34
|
+
# tshark -r capture.pcap -Y "usb.transfer_type==1" -T fields -e usb.capdata
|
|
35
|
+
|
|
36
|
+
packets = []
|
|
37
|
+
with open('hid_data.txt') as f:
|
|
38
|
+
for line in f:
|
|
39
|
+
raw = bytes.fromhex(line.strip().replace(':', ''))
|
|
40
|
+
if len(raw) >= 7:
|
|
41
|
+
btn = raw[0]
|
|
42
|
+
mode = raw[1]
|
|
43
|
+
dx = struct.unpack('<h', raw[2:4])[0]
|
|
44
|
+
dy = struct.unpack('<h', raw[4:6])[0]
|
|
45
|
+
packets.append((btn, mode, dx, dy))
|
|
46
|
+
|
|
47
|
+
# Accumulate positions per mode
|
|
48
|
+
SCALE = 5
|
|
49
|
+
positions = {0: [], 1: [], 2: []}
|
|
50
|
+
x, y = 0, 0
|
|
51
|
+
for btn, mode, dx, dy in packets:
|
|
52
|
+
x += dx
|
|
53
|
+
y += dy
|
|
54
|
+
positions[mode].append((x, y))
|
|
55
|
+
|
|
56
|
+
# Render each mode separately (different colors = different text layers)
|
|
57
|
+
for mode in [1, 2]:
|
|
58
|
+
pts = positions[mode]
|
|
59
|
+
if not pts:
|
|
60
|
+
continue
|
|
61
|
+
min_x = min(p[0] for p in pts) - 100
|
|
62
|
+
min_y = min(p[1] for p in pts) - 100
|
|
63
|
+
max_x = max(p[0] for p in pts) + 100
|
|
64
|
+
max_y = max(p[1] for p in pts) + 100
|
|
65
|
+
w = (max_x - min_x) * SCALE
|
|
66
|
+
h = (max_y - min_y) * SCALE
|
|
67
|
+
img = Image.new('RGB', (w, h), 'white')
|
|
68
|
+
draw = ImageDraw.Draw(img)
|
|
69
|
+
for i in range(1, len(pts)):
|
|
70
|
+
x0 = (pts[i-1][0] - min_x) * SCALE
|
|
71
|
+
y0 = (pts[i-1][1] - min_y) * SCALE
|
|
72
|
+
x1 = (pts[i][0] - min_x) * SCALE
|
|
73
|
+
y1 = (pts[i][1] - min_y) * SCALE
|
|
74
|
+
# Skip long jumps (pen lifts)
|
|
75
|
+
if abs(pts[i][0]-pts[i-1][0]) < 50 and abs(pts[i][1]-pts[i-1][1]) < 50:
|
|
76
|
+
draw.line([(x0,y0),(x1,y1)], fill='black', width=3)
|
|
77
|
+
img.save(f'mode_{mode}.png')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Key techniques:**
|
|
81
|
+
- **Separate modes:** Different button/mode values draw different text layers — render each independently
|
|
82
|
+
- **Skip pen lifts:** Large dx/dy jumps indicate pen was lifted, not drawn — filter by distance threshold
|
|
83
|
+
- **High resolution:** Scale 5-8x with margins for readable handwriting
|
|
84
|
+
- **Time gradient:** Color points by temporal order (rainbow gradient) to trace stroke direction
|
|
85
|
+
- **Character segmentation:** Group consecutive same-mode points by large X gaps to isolate characters
|
|
86
|
+
|
|
87
|
+
**Alternative: AWK extraction + SVG rendering (faster pipeline):**
|
|
88
|
+
```bash
|
|
89
|
+
# Extract capdata and convert to signed deltas in one pass
|
|
90
|
+
tshark -r pref.pcap -Y "usb.transfer_type==0x01 && usb.endpoint_address==0x81 && usb.capdata" \
|
|
91
|
+
-T fields -e usb.capdata > capdata.txt
|
|
92
|
+
|
|
93
|
+
awk '
|
|
94
|
+
function hexval(c){ return index("0123456789abcdef",tolower(c))-1 }
|
|
95
|
+
function hex2dec(h, n,i){ n=0; for(i=1;i<=length(h);i++) n=n*16+hexval(substr(h,i,1)); return n }
|
|
96
|
+
function s16(u){ return (u>=32768)?u-65536:u }
|
|
97
|
+
{ d=$1; if(length(d)!=14) next
|
|
98
|
+
btn=hex2dec(substr(d,3,2))
|
|
99
|
+
x=s16(hex2dec(substr(d,7,2) substr(d,5,2)))
|
|
100
|
+
y=s16(hex2dec(substr(d,11,2) substr(d,9,2)))
|
|
101
|
+
print btn, x, y }' capdata.txt > deltas.txt
|
|
102
|
+
```
|
|
103
|
+
Then render with SVG (Python) — filter on pen-down state (button=2), accumulate deltas, flip Y axis, draw strokes between consecutive pen-down points.
|
|
104
|
+
|
|
105
|
+
**Difference from keyboard HID:** Mouse HID uses relative movements (accumulated), keyboard uses keycodes (direct). Mouse drawing requires rendering; keyboard requires keymap lookup.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## USB HID Keyboard Capture Decoding (EKOPARTY CTF 2016)
|
|
110
|
+
|
|
111
|
+
USB keyboard captures contain HID scan codes that map to keystrokes. Decode the capture to reconstruct typed text.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
# USB HID keyboard report format:
|
|
115
|
+
# Byte 0: Modifier keys (Shift, Ctrl, Alt)
|
|
116
|
+
# Byte 1: Reserved (0x00)
|
|
117
|
+
# Bytes 2-7: Up to 6 simultaneous key codes
|
|
118
|
+
|
|
119
|
+
# HID scan code to character mapping (partial)
|
|
120
|
+
HID_MAP = {
|
|
121
|
+
0x04: 'a', 0x05: 'b', 0x06: 'c', 0x07: 'd', 0x08: 'e',
|
|
122
|
+
0x09: 'f', 0x0a: 'g', 0x0b: 'h', 0x0c: 'i', 0x0d: 'j',
|
|
123
|
+
0x0e: 'k', 0x0f: 'l', 0x10: 'm', 0x11: 'n', 0x12: 'o',
|
|
124
|
+
0x13: 'p', 0x14: 'q', 0x15: 'r', 0x16: 's', 0x17: 't',
|
|
125
|
+
0x18: 'u', 0x19: 'v', 0x1a: 'w', 0x1b: 'x', 0x1c: 'y',
|
|
126
|
+
0x1d: 'z', 0x1e: '1', 0x1f: '2', 0x20: '3', 0x21: '4',
|
|
127
|
+
0x22: '5', 0x23: '6', 0x24: '7', 0x25: '8', 0x26: '9',
|
|
128
|
+
0x27: '0', 0x28: '\n', 0x2c: ' ', 0x2d: '-', 0x2e: '=',
|
|
129
|
+
0x2f: '[', 0x30: ']', 0x33: ';', 0x34: "'", 0x36: ',',
|
|
130
|
+
0x37: '.', 0x38: '/',
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
SHIFT_MAP = {
|
|
134
|
+
'a': 'A', 'b': 'B', '1': '!', '2': '@', '3': '#', '4': '$',
|
|
135
|
+
'5': '%', '6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
|
|
136
|
+
'-': '_', '=': '+', '[': '{', ']': '}', ';': ':', "'": '"',
|
|
137
|
+
',': '<', '.': '>', '/': '?',
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def decode_hid_keyboard(capture_data):
|
|
141
|
+
"""Decode USB HID keyboard capture to text"""
|
|
142
|
+
text = ""
|
|
143
|
+
for report in capture_data:
|
|
144
|
+
modifier = report[0]
|
|
145
|
+
keycode = report[2] # first key in report
|
|
146
|
+
|
|
147
|
+
if keycode == 0:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
char = HID_MAP.get(keycode, '')
|
|
151
|
+
if modifier & 0x22: # Left or Right Shift
|
|
152
|
+
char = SHIFT_MAP.get(char, char.upper())
|
|
153
|
+
|
|
154
|
+
text += char
|
|
155
|
+
return text
|
|
156
|
+
|
|
157
|
+
# Extract from Wireshark: tshark -r capture.pcapng -T fields -e usb.capdata
|
|
158
|
+
# Or from text dump: parse +XX/-XX format (+ = keydown, - = keyup)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Key insight:** USB HID keyboards send 8-byte reports where byte 0 is modifiers (Shift/Ctrl/Alt) and bytes 2-7 are active key scan codes. In Wireshark, filter with `usb.transfer_type == 1` and extract `usb.capdata`. Ignore reports where byte 2 is 0x00 (key release).
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## USB Keyboard LED Morse Code Exfiltration (BITSCTF 2017)
|
|
166
|
+
|
|
167
|
+
**Pattern (Ghost in the Machine):** A pcap of USB keyboard traffic contains host-to-device packets with alternating `0x01`/`0x03` values controlling the Caps Lock LED state. Timing differences between LED state changes encode Morse code: durations >300ms represent dashes, shorter durations represent dots. Decode the Morse sequence to recover the flag.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from scapy.all import rdpcap
|
|
171
|
+
import struct
|
|
172
|
+
|
|
173
|
+
packets = rdpcap('usb_capture.pcap')
|
|
174
|
+
signals = []
|
|
175
|
+
|
|
176
|
+
for p in packets:
|
|
177
|
+
raw = bytes(p)
|
|
178
|
+
# USB HID SET_REPORT to keyboard (host -> device)
|
|
179
|
+
if len(raw) >= 35 and raw[30] in (0x01, 0x03):
|
|
180
|
+
timestamp = p.time
|
|
181
|
+
led_state = raw[30] # 0x01 = LED off, 0x03 = LED on
|
|
182
|
+
signals.append((timestamp, led_state))
|
|
183
|
+
|
|
184
|
+
# Convert timing to Morse
|
|
185
|
+
morse = ''
|
|
186
|
+
for i in range(0, len(signals) - 1, 2):
|
|
187
|
+
duration = signals[i+1][0] - signals[i][0]
|
|
188
|
+
if duration > 0.3:
|
|
189
|
+
morse += '-'
|
|
190
|
+
else:
|
|
191
|
+
morse += '.'
|
|
192
|
+
# Gap between signals indicates letter/word boundary
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Key insight:** Data exfiltration via keyboard LED state changes captured in USB pcap. The LED control packets use HID SET_REPORT class requests. Timing analysis of on/off transitions reveals Morse code patterns. Tools: Wireshark USB dissector, filter on `usb.transfer_type == 0x02` (interrupt) and direction host→device.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## USB HID Keyboard Arrow Key Navigation Tracking (HackIT 2017)
|
|
200
|
+
|
|
201
|
+
USB HID keyboard traffic from an Apple Keyboard requires tracking arrow key navigation. Decode HID keycodes using the USB HID usage table. Modifier byte `0x02` = Shift (uppercase). Track cursor position via up/down arrow presses to determine which line contains the flag.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
tshark -r capture.pcap -T fields -e usb.capdata | \
|
|
205
|
+
python3 decode_hid.py # Must track arrow keys for line position
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Arrow key HID codes to track:
|
|
209
|
+
- `0x4F` = Right Arrow
|
|
210
|
+
- `0x50` = Left Arrow
|
|
211
|
+
- `0x51` = Down Arrow (next line)
|
|
212
|
+
- `0x52` = Up Arrow (previous line)
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# Skeleton: track line position during HID decode
|
|
216
|
+
line = 0
|
|
217
|
+
lines = {0: ""}
|
|
218
|
+
for report in hid_reports:
|
|
219
|
+
modifier = report[0]
|
|
220
|
+
keycode = report[2]
|
|
221
|
+
if keycode == 0x51: # Down arrow
|
|
222
|
+
line += 1; lines.setdefault(line, "")
|
|
223
|
+
elif keycode == 0x52: # Up arrow
|
|
224
|
+
line -= 1; lines.setdefault(line, "")
|
|
225
|
+
elif keycode in HID_MAP:
|
|
226
|
+
char = HID_MAP[keycode]
|
|
227
|
+
if modifier & 0x22:
|
|
228
|
+
char = char.upper()
|
|
229
|
+
lines[line] += char
|
|
230
|
+
# Flag is on a specific line determined by arrow navigation
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Key insight:** USB keyboard captures must account for cursor movement keys (arrows, backspace). Track cursor line position to reconstruct text typed on each line separately — the flag may be on a non-zero line that arrow keys navigated to.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Bluetooth RFCOMM Packet Reassembly (HITCON 2018)
|
|
238
|
+
|
|
239
|
+
**Pattern:** A Lego EV3-over-Bluetooth capture contains RFCOMM frames whose payloads are EV3 direct commands. Packets are 32–34 bytes long, have an 8-byte RFCOMM header, and carry an `order` byte plus a `group_number` byte that together reorder into a coherent binary. Reassemble by (1) filtering `btrfcomm` in Wireshark, (2) sorting packets first by `group_number` then by `order`, and (3) concatenating the data fields after the header.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
# Python with pyshark
|
|
243
|
+
import pyshark
|
|
244
|
+
cap = pyshark.FileCapture("capture.pcap", display_filter="btrfcomm")
|
|
245
|
+
frames = []
|
|
246
|
+
for pkt in cap:
|
|
247
|
+
raw = bytes.fromhex(pkt.btrfcomm.payload.replace(":", ""))
|
|
248
|
+
# RFCOMM header size varies: 4 (UIH) or 5 (with length extension)
|
|
249
|
+
hdr_len = 4 if raw[2] & 0x01 == 0 else 5
|
|
250
|
+
body = raw[hdr_len:]
|
|
251
|
+
order, group = body[0], body[1]
|
|
252
|
+
frames.append((group, order, body[2:]))
|
|
253
|
+
frames.sort()
|
|
254
|
+
binary = b"".join(chunk for _, _, chunk in frames)
|
|
255
|
+
open("payload.bin", "wb").write(binary)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Key insight:** RFCOMM is a TCP-like serial port emulation layered on L2CAP; it fragments application payloads when they exceed the MTU. CTF challenges love to split flags across many frames because most pcap walkthroughs stop at TCP/UDP and skip the Bluetooth link layer. Use Wireshark filters `btrfcomm.channel`, `btl2cap`, or `btsnoop_hci` to isolate the relevant flows, then sort by any available order/group bytes before concatenating. Similar logic applies to USB bulk transfers (`usb.transfer_type == 0x03`) and MIDI-over-BLE traffic.
|
|
259
|
+
|
|
260
|
+
**References:** HITCON CTF 2018 — EV3 Basic, writeup 11902
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## GBA USB URB_INTERRUPT Framebuffer Extraction (hxp 2018)
|
|
265
|
+
|
|
266
|
+
**Pattern:** pcap contains USB `URB_INTERRUPT` packets from a Game Boy Advance debug adapter. The GBA framebuffer is `240 × 160` at RGB565 (2 bytes/pixel = 76 800 bytes). Block type 6 carries memory dumps; split each packet's payload into the framebuffer grid and convert RGB565 to 8-bit RGB tuples.
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from PIL import Image
|
|
270
|
+
from scapy.all import rdpcap
|
|
271
|
+
pkts = [p for p in rdpcap('cap.pcap') if p.haslayer('Raw')]
|
|
272
|
+
img = Image.new('RGB', (240, 160))
|
|
273
|
+
for p in pkts:
|
|
274
|
+
if p.Raw.load[3] == 0x06: # type 6 = memory dump
|
|
275
|
+
data = p.Raw.load[4:]
|
|
276
|
+
for i in range(76800 // 2):
|
|
277
|
+
rgb = int.from_bytes(data[2*i:2*i+2], 'little')
|
|
278
|
+
r = (rgb & 0xF800) >> 8
|
|
279
|
+
g = (rgb & 0x07E0) >> 3
|
|
280
|
+
b = (rgb & 0x001F) << 3
|
|
281
|
+
img.putpixel((i % 240, i // 240), (r, g, b))
|
|
282
|
+
img.save('screen.png')
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Key insight:** Handheld console debug protocols usually wrap memory dumps in typed blocks. When you see GBA/NDS/PSP USB traffic, grep for block type 6 (framebuffer) or type 7 (audio) before parsing the rest.
|
|
286
|
+
|
|
287
|
+
**References:** hxp CTF 2018 — cheatquest of hxpschr 2, writeup 12591
|