๋ฌธ์ : DreamHack โ My Nervous PPT ๋ถ๋ฅ: Misc / Forensics ๋์ด๋: ๐ฅ Silver 3 FLAG:
KHK{LOOK_AT_MY_PPT}
๋ฌธ์ ์ค๋ช ์ด ์งง๋ค.
Umm.. ๋ PPT ๋ฐํ๋ฅผ ํ๊ธฐ ์ , ๊ธด์ฅ๋ ๋ง์์ ํ๊ณ ์ ํ๋ฆฌ์ ํฐ๋ฅผ ๋ง ๋๋ฅด๊ณค ํ์ง.. :( FLag ํ์์
KHK{}์ ๋๋ค. *์์ โ ๋ชจ๋ Flag๋ ๋๋ฌธ์์ ๋๋ค.
์ฃผ์ด์ง ํ์ผ์ My_PPT.pcapng ํ๋. 21KB์ง๋ฆฌ ํจํท ์บก์ณ๋ค. ์ค๋ช
์ ๋์จ "ํ๋ฆฌ์ ํฐ"๊ฐ ๊ณง ํํธ๋ค โ ๋ฐํ์ฉ ๋ฌด์ ๋ฆฌ๋ชจ์ปจ์ ํธ์คํธ ์
์ฅ์์ ๊ทธ๋ฅ USB ํค๋ณด๋๋ก ๋ณด์ด๊ณ , ๋ฒํผ์ ๋๋ฅด๋ฉด ํค ์
๋ ฅ์ด ์ฌ๋ผ๊ฐ๋ค. ๊ทธ๋ฌ๋ ์ด ์บก์ณ๋ ๋คํธ์ํฌ ํธ๋ํฝ์ด ์๋๋ผ USB ํธ๋ํฝ์ด๊ณ , ์ฐ๋ฆฌ๊ฐ ๋ณต์ํด์ผ ํ ๊ฑด ๋๊ตฐ๊ฐ 156์ด ๋์ ๋๋ฅธ ๋ฒํผ์ ์์๋ค.
"๋ง ๋๋ฅด๊ณค ํ์ง"๋ผ๋ ๋ง์ด ๋ฌด์๋ฏธํ ์ฐํ๋ฅผ ๋ปํ๋ ๊ฒ์ฒ๋ผ ์ฝํ์ง๋ง, ์ค์ ๋ก๋ ๊ทธ ๋ฐ๋๋ค. ๋๋ฅธ ํจํด ์์ฒด๊ฐ ๋ฉ์์ง๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | My Nervous PPT (id 1757) |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Misc / Forensics |
| ์ ๊ณต ํ์ผ | My_PPT.pcapng (21,500 ๋ฐ์ดํธ, ํจํท 310๊ฐ) |
| ์บก์ณ ํ๊ฒฝ | Windows 11 23H2 + USBPcap2 + Dumpcap 4.4.3 |
| ์๋ฒ | ์์ (์์ ์คํ๋ผ์ธ ๋ฌธ์ ) |
| ํต์ฌ ๊ธฐ๋ฒ | USB HID ๋ถํธ ํค๋ณด๋ ๋ฆฌํฌํธ ๋ณต์ โ ๋๋ฆผ ๊ฐ๊ฒฉ์ผ๋ก ๊ธ์ ๋ถํ โ ๋ชจ์ค๋ถํธ ๋ณตํธ |
ํ์ด ํ๋ฆ์ ๋ค ๋จ๊ณ๋ค. โ pcapng์์ USB ์ธํฐ๋ฝํธ ์ ์ก์ 8๋ฐ์ดํธ HID ๋ฆฌํฌํธ๋ง ๋ฝ๊ณ โก ๊ทธ์ค keycode๊ฐ 0์ด ์๋ "๋๋ฆผ"๋ง ๋จ๊ธฐ๊ณ โข ๋๋ฆผ ์ฌ์ด ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ๊ธ์๋ฅผ ์๋ฅธ ๋ค โฃ ๋ ํค๋ฅผ ์ ๊ณผ ์ ์ ๋์์์ผ ๋ชจ์ค๋ถํธ๋ก ์ฝ๋๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ USBPcap ์ด ๋จ๊ธด pcapng
pcap์ด๋ผ๊ณ ํ๋ฉด ๋๊ฐ ์ด๋๋ท ํ๋ ์์ ๋ ์ฌ๋ฆฌ๋๋ฐ, pcapng์ ๋งํฌ ํ์
์๋ USB๋ ์๋ค. ์๋์ฐ์์๋ USBPcap ๋๋ผ์ด๋ฒ๊ฐ USB ์คํ ์ฌ์ด์ ๋ผ์ด ์์ IRP(I/O Request Packet)๋ฅผ ๊ทธ๋๋ก ๋ ์ค๋ค. Wireshark๊ฐ ๊ทธ๊ฑธ usb-usbpcap ์ธ์บก์๋ ์ด์
(152๋ฒ)์ผ๋ก ์ ์ฅํ๋ค.
๋จผ์ ํ์ผ์ด ๋ฌด์์ธ์ง๋ถํฐ ํ์ธํ๋ค.
file extracted/My_PPT.pcapng && capinfos extracted/My_PPT.pcapng
์ฝ์ ๊ฐ์ด ์ฌ๋ฟ ๋์จ๋ค. ์ธ์บก์๋ ์ด์
์ด USB packets with USBPcap header๋ผ USB ์บก์ณ๊ฐ ๋ง๊ณ , ์ธํฐํ์ด์ค ์ด๋ฆ์ด \\.\USBPcap2๋ผ ํน์ USB ๋ฃจํธ ํ๋ธ ํ๋๋ง ๋ด๋ค. 156.498์ด ๋์ 310๊ฐ๋๊น ์ด๋น ๋ ๊ฐ๊ผด โ ๋์ญํญ์ ์ฐ๋ ์ฅ์น๋ ์๊ณ ๋๊ตฐ๊ฐ ๋ฒํผ์ ๋์๋์ ๋๋ฅธ ํ์ ์ ๊ฐ๊น๋ค.
Capture oper-sys์ 64-bit Windows 11 (23H2), build 22631๊น์ง ๋ฐํ ์๋ค. ์ถ์ ์๊ฐ ์๊ธฐ ๋
ธํธ๋ถ์์ ๊ทธ๋๋ก ๋ ์ ์ฌ๋ฆฐ ํ์ผ์ด๋ค.
๐ฌ ์ ์ฐฐ โ ์ด ๋ฒ์ค์ ๋๊ฐ ๋ถ์ด ์๋
USBPcap์ ๊ทธ ํ๋ธ์ ๋ถ์ ์ฅ์น๋ฅผ ์ ๋ถ ๋ด๊ธฐ ๋๋ฌธ์, ๋จผ์ "์ด๋ ์ฅ์น๊ฐ ๋งํ๊ณ ์๋"๋ฅผ ๊ฐ๋ผ์ผ ํ๋ค. tshark๋ก ์๋ถ๋ถ์ ์ฝ์ด ๋ณด๋ฉด ์ด๊ฑฐ(enumeration) ๊ณผ์ ์ด ๊ทธ๋๋ก ๋ณด์ธ๋ค.
cp extracted/My_PPT.pcapng /tmp/mnp.pcapng && tshark -r /tmp/mnp.pcapng -c 18
GET DESCRIPTOR DEVICE โ GET DESCRIPTOR CONFIGURATION โ SET CONFIGURATION ์ด ์ฅ์น๋ณ๋ก ํ ์ธํธ์ฉ ๋ฐ๋ณต๋๋ค. ์บก์ณ๋ฅผ ์์ํ ์์ ์ ์ด๋ฏธ ๊ฝํ ์๋ ์ฅ์น๋ค์ USBPcap์ด ๋ค๋ฆ๊ฒ ํ์ ๊ฒ์ด๋ค.
/tmp ๋ก ๋ณต์ฌํ๋ ์ด์ ๋ ๋ค์ ์ฝ์ง ํ ๊ธ์ ์ ์๋ค. ํ์ผ์ ๊ทธ ์๋ฆฌ์์ ์ฝ์ ์๊ฐ ์์๋ค.
์ฌ๊ธฐ์๋ถํฐ๋ tshark ์ถ๋ ฅ์ ๋์ผ๋ก ์ข๋ ๋์ , ์บก์ณ๋ฅผ ์ง์ ํ์ฑํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ํ๋ ๋ง๋ค์ด ๋๊ณ ๊ณ์ ์ฐ๊ธฐ๋ก ํ๋ค. ๋์ค์ ๋ณตํธ๊น์ง ์ด ํ์๋ฅผ ๊ทธ๋๋ก ์ฌํ์ฉํ๋ค.
โธ๐ analyze.py ์ ๋ฌธ โ ์ฅ์น ๋ชฉ๋ก ยท ๋์คํฌ๋ฆฝํฐ ยท ๊ฐ๊ฒฉ ํ์คํ ๊ทธ๋จ
#!/usr/bin/env python3
"""My Nervous PPT โ ์บก์ณ ์ ์ ์ฐฐ์ฉ ๋ถ์ ์คํฌ๋ฆฝํธ.
solve.py ์ ํ์๋ฅผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํด์ ์บก์ณ์ ์ธ ์ธ ๊ฐ์ง๋ฅผ ๋ฝ๋๋ค.
[1] ์บก์ณ์ ์กํ USB ์ฅ์น ๋ชฉ๋ก (๋๋ฐ์ด์ค ๋์คํฌ๋ฆฝํฐ์ VID/PID)
[2] 32๋ฒ ์ฅ์น์ ์ค์ ๋์คํฌ๋ฆฝํฐ ํ์ฑ โ ์ 8๋ฐ์ดํธ ๋ถํธ ํค๋ณด๋ ๋ฆฌํฌํธ์ธ๊ฐ
[3] HID ๋ฆฌํฌํธ ํต๊ณ + ๋๋ฆผ ๊ฐ๊ฒฉ ํ์คํ ๊ทธ๋จ โ ๊ธ์ ๊ฒฝ๊ณ๊ฐ ์ด๋์ ๊ฐ๋ฆฌ๋
์ฌ์ฉ๋ฒ: python3 analyze.py extracted/My_PPT.pcapng [devices|config|reports]
(์น์
์ด๋ฆ์ ๋นผ๋ฉด ์
๋ค ์ถ๋ ฅํ๋ค)
"""
import struct
import sys
from collections import Counter
from solve import parse_pcapng, hid_reports
CLASS = {0x00: "(์ธํฐํ์ด์ค๋ณ)", 0x03: "HID", 0x09: "Hub", 0xE0: "Wireless", 0xFF: "Vendor"}
PROTO = {0: "None", 1: "Keyboard", 2: "Mouse"}
def usbpcap_split(pkt):
"""USBPcap ์์ฌํค๋์์ (device, endpoint, transfer, ํ์ด๋ก๋) ๋ฅผ ๊บผ๋ธ๋ค."""
hlen = struct.unpack_from("<H", pkt, 0)[0]
device = struct.unpack_from("<H", pkt, 19)[0]
endpoint, transfer = pkt[21], pkt[22]
return device, endpoint, transfer, pkt[hlen:]
def show_devices(packets):
print("[1] ์บก์ณ์ ์กํ USB ์ฅ์น (๋๋ฐ์ด์ค ๋์คํฌ๋ฆฝํฐ ์๋ต)")
seen = {}
for _, pkt in packets:
dev, _, transfer, data = usbpcap_split(pkt)
# bLength=18, bDescriptorType=0x01 ์ธ ๊ฒ์ด ๋๋ฐ์ด์ค ๋์คํฌ๋ฆฝํฐ
if transfer == 0x02 and len(data) == 18 and data[0] == 18 and data[1] == 0x01:
vid, pid = struct.unpack_from("<HH", data, 8)
seen.setdefault(dev, (vid, pid, data[4]))
for dev, (vid, pid, cls) in sorted(seen.items()):
print(f" device {dev:>3} VID:PID = {vid:04x}:{pid:04x} "
f"bDeviceClass=0x{cls:02x} {CLASS.get(cls, '')}")
print()
def show_config(packets, want_dev):
print(f"[2] device {want_dev} ์ ์ค์ ๋์คํฌ๋ฆฝํฐ")
for _, pkt in packets:
dev, _, transfer, data = usbpcap_split(pkt)
if dev != want_dev or transfer != 0x02 or len(data) < 9 or data[1] != 0x02:
continue
off = 0
while off + 2 <= len(data):
blen, btype = data[off], data[off + 1]
if blen == 0:
break
b = data[off:off + blen]
if btype == 0x02:
print(f" CONFIGURATION ์ด๊ธธ์ด={struct.unpack_from('<H', b, 2)[0]} "
f"์ธํฐํ์ด์ค={b[4]}๊ฐ ์ต๋์ ๋ฅ={b[8] * 2}mA")
elif btype == 0x04:
print(f" INTERFACE {b[2]} class=0x{b[5]:02x} {CLASS.get(b[5], '')} "
f"subclass=0x{b[6]:02x}{' (Boot)' if b[6] == 1 else ''} "
f"protocol=0x{b[7]:02x} {PROTO.get(b[7], '')} endpoint={b[4]}๊ฐ")
elif btype == 0x21:
print(f" HID bcdHID=0x{struct.unpack_from('<H', b, 2)[0]:04x} "
f"๋ฆฌํฌํธ ๋์คํฌ๋ฆฝํฐ {struct.unpack_from('<H', b, 7)[0]}๋ฐ์ดํธ")
elif btype == 0x05:
# bmAttributes ํ์ 2๋นํธ: 00=Control 01=Isochronous 10=Bulk 11=Interrupt
tt = {0: "Control", 1: "Isochronous", 2: "Bulk", 3: "Interrupt"}[b[3] & 3]
print(f" ENDPOINT 0x{b[2]:02x} {'IN' if b[2] & 0x80 else 'OUT'} "
f"{tt} wMaxPacketSize={struct.unpack_from('<H', b, 4)[0]} "
f"bInterval={b[6]}ms")
off += blen
print()
return
def show_reports(packets):
reports = list(hid_reports(packets))
print(f"[3] 8๋ฐ์ดํธ HID ํค๋ณด๋ ๋ฆฌํฌํธ {len(reports)}๊ฐ")
print(" modifier ๋ถํฌ:", dict(Counter(f"0x{r[0]:02x}" for _, r in reports)))
print(" keycode ๋ถํฌ:", dict(Counter(f"0x{r[2]:02x}" for _, r in reports)))
print(" 2~7๋ฒ ๋ฐ์ดํธ๊ฐ ํญ์ 0์ธ๊ฐ:",
all(r[1] == 0 and r[3:] == b"\x00" * 5 for _, r in reports))
print()
presses = [ts for ts, r in reports if r[2] != 0]
gaps = sorted(round(b - a, 3) for a, b in zip(presses, presses[1:]))
print(f" ๋๋ฆผ {len(presses)}ํ / ๋๋ฆผ ์ฌ์ด ๊ฐ๊ฒฉ {len(gaps)}๊ฐ")
print(f" ๊ธ์ ์ : ์ต์ {gaps[0]:.3f}s ~ ์ต๋ "
f"{max(g for g in gaps if g < 2):.3f}s ({sum(1 for g in gaps if g < 2)}๊ฐ)")
print(f" ๊ธ์ ์ฌ์ด: ์ต์ {min(g for g in gaps if g >= 2):.3f}s ~ ์ต๋ "
f"{gaps[-1]:.3f}s ({sum(1 for g in gaps if g >= 2)}๊ฐ)")
print(" โ 0.9s ์ 3.1s ์ฌ์ด๊ฐ ํ
๋น์ด ์์ด ์๊ณ๊ฐ 2s ๋ก ์์ ํ๊ฒ ์๋ฅธ๋ค")
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
want = sys.argv[2] if len(sys.argv) > 2 else "all"
packets = parse_pcapng(path)
if want in ("all", "devices"):
show_devices(packets)
if want in ("all", "config"):
show_config(packets, 32)
if want in ("all", "reports"):
show_reports(packets)
if __name__ == "__main__":
main()๋จผ์ ์ฅ์น ๋ชฉ๋ก๋ถํฐ.
python3 analyze.py extracted/My_PPT.pcapng devices
๋ค์ฏ ๊ฐ ์ค ๋ท์ ๋ฒค๋ ID๋ง ๋ด๋ ๋์ถฉ ๊ฐ๋ฆฐ๋ค. 8087์ ์ธํ
, 045e๋ ๋ง์ดํฌ๋ก์ํํธ, 0bda๋ ๋ฆฌ์ผํ
์ด๊ณ 214b:7250์ bDeviceClass=0x09, ์ฆ ํ๋ธ๋ค. ๋จ๋ ๊ฑด 3243:0131 ํ๋. bDeviceClass๊ฐ 0์ด๋ผ๋ ๊ฑด ํด๋์ค๋ฅผ ์ฅ์น๊ฐ ์๋๋ผ ์ธํฐํ์ด์ค๊ฐ ์ ํ๋ค๋ ๋ป์ด๋ผ, ์ค์ ๋ก ๋ญ์ง ๋ณด๋ ค๋ฉด ์ค์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ํผ์ณ์ผ ํ๋ค.
๊ทธ 32๋ฒ ์ฅ์น์ ์ค์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ํผ์ณ ๋ณธ๋ค.
python3 analyze.py extracted/My_PPT.pcapng config
์ด ํ๋ฉด์ด ์ด๋ฒ ํ์ด์์ ๊ฐ์ฅ ์ค์ํ ๊ทผ๊ฑฐ๋ค. ์ธํฐํ์ด์ค๊ฐ ๋์ธ๋ฐ ๋ ๋ค subclass=0x01 (Boot) ยท protocol=0x01 Keyboard ๋ก ์ ์ธ๋ผ ์๋ค. ๋ฌด์ ํ๋ฆฌ์ ํฐ๊ฐ ํํ ์ฐ๋ ๊ตฌ์ฑ์ด๋ค โ ํ๋๋ ์ผ๋ฐ ํค(๋ฐฉํฅํคยทF5), ๋ค๋ฅธ ํ๋๋ ๋ฉํฐ๋ฏธ๋์ด ํค๋ฅผ ์ฌ๋ฆฐ๋ค.
Boot ํ๋กํ ์ฝ์ด๋ผ๋ ๊ฒ ์ ์ค์ํ๋๋ฉด, ๋ฆฌํฌํธ ๋์คํฌ๋ฆฝํฐ๋ฅผ ์ฝ์ง ์์๋ ๋ฆฌํฌํธ ํ์์ด ํ์ ๋๊ธฐ ๋๋ฌธ์ด๋ค. USB HID ๋ช ์ธ ๋ถ๋ก B๊ฐ ๋ถํธ ํค๋ณด๋ ๋ฆฌํฌํธ๋ฅผ 8๋ฐ์ดํธ๋ก ๋ชป ๋ฐ์ ๋๋ค. ์บก์ณ์๋ ๋ฆฌํฌํธ ๋์คํฌ๋ฆฝํฐ๋ฅผ ์ค์ ๋ก ๋ฐ์ ์ค๋ ํจํท์ด ์๋๋ฐ(206๋ฐ์ดํธ์ง๋ฆฌ๊ฐ ์๋ค๊ณ ์ ์ธ๋ง ๋ผ ์๋ค), ๊ทธ๋๋ ํด์์ด ๋งํ์ง ์๋ ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
์๋ํฌ์ธํธ๋ 0x81 IN ์ธํฐ๋ฝํธ, bInterval=10ms. ์ค์ ๋ฐ์ดํฐ๋ ์ ๋ถ ์ฌ๊ธฐ๋ก ์ฌ๋ผ์จ๋ค.
๐งฑ ๋ฐฐ๊ฒฝ โ ๋ถํธ ํค๋ณด๋ ๋ฆฌํฌํธ 8๋ฐ์ดํธ
๋ถํธ ํค๋ณด๋ ๋ฆฌํฌํธ์ ๊ตฌ์กฐ๋ ์ด๋ ๋ค.
| ๋ฐ์ดํธ | ์ด๋ฆ | ๋ป |
|---|---|---|
| 0 | modifier | CtrlยทShiftยทAltยทGUI ๋นํธ๋งต (์ข์ฐ 8์ข ) |
| 1 | reserved | ํญ์ 0 |
| 2~7 | keycode 1~6 | ์ง๊ธ ๋๋ ค ์๋ ํค์ usage ID, ์ต๋ 6๊ฐ ๋์ |
์ฌ๊ธฐ์ ์์ฃผ ํท๊ฐ๋ฆฌ๋ ์ ํ๋. ์ด ๋ฆฌํฌํธ๋ "ํค๋ฅผ ๋๋ ๋ค"๋ ์ด๋ฒคํธ๊ฐ ์๋๋ผ "์ง๊ธ ์ด ์๊ฐ ๋๋ ค ์๋ ํค์ ๋ชฉ๋ก" ์ด๋ค. ๊ทธ๋์ ํค๋ฅผ ๋ผ๋ฉด keycode ์๋ฆฌ๊ฐ 0์ผ๋ก ์ฑ์์ง ๋ฆฌํฌํธ๊ฐ ํ ๋ฒ ๋ ์ฌ๋ผ์จ๋ค. ์บก์ณ์ ๋ฆฌํฌํธ๊ฐ 140๊ฐ์ธ๋ฐ ์ค์ ๋๋ฆผ์ด 70ํ์ธ ๊ฑด ๊ทธ๋์๋ค โ ๋๋ฅผ ๋ ํ๋, ๋ ๋ ํ๋.

์ด์ ์ค์ ๋ฆฌํฌํธ๋ฅผ ๋ณธ๋ค.
tshark -r /tmp/mnp.pcapng -c 60 -Y usbhid.data -T fields -e frame.time_relative -e usb.src -e usbhid.data
ํจํด์ด ํ๋์ ๋ค์ด์จ๋ค. ๊ฐ์ด ๋ฑ ์ธ ์ข ๋ฅ๋ค.
02 00 52 00 00 00 00 00โ Left Shift + UpArrow ๋ฅผ ๋๋ฆ02 00 51 00 00 00 00 00โ Left Shift + DownArrow ๋ฅผ ๋๋ฆ02 00 00 00 00 00 00 00โ Shift ๋ ์ก์ ์ฑ ํค๋ฅผ ๋
0x51๊ณผ 0x52๋ HID Usage Tables ํค๋ณด๋ ํ์ด์ง์์ ๊ฐ๊ฐ DownArrow์ UpArrow๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ฆ๊ณผ ๋์ด ์ฝ 40๋ฐ๋ฆฌ์ด ๊ฐ๊ฒฉ์ผ๋ก ๋ถ์ด ๋ค๋๋ค. ์ฌ๋์ด ์์ผ๋ก ๋๋ฅธ ๊ฒ์น๊ณ ๋ ์งง์๋ฐ, ๋ฆฌ๋ชจ์ปจ ํ์จ์ด๊ฐ ๋ฒํผ ํ ๋ฒ์ ๋๋ฆผ/๋์ ๋ถ์ฌ ์๊ธฐ ๋๋ฌธ์ด๋ค.
modifier๊ฐ ํญ์ 0x02(Left Shift)๋ก ๊ณ ์ ์ด๋ผ๋ ๊ฒ๋ ์ค์ํ๋ค. ๊ฐ์ด ๋ณํ์ง ์์ผ๋ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ง ์๋ค. ์ฆ ์๋ฏธ๋ฅผ ๋๋ฅด๋ ๊ฑด keycode ํ ๋ฐ์ดํธ๋ฟ์ด๊ณ , ๊ทธ ๊ฐ์ ๋ ๊ฐ์ง๋ฐ์ ์๋ค.
๐ ์ฝ์ง
์ฌ๊ธฐ๊น์ง ์ค๋ ๋์ ์ธ ๋ฒ ํ๋์๋ค.
โถ๐ ์ฝ์ง 1 โ tshark ๊ฐ ๋ด ํ์ผ์ ๋ชป ์ฝ๋๋ค
์ฒ์์ ๋ฌธ์ ํด๋์์ ๊ณง์ฅ tshark๋ฅผ ๋๋ ธ๋๋ ์ด๋ฌ๋ค.
tshark: You don't have permission to read the file "My_PPT.pcapng".ํ์ผ ์์ ์๋ ๋๊ณ ๊ถํ๋ -rw-rw-r--์ธ๋ฐ ๋ชป ์ฝ๋๋ค๊ณ ํ๋ค. ์์ ๊ถ ๋ฌธ์ ๊ฐ ์๋๋ผ AppArmor ๋ฌธ์ ์๋ค. ์ฐ๋ถํฌ 24.04๋ถํฐ /etc/apparmor.d/tshark ํ๋กํ์ผ์ด ๋ถ๋๋ฐ, ๋ฐ๊นฅ์ชฝ tshark ํ๋กํ์ผ์ abstractions/user-tmp๋ง includeํ๊ณ ํ ๋๋ ํ ๋ฆฌ๋ ์์ ์ด์ด ์ฃผ์ง ์๋๋ค. pcap ์ฝ๊ธฐ ๊ท์น(owner rw /**.pcap{,ng}{,.gz})์ ์์ชฝ dumpcap ์๋ธํ๋กํ์ผ์๋ง ๋ค์ด ์๋ค.
์ปค๋ ๊ฐ์ฌ ๋ก๊ทธ๋ฅผ ๋ณด๋ฉด ๋ช ํํ๋ค.
apparmor="DENIED" operation="open" profile="tshark"
name="/home/jinho/tsh_test/mp.pcapng" requested_mask="r" denied_mask="r"
fsuid=1000 ouid=1000์์คํ
ํ๋กํ์ผ์ ๊ณ ์น๋ ๋์ ํ์ฉ๋ /tmp๋ก ๋ณต์ฌํด ์ฐ๊ธฐ๋ก ํ๋ค. ๊ทธ๋์ ์ด ๊ธ์ tshark ๋ช
๋ น์ด ์ ๋ถ /tmp/mnp.pcapng๋ฅผ ์ฝ๋๋ค. capinfos๋ ํ๋กํ์ผ์ด ์์ด์ ์๋ ์๋ฆฌ์์ ๊ทธ๋ฅ ์ฝํ๋ค.
ํ์ผ ๊ถํ์ ์๋ฌด๋ฆฌ ๋ค์ฌ๋ค๋ด๋ ๋ต์ด ์ ๋์ค๋ฉด dmesg | grep -i apparmor ๋ฅผ ๋จผ์ ๋ณผ ๊ฒ.
โถ๐ ์ฝ์ง 2 โ usb.capdata ๋ ๋น์ด ์๋ค
๋ฆฌํฌํธ ํ์ด๋ก๋๋ฅผ ๊บผ๋ด๋ ค๊ณ ์ต๊ด์ ์ผ๋ก -e usb.capdata๋ฅผ ์ผ๋๋ฐ ์ปฌ๋ผ์ด ํต์งธ๋ก ๋น์ด์ ๋์๋ค. ํจํท์ ๋ถ๋ช
ํ ์๋๋ฐ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ ๋ป์ด๋ผ ํ์ฐธ ํค๋งธ๋ค.
์์ธ์ ํ๋ ์ด๋ฆ์ด์๋ค. Wireshark๊ฐ ์ธํฐํ์ด์ค ํด๋์ค๋ฅผ HID๋ก ์ธ์ํ๋ฉด ํ์ด๋ก๋๋ฅผ usb ๊ณ์ธต์ด ์๋๋ผ usbhid ๊ณ์ธต์ ๋ถ์ธ๋ค. ๊ทธ๋์ ํ๋๊ฐ usb.capdata๊ฐ ์๋๋ผ usbhid.data ๋ค. ํจํท ํ๋๋ฅผ -T json์ผ๋ก ํต์งธ๋ก ์ฐ์ด ๋ณด๊ณ ์์ผ ์์๋ค.
"usbhid.data": "02:00:52:00:00:00:00:00"ํ๋ ์ด๋ฆ์ ๊ธฐ์ต์ ์์กดํ์ง ๋ง๊ณ , ๋งํ๋ฉด ํจํท ํ๋๋ฅผ ํต์งธ๋ก ๋คํํด ์ค์ ํธ๋ฆฌ๋ฅผ ๋ณด๋ ๊ฒ ๋น ๋ฅด๋ค.
โถ๐ ์ฝ์ง 3 โ USBPcap ํค๋ ํ๋ ์์น๋ฅผ ๋๋์ค์ผ๋ก ์ ๋ค
์์ฒด ํ์๋ฅผ ์ง๋ฉด์ ์ ์ก ํ์
ํ๋๋ฅผ pkt[26]์ผ๋ก ์ฝ์๋๋ ์ธํฐ๋ฝํธ ์ ์ก์ด ํ๋๋ ์ ๊ฑธ๋ ค์ ์ด๋ ๊ฒ ์ฃฝ์๋ค.
HID ๋๋ฆผ ์ด๋ฒคํธ๊ฐ ์๋คUSBPcap ์์ฌํค๋์ ์ค์ ๋ฐฐ์น๋ ์ด๋ ๋ค.
| ์คํ์ | ํฌ๊ธฐ | ํ๋ |
|---|---|---|
| 0 | 2 | headerLen |
| 2 | 8 | irpId |
| 10 | 4 | status |
| 14 | 2 | function |
| 16 | 1 | info |
| 17 | 2 | bus |
| 19 | 2 | device |
| 21 | 1 | endpoint |
| 22 | 1 | transfer |
| 23 | 4 | dataLength |
transfer๋ 22๋ฒ์ด์ง 26๋ฒ์ด ์๋๋ค. 26๋ฒ์ dataLength์ ์ค๊ฐ์ด๋ผ ํญ์ 0์ด ๋์๊ณ , ๊ทธ๋์ ์กฐ๊ฑด์ด ํ ๋ฒ๋ ์ฐธ์ด ๋์ง ์์๋ค.
๊ฐ์ ์ค์๋ฅผ ํ ๋ฒ ๋ ํ๋ค. ์๋ํฌ์ธํธ์ bmAttributes ํ์ 2๋นํธ๋ฅผ {0:Control, 1:Interrupt, 2:Bulk, 3:Isochronous}๋ก ๋งคํํ๋๋ฐ, ๋ช
์ธ์ ์์๋ Control / Isochronous / Bulk / Interrupt ๋ค. ์ธํฐ๋ฝํธ ์๋ํฌ์ธํธ๊ฐ "Isochronous"๋ก ์ถ๋ ฅ๋ผ๋ ์ฝ๋๋ ๋ฉ์ฉกํ ๋๊ธฐ ๋๋ฌธ์ ์ถ๋ ฅ์ ๋์ผ๋ก ์ฝ๋ค๊ฐ ์ด์ํด์ ๋ฐ๊ฒฌํ๋ค.
๋ฐ์ดํธ ์คํ์ ๊ณผ enum ์์๋ ์ธ์ด ๊ฑธ ์ฐ์ง ๋ง๊ณ ๋ช ์ธ๋ฅผ ์ด์ด ์ธ๋ ๊ฒ ๊ฒฐ๊ตญ ๋น ๋ฅด๋ค. ํนํ ํ์์ฒ๋ผ ํ๋ ค๋ ํฌ๋์๊ฐ ์ ๋๋ ์ข ๋ฅ๊ฐ ์ํํ๋ค.
๐ฃ ํต์ฌ โ ๊ฐ๊ฒฉ์ด ๊ธ์๋ฅผ ๊ฐ๋ฅธ๋ค
์ ํธ๊ฐ ๋ ์ข ๋ฅ๋ผ๋ ๊ฑธ ์์์ผ๋, ์ด์ ์ด๊ฑธ ์ด๋ป๊ฒ ๋ฌธ์๋ก ๋ฐ๊พธ๋๋๊ฐ ๋จ๋๋ค.
๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅด๋ ๊ฑด ์ด์ง๋ฒ์ด๋ค. Down์ 0, Up์ 1๋ก ๋๊ณ 70๋นํธ๋ฅผ 8๋นํธ์ฉ ๋์ผ๋ฉด 8๊ธ์ ๋จ์ง ๋์จ๋ค. ๊ทธ๋ฐ๋ฐ 70์ 8๋ก ๋๋์ด๋จ์ด์ง์ง ์๊ณ , ๋ฌด์๋ณด๋ค ๋๋ฆผ ์๊ฐ์ ๋ณด๋ฉด ๋ฉ์ด๋ฆฌ๊ฐ ์ด๋ฏธ ๋์ ๋ณด์ธ๋ค. 5.6์ดยท5.9์ดยท6.4์ด์ ์ธ ๋ฒ ๋ชฐ์์น๊ณ ๋์ 9.6์ด๊น์ง ์กฐ์ฉํ๋ค. ์ด์ง๋ฒ์ด๋ผ๋ฉด ๊ทธ๋ด ์ด์ ๊ฐ ์๋ค.
๊ฐ๊ฒฉ์ ์ค์ ๋ก ์ธ์ด ๋ณธ๋ค.
python3 analyze.py extracted/My_PPT.pcapng reports
๊น๋ํ๊ฒ ๊ฐ๋ฆฐ๋ค.
- ๊ธ์ ์์ ๊ฐ๊ฒฉ: 0.328s ~ 0.880s (51๊ฐ)
- ๊ธ์ ์ฌ์ด์ ๊ฐ๊ฒฉ: 3.184s ~ 7.808s (18๊ฐ)
0.9์ด์ 3.1์ด ์ฌ์ด์ ๊ฐ์ด ํ๋๋ ์๋ค. ์ด๋ ๊ฒ ๋๋ ทํ๊ฒ ๋น์ด ์์ผ๋ฉด ์๊ณ๊ฐ์ ์ด๋๋ก ์ก๋ ๊ฒฐ๊ณผ๊ฐ ๊ฐ๋ค๋ ๋ป์ด๋ผ, 2์ด๋ก ์ก์๋ค. ๊ฐ๊ฒฉ 18๊ฐ๊ฐ ๊ฒฝ๊ณ์ด๋ฏ๋ก ๊ธ์๋ 19๊ฐ๋ค.
2~7๋ฒ ๋ฐ์ดํธ๊ฐ ํญ์ 0์ธ๊ฐ: True ๋ ๊ฐ์ด ํ์ธํด ๋๋ค. ๋์ ์
๋ ฅ์ด ํ ๋ฒ๋ ์์๋ค๋ ๋ป์ด๋ผ, keycode ์๋ฆฌ ํ๋๋ง ๋ด๋ ์ ๋ณด๋ฅผ ๋น ๋จ๋ฆฌ์ง ์๋๋ค.
๋ ์ข ๋ฅ ์ ํธ + ๊ธ์ ๋จ์ ๊ตฌ๋ถ + ๊ธ์๋ง๋ค ๊ธธ์ด๊ฐ 1~6์ผ๋ก ์ ๊ฐ๊ฐ. ์ด ์ธ ๊ฐ์ง๊ฐ ๊ฒน์น๋ฉด ๋ต์ ๋ชจ์ค๋ถํธ๋ค. ๊ณ ์ ๊ธธ์ด ๋ถํธ์๋ค๋ฉด ๋ฉ์ด๋ฆฌ ํฌ๊ธฐ๊ฐ ์ ๋ถ ๊ฐ์์ผ ํ๋ค.
๐งฑ ๋ฐฐ๊ฒฝ โ ๋ชจ์ค๋ถํธ
๋ชจ์ค๋ถํธ๋ ์ (dot)๊ณผ ์ (dash) ๋ ๊ธฐํธ๋ก ๊ธ์๋ฅผ ๋ง๋ ๋ค. ๊ท์น์ ๊ธธ์ด์ ๋น์จ๋ก ์ ์๋ผ ์๋ค.

์ถ์ฒ: Wikimedia Commons, public domain
์๋ ๋ชจ์ค๋ ํ๋์ ์ ํธ์ ์์ "๋๋ฆฐ ์๊ฐ์ ๊ธธ์ด"๋ก ์ ๊ณผ ์ ์ ๊ตฌ๋ณํ๋ค. ์ด๋ฒ ๋ฌธ์ ๋ ๊ทธ๊ฑธ ์กฐ๊ธ ๋ฐ๊ฟ์ ๋ฒํผ ๋ ๊ฐ์ ์ ๊ณผ ์ ์ ํ๋์ฉ ๋ฐฐ์ ํ๋ค. ํ๋ฆฌ์ ํฐ์๋ ์ด์ /๋ค์ ๋ฒํผ๋ฐ์ ์์ผ๋ ์์ฐ์ค๋ฌ์ด ๋ณํ์ด๊ณ , ๋๋ถ์ ๋๋ฆฐ ์๊ฐ์ ์ด ํ์ ์์ด ์ด๋ ํค์ธ์ง๋ง ๋ณด๋ฉด ๋๋ค.
๊ธ์ ์ฌ์ด ๊ฐ๊ฒฉ์ ๊ทธ๋๋ก ์ด์ ์๋ค. ํ์ 4๋ฒ ๊ท์น์ด ์ค์ ์บก์ณ์์ 3.18์ด ์ด์์ ๊ณต๋ฐฑ์ผ๋ก ๋ํ๋ ๊ฒ์ด๋ค.
๋ค๋ง ๋จ์ด ์ฌ์ด ๊ฐ๊ฒฉ์ ์ฐ์ด์ง ์์๋ค. 7๋จ์์ง๋ฆฌ ๋ ํฐ ๊ณต๋ฐฑ์ด ์์ด์ผ ํ๋๋ฐ ๊ฐ๊ฒฉ ์ต๋๊ฐ์ด 7.8์ด๋ก ๊ธ์ ์ฌ์ด ๋ฒ์ ์์ ๋ค์ด์จ๋ค. ๊ทธ๋์ ๋์ด์ฐ๊ธฐ๋ ๋ถํธ๋ก ํํํ๋ค โ ๋ค์ ๋์ค๋ ์ธ๋์ค์ฝ์ด๊ฐ ๊ทธ ์ญํ ์ ํ๋ค.
๐ฏ ์ ๊ณผ ์ ์ ๋ฐฉํฅ ์ ํ๊ธฐ
์ฌ๊ธฐ์ ํ ๋ฒ ๋ ๊ฐ๋ฆฐ๋ค. ๋ ํค ์ค ์ด๋ ์ชฝ์ด ์ ์ด๊ณ ์ด๋ ์ชฝ์ด ์ ์ธ๊ฐ? ์บก์ณ ์ด๋์๋ ์ ์ ํ ์๋ค.
์ถ์ธกํ์ง ์๊ณ ๋ ๋ค ๋๋ ค์ ๊ณจ๋๋ค. ํ๋๊ทธ ํ์์ด KHK{} ๋ก ์์ํ๋ค๋ ๊ฒ ๋ฌธ์ ์ค๋ช
์ ์์ผ๋, ์ฒซ ์ธ ๊ธ์๊ฐ KHK๋ก ๋จ์ด์ง๋ ์ชฝ์ด ์ ๋ต์ด๋ค.
#!/usr/bin/env python3
"""My Nervous PPT โ ์ด๋ ํค๊ฐ ์ (dot)์ด๊ณ ์ด๋ ํค๊ฐ ์ (dash)์ธ๊ฐ.
ํค๊ฐ 0x51/0x52 ๋๋ฟ์ด๋ผ ๋ชจ์ค๋ก ์ฝ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๋ค. ๋ ๋ค ๋๋ ค ๋ณด๊ณ
ํ๋๊ทธ ํ์(KHK)์ด ๋์ค๋ ์ชฝ์ ๊ณ ๋ฅธ๋ค. ์ถ์ธก์ด ์๋๋ผ ๋์กฐ๋ก ์ ํ๋ค.
์ฌ์ฉ๋ฒ: python3 orientation.py extracted/My_PPT.pcapng
"""
import sys
from solve import KEY_DOWN, KEY_UP, LETTER_GAP, MORSE, hid_reports, parse_pcapng
def groups(path):
presses = [(ts, r[2]) for ts, r in hid_reports(parse_pcapng(path)) if r[2] != 0]
out, cur, prev = [], [], None
for ts, key in presses:
if prev is not None and ts - prev > LETTER_GAP:
out.append(cur)
cur = []
cur.append(key)
prev = ts
out.append(cur)
return out
def decode(gs, dot_key):
letters = ["".join("." if k == dot_key else "-" for k in g) for g in gs]
return "".join(MORSE.get(c, "?") for c in letters), letters
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
gs = groups(path)
for label, dot_key in (("Down(0x51)=์ / Up(0x52)=์ ", KEY_DOWN),
("Up(0x52)=์ / Down(0x51)=์ ", KEY_UP)):
text, letters = decode(gs, dot_key)
bad = sum(1 for c in text if c == "?")
print(f"[{label}]")
print(f" ์ ์ธ ๊ธ์ ๋ชจ์ค: {' '.join(letters[:3])}")
print(f" ๋ณตํธ ๊ฒฐ๊ณผ : {text}")
print(f" ๋ชจ์คํ์ ์๋ ๊ธ์: {bad}๊ฐ "
f"โ {'ํ๋๊ทธ ํ์ KHK ๋ก ์์, ์ฑํ' if text.startswith('KHK') else 'ํ์ ๋ถ์ผ์น, ๊ธฐ๊ฐ'}")
print()
if __name__ == "__main__":
main()python3 orientation.py extracted/My_PPT.pcapng
ํ์ชฝ์ ๋ฌผ์ํ๊ฐ 0๊ฐ, ๋ค๋ฅธ ์ชฝ์ 5๊ฐ๋ค. ๋ค์งํ ์ชฝ์์ ๋ ๋ฒ์งธ ๊ธ์๊ฐ ----๋ก ๋์ค๋๋ฐ, ์ด๊ฑด ๋ชจ์ค ํ์ ์์ ์๋ ๋ถํธ๋ค. ์ฐ์ฐํ ๋ ๋ค ๊ทธ๋ด๋ฏํ๊ฒ ๋์ฌ ์ฌ์ง๊ฐ ์๋ค.
DownArrow๊ฐ ์ , UpArrow๊ฐ ์ . ํ๋ฆฌ์ ํฐ ๊ธฐ์ค์ผ๋ก๋ "๋ค์ ์ฌ๋ผ์ด๋"๊ฐ ์ ์ด๋ค. ์งง๊ฒ ํก ์น๋ ์ชฝ์ ์ ์ผ๋ก ๋ฐฐ์ ํ ์ ์ด๋ผ ๊ฐ๊ฐ์ ์ผ๋ก๋ ๋ง๋๋ค.
๐ Full Exploit
์ ์ฐฐ์์ ์ฐ๋ ํ์์ ๋ชจ์ค ๋ณตํธ๋ฅผ ๋ถ์ด๋ฉด ๋์ด๋ค. tshark ์์ด pcapng๋ฅผ ์ง์ ์ฝ์ผ๋ฏ๋ก ํ์ด์ฌ๋ง ์์ผ๋ฉด ๋์๊ฐ๋ค.
#!/usr/bin/env python3
"""My Nervous PPT (DreamHack, Silver 3 / misc-forensics) โ solver.
My_PPT.pcapng ๋ USBPcap ์ผ๋ก ๋ฌ USB ์บก์ณ๋ค. ํ๋ฆฌ์ ํฐ(๋ฌด์ ๋ฆฌ๋ชจ์ปจ)๋ ํธ์คํธ์
ํ๋ฒํ USB HID ํค๋ณด๋๋ก ๋ณด์ด๊ณ , "๋ค์ ์ฌ๋ผ์ด๋"/"์ด์ ์ฌ๋ผ์ด๋" ๋ฒํผ์ ๊ฐ๊ฐ
Shift+Down(0x51) / Shift+Up(0x52) ๋ฆฌํฌํธ๋ฅผ ์ฌ๋ฆฐ๋ค.
์ด ์คํฌ๋ฆฝํธ๋ tshark ์์ด pcapng ๋ฅผ ์ง์ ํ์ฑํด์
1) Enhanced Packet Block ์์ USBPcap ์์ฌํค๋๋ฅผ ๋ฒ๊ธฐ๊ณ
2) 8๋ฐ์ดํธ HID ํค๋ณด๋ ๋ฆฌํฌํธ ์ค keycode != 0 ์ธ ๊ฒ(=ํค ๋๋ฆผ)๋ง ๊ณจ๋ผ
3) ๋๋ฆผ ์ฌ์ด ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ๊ธ์ ๊ฒฝ๊ณ๋ฅผ ์๋ฅธ ๋ค
4) Down=dot / Up=dash ๋ก ๋ชจ์ค๋ถํธ๋ฅผ ๋ณตํธํ๋ค.
์ฌ์ฉ๋ฒ: python3 solve.py extracted/My_PPT.pcapng
"""
import struct
import sys
# โโ pcapng ๋ธ๋ก ํ์
SHB = 0x0A0D0D0A # Section Header Block
IDB = 0x00000001 # Interface Description Block
EPB = 0x00000006 # Enhanced Packet Block
# โโ USB HID ํค๋ณด๋ usage ID (HID Usage Tables, Keyboard/Keypad page 0x07)
KEY_DOWN = 0x51 # Keyboard DownArrow โ ํ๋ฆฌ์ ํฐ "๋ค์ ์ฌ๋ผ์ด๋"
KEY_UP = 0x52 # Keyboard UpArrow โ ํ๋ฆฌ์ ํฐ "์ด์ ์ฌ๋ผ์ด๋"
LETTER_GAP = 2.0 # ์ด. ์ด๋ณด๋ค ํฌ๋ฉด ๊ธ์ ๊ฒฝ๊ณ
MORSE = {
".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F",
"--.": "G", "....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L",
"--": "M", "-.": "N", "---": "O", ".--.": "P", "--.-": "Q", ".-.": "R",
"...": "S", "-": "T", "..-": "U", "...-": "V", ".--": "W", "-..-": "X",
"-.--": "Y", "--..": "Z",
"-----": "0", ".----": "1", "..---": "2", "...--": "3", "....-": "4",
".....": "5", "-....": "6", "--...": "7", "---..": "8", "----.": "9",
".-.-.-": ".", "--..--": ",", "..--..": "?", "-..-.": "/", "-....-": "-",
"-.--.": "(", "-.--.-": ")", "..--.-": "_", "-...-": "=", ".-.-.": "+",
"---...": ":", ".----.": "'", ".-..-.": '"', "...-..-": "$", ".--.-.": "@",
}
def parse_pcapng(path):
"""pcapng ๋ฅผ ํ์ด (ํ์์คํฌํ[์ด], ํ์ด๋ก๋ bytes) ๋ฆฌ์คํธ๋ฅผ ๋๋ ค์ค๋ค."""
raw = open(path, "rb").read()
endian, tsres = "<", 1e-6
out, off = [], 0
while off + 12 <= len(raw):
btype = struct.unpack_from(endian + "I", raw, off)[0]
if btype == SHB:
# byte-order magic ์ผ๋ก ์๋์์ ํ์ ํ ๋ค ๋ธ๋ก ๊ธธ์ด๋ฅผ ๋ค์ ์ฝ๋๋ค
endian = "<" if raw[off + 8:off + 12] == b"\x4d\x3c\x2b\x1a" else ">"
btype = struct.unpack_from(endian + "I", raw, off)[0]
blen = struct.unpack_from(endian + "I", raw, off + 4)[0]
if blen < 12 or off + blen > len(raw):
break
body = raw[off + 8: off + blen - 4]
if btype == IDB:
# if_tsresol ์ต์
(์ฝ๋ 9)์ด ์์ผ๋ฉด ํ์์คํฌํ ํด์๋๋ฅผ ๊ฐฑ์
opt = 8
while opt + 4 <= len(body):
ocode, olen = struct.unpack_from(endian + "HH", body, opt)
if ocode == 0:
break
if ocode == 9 and olen >= 1:
v = body[opt + 4]
tsres = 2.0 ** -(v & 0x7F) if v & 0x80 else 10.0 ** -v
opt += 4 + ((olen + 3) & ~3)
elif btype == EPB:
hi, lo, caplen = struct.unpack_from(endian + "III", body, 4)
ts = ((hi << 32) | lo) * tsres
out.append((ts, body[20:20 + caplen]))
off += blen
return out
def hid_reports(packets):
"""USBPcap ์์ฌํค๋๋ฅผ ๋ฒ๊ธฐ๊ณ 8๋ฐ์ดํธ HID ํค๋ณด๋ ๋ฆฌํฌํธ๋ง ์ถ๋ฆฐ๋ค."""
for ts, pkt in packets:
if len(pkt) < 2:
continue
hlen = struct.unpack_from("<H", pkt, 0)[0] # USBPcap headerLen
if hlen < 27 or len(pkt) < hlen:
continue
transfer = pkt[22] # 0x01 = interrupt
data = pkt[hlen:]
if transfer == 0x01 and len(data) == 8:
yield ts, data
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
packets = parse_pcapng(path)
# keycode ๊ฐ 0 ์ธ ๋ฆฌํฌํธ๋ "๋ผ๊ธฐ(release)" ๋ผ ๋ฒ๋ฆฌ๊ณ ๋๋ฆผ๋ง ๋จ๊ธด๋ค
presses = [(ts, r[2]) for ts, r in hid_reports(packets) if r[2] != 0]
if not presses:
sys.exit("HID ๋๋ฆผ ์ด๋ฒคํธ๊ฐ ์๋ค")
print(f"[*] ํจํท {len(packets)}๊ฐ / HID ๋๋ฆผ {len(presses)}ํ "
f"/ ์ฌ์ฉ keycode {sorted({k for _, k in presses})}")
# ๋๋ฆผ ์ฌ์ด ๊ฐ๊ฒฉ์ด LETTER_GAP ์ ๋์ผ๋ฉด ๊ธ์๋ฅผ ๋๋๋ค
letters, cur, prev = [], "", None
for ts, key in presses:
if prev is not None and ts - prev > LETTER_GAP:
letters.append(cur)
cur = ""
cur += "." if key == KEY_DOWN else "-" if key == KEY_UP else "?"
prev = ts
letters.append(cur)
print(f"[*] ๊ธ์ {len(letters)}๊ฐ๋ก ๋ถํ ")
decoded = ""
for i, code in enumerate(letters):
ch = MORSE.get(code, "?")
decoded += ch
print(f" {i:2d} {code:<8} {ch}")
# ๋ชจ์ค์๋ ์ค๊ดํธ๊ฐ ์๋ค. ์ถ์ ์๋ ๊ดํธ (โฆ) ๋ก ๋์ ์ผ๋ค.
flag = "KHK{" + decoded.split("(", 1)[1].rsplit(")", 1)[0] + "}" if "(" in decoded else decoded
print(f"\n[*] ๋ชจ์ค ๋ณตํธ ๊ฒฐ๊ณผ: {decoded}")
print(f"[+] FLAG: {flag}")
if __name__ == "__main__":
main()python3 solve.py extracted/My_PPT.pcapng
19๊ธ์๊ฐ ์์๋๋ก ๋จ์ด์ง๋ค. K H K ( L O O K _ A T _ M Y _ P P T ).
๋ง์ง๋ง์ ์์ ํ ๋ฒ ๋์ผ ํ๋ค. ๋ชจ์ค๋ถํธ์๋ ์ค๊ดํธ๊ฐ ์๋ค. ๊ทธ๋์ ์ถ์ ์๋ ์ฌ๋ ๊ดํธ -.--. ์ ๋ซ๋ ๊ดํธ -.--.- ๋ฅผ ๋์ ์ผ๋ค. ์ด ๋์ ์ค์ ๋ก ๋ชจ์ค ํ์ ์๋ ๋ถํธ๋ค(๊ฐ๊ฐ KN, KK ๋ก๋ ์ฝํ๋ค). ๊ดํธ๋ฅผ ์ค๊ดํธ๋ก ๋ฐ๊พธ๋ฉด ํ์์ด ๋ง๋๋ค.
๋์ด์ฐ๊ธฐ ์๋ฆฌ์ ๋ค์ด๊ฐ ..--.- ๋ ์ธ๋์ค์ฝ์ด๋ค. ์์์ ๋จ์ด ๊ฐ๊ฒฉ์ด ์ ์ฐ์๋ค๊ณ ํ๋ ๊ฒ ์ฌ๊ธฐ์ ๋ง์๋จ์ด์ง๋ค โ ์ถ์ ์๊ฐ ๊ณต๋ฐฑ ๋์ ๋ถํธ๋ฅผ ๋ฃ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด 7๋จ์ ๊ฐ๊ฒฉ์ ๋ง๋ค ํ์๊ฐ ์์๋ค.
KHK(LOOK_AT_MY_PPT) โ KHK{LOOK_AT_MY_PPT}ํ๋๊ทธ๋ KHK{LOOK_AT_MY_PPT}. "๋ด PPT ์ข ๋ด์ค"๋ผ๋, ๋ฌธ์ ์ค๋ช
๊ณผ ์ด์ด์ง๋ ๋ฌธ์ฅ์ด๋ค.
โ ๊ต์ฐจ ๊ฒ์ฆ โ tshark ๋ก ํ ๋ฒ ๋
์์ฒด ํ์๊ฐ pcapng๋ฅผ ์๋ชป ์ฝ์์ ๊ฐ๋ฅ์ฑ์ ์์ ๋ ค๊ณ , ํ์๋ฅผ ์ ํ ๊ณต์ ํ์ง ์๋ ๊ฒฝ๋ก๋ก ํ ๋ฒ ๋ ๋ณตํธํ๋ค. tshark๊ฐ ๋ฝ์ ์ค ํ๋๋ฅผ awk๋ก ํ์ด ์ ๊ณผ ์ ์ ๋ง๋๋ ๋ฐฉ์์ด๋ค.
#!/usr/bin/env bash
# My Nervous PPT โ tshark ๋ก ๋
๋ฆฝ ๋ณตํธ. solve.py ์ ์์ฒด ํ์์ ๊ฒฐ๊ณผ๊ฐ ๊ฐ์์ง ๋์กฐํ๋ค.
#
# ์ฐ๋ถํฌ 24.04 ์ดํ tshark ๋ AppArmor ํ๋กํ์ผ(/etc/apparmor.d/tshark)์ ๋ฌถ์ฌ
# ํ ๋๋ ํ ๋ฆฌ์ pcap ์ ๋ชป ์ฝ๋๋ค. ํ์ฉ๋ ๊ณณ์ด /tmp ๋ผ ๊ฑฐ๊ธฐ๋ก ๋ณต์ฌํด์ ๋๋ฆฐ๋ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
PCAP=${1:-extracted/My_PPT.pcapng}
cp "$PCAP" /tmp/mnp.pcapng
tshark -r /tmp/mnp.pcapng -Y usbhid.data \
-T fields -e frame.time_relative -e usbhid.data 2>/dev/null |
awk '
# ๋ฆฌํฌํธ 8๋ฐ์ดํธ ์ค 3๋ฒ์งธ(keycode) ์๋ฆฌ๋ง ๋ณธ๋ค. 00 ์ด๋ฉด ํค๋ฅผ ๋ ๊ฒ์ด๋ผ ๋ฒ๋ฆฐ๋ค.
{ key = substr($2, 5, 2) }
key == "00" { next }
{
if (prev != "" && $1 - prev > 2.0) printf " " # 2์ด ๋๊ฒ ์ฌ๋ฉด ๊ธ์ ๊ฒฝ๊ณ
printf "%s", (key == "51" ? "." : "-") # Down=์ Up=์
prev = $1
}
END { printf "\n" }
' | tee /tmp/mnp_morse.txt
python3 - /tmp/mnp_morse.txt <<'PY'
import sys
from solve import MORSE
codes = open(sys.argv[1]).read().split()
text = "".join(MORSE.get(c, "?") for c in codes)
print(f"๊ธ์ {len(codes)}๊ฐ โ {text}")
print("FLAG: KHK{" + text.split("(", 1)[1].rsplit(")", 1)[0] + "}")
PY./crosscheck_tshark.sh
19๋ฉ์ด๋ฆฌ, ๊ฐ์ ๋ฌธ์์ด, ๊ฐ์ ํ๋๊ทธ. ๋ ๊ฒฝ๋ก๊ฐ ์ผ์นํ๋ pcapng ํ์ฑ๊ณผ ์๊ณ๊ฐ ์ ํ์ด ๋ชจ๋ ๋ง๋ค๊ณ ๋ด๋ ๋๋ค.
๐ ์ฌํ
๋ฌธ์ ํด๋๋ง ์์ผ๋ฉด ํ ๋ฐฉ์ ๋ค์ ๋์๊ฐ๋๋ก ์คํฌ๋ฆฝํธ๋ฅผ ๋จ๊ฒผ๋ค. ์๋ฒ๋ VM๋ ํ์ ์๊ณ ํ์ด์ฌ 3๋ง ์์ผ๋ฉด ๋๋ค.
#!/usr/bin/env bash
# My Nervous PPT (DreamHack 1757, Silver 3 misc/forensics) โ ํ ๋ฐฉ ์ฌํ.
# ๋ฐฐํฌ๋ณธ pcapng ์์ flag ๋ฅผ ๋ฝ์ ๊ธฐ๋๊ฐ๊ณผ ๋์กฐํ๋ค. ์ธ๋ถ ์๋ฒยทVM ๋ถํ์.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null \
|| echo 'KHK{LOOK_AT_MY_PPT}')
OUT=$(timeout 120 python3 solve.py extracted/My_PPT.pcapng 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'KHK\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && [ "$FLAG" = "$EXPECT" ]; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '$EXPECT')"; exit 1./reproduce.sh
๋ก์ปฌ ํ๊ฒฝ์ Ubuntu 25.10 ยท Python 3.13 ยท tshark 4.4.x ๋ค. solve.py๋ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง ์ฐ๋ฏ๋ก tshark๊ฐ ์์ด๋ ๋๊ณ , ๊ต์ฐจ ๊ฒ์ฆ ์คํฌ๋ฆฝํธ๋ง tshark๋ฅผ ์๊ตฌํ๋ค.
๐ ๊ฒฐ๋ก
ํค๋ณด๋ ์บก์ณ๋ "๋ฌด์์ ์ณค๋"๋ณด๋ค "์ธ์ ์ณค๋"๊ฐ ๋ต์ผ ๋๊ฐ ์๋ค
USB HID ๋ฌธ์ ๋ผ๊ณ ํ๋ฉด ๋ณดํต keycode๋ฅผ ๋ฌธ์๋ก ๋งคํํด์ ๋ฌธ์ฅ์ ๋ณต์ํ๋ค. ์ด ๋ฌธ์ ๋ ๊ทธ ๊ธธ์ด ๋งํ ์๋ค โ ๋ฑ์ฅํ๋ ํค๊ฐ ๋ฐฉํฅํค ๋ ๊ฐ๋ฟ์ด๋ผ ๋งคํํ ๊ฒ ์๋ค.
๋์ ํ์์คํฌํ๊ฐ ์ด์ ์์๋ค. ๋๋ฆผ ๊ฐ๊ฒฉ์ด 0.9์ด์ 3.1์ด ์ฌ์ด์์ ์์ ํ ๊ฐ๋ผ์ง๋ค๋ ์ฌ์ค ํ๋๋ก ๊ธ์ ๊ฒฝ๊ณ๊ฐ ์ ํด์ก๊ณ , ๊ทธ๋๋ถํฐ๋ ๊ทธ๋ฅ ๋ชจ์ค๋ถํธ์๋ค. ํจํท ์บก์ณ์ ์๊ฐ ์ ๋ณด๋ฅผ ํ์ด๋ก๋๋งํผ ์ง์งํ๊ฒ ๋ณผ ๊ฒ.
๋ถํธ ํ๋กํ ์ฝ์ด ๋ฆฌํฌํธ ํ์์ ๋ณด์ฆํ๋ค
์ด ์บก์ณ์๋ HID ๋ฆฌํฌํธ ๋์คํฌ๋ฆฝํฐ๋ฅผ ์ค์ ๋ก ๋ฐ์ ์ค๋ ํจํท์ด ์๋ค. 206๋ฐ์ดํธ์ง๋ฆฌ๊ฐ ์๋ค๊ณ ์ ์ธ๋ง ๋ผ ์์ ๋ฟ์ด๋ค. ๊ทธ๋ฐ๋ฐ๋ 8๋ฐ์ดํธ ๋ฆฌํฌํธ๋ฅผ ์ ํํ ํด์ํ ์ ์์๋ ๊ฑด ์ธํฐํ์ด์ค๊ฐ subclass=0x01 (Boot)๋ก ์ ์ธ๋ผ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
๋์คํฌ๋ฆฝํฐ๋ฅผ ํ์ฑํด ๋ณด๋ ๊ฑด ๊ท์ฐฎ์ ๋ณด์ฌ๋, "์ด 8๋ฐ์ดํธ๋ฅผ ์ด๋ ๊ฒ ์ฝ์ด๋ ๋๋๊ฐ"์ ๋ํ ๋ต์ ์ค๋ค. ์ถ์ธก์ผ๋ก ๋์ด๊ฐ๋ค๋ฉด modifier ๋ฐ์ดํธ๋ฅผ keycode๋ก ์คํดํ๋ ์์ ์ฌ๊ณ ๊ฐ ๋ฌ์ ๊ฒ์ด๋ค.
์ ๋งคํ ๊ฒฐ์ ์ ๋ ๊ฐ๋ ๋ค ๋๋ ค์ ๊ณ ๋ฅธ๋ค
์ ๊ณผ ์ ์ ๋ฐฉํฅ์ ์บก์ณ ์ด๋์๋ ์์๋ค. ๊ฐ์ผ๋ก ์ ํ๊ณ ๊ฒฐ๊ณผ๊ฐ ์ด์ํ๋ฉด ๋ค์ง๋ ๋ฐฉ์์ผ๋ก๋ ํ๋ฆฌ๊ธด ํ์ง๋ง, ๋ ๊ฒฝ์ฐ๋ฅผ ๋ค ๋๋ ค์ "๋ชจ์ค ํ์ ์๋ ๋ถํธ๊ฐ ๋ช ๊ฐ ๋์ค๋"๋ก ํ์ ํ๋ฉด ๊ทผ๊ฑฐ๊ฐ ๋จ๋๋ค. ์ค์ ๋ก ๋ค์ง์ ์ชฝ์ ---- ๊ฐ์ ์กด์ฌํ์ง ์๋ ๋ถํธ๋ฅผ ๋ฑ์๋ค.
๋ฐฉ์ด ๊ด์ โ ๋ฌด์ ํ๋ฆฌ์ ํฐ๋ ๋ฌด์ ํค๋ณด๋๋ค
๋ฌธ์ ์ ๋ณ๊ฐ๋ก, ์ด ์บก์ณ๊ฐ ๋ณด์ฌ ์ฃผ๋ ๊ทธ๋ฆผ ์์ฒด๊ฐ ํ ๋ฒ์ฏค ์๊ฐํด ๋ณผ ๋งํ๋ค. ๋ฐํ์ฉ ๋ฆฌ๋ชจ์ปจ์ bInterfaceProtocol=0x01, ์ฆ ํธ์คํธ์๊ฒ ํค๋ณด๋๋ก ์๊ธฐ๋ฅผ ์๊ฐํ๋ค. ํ๋ฉด์ ๋๊ธฐ๋ ๊ถํ๋ง ์๋ ๊ฒ ์๋๋ผ ์์์ ํค ์
๋ ฅ์ ์ฌ๋ฆด ์ ์๋ค๋ ๋ป์ด๋ค.
USB ์บก์ณ๊ฐ ๋จ์ผ๋ฉด ๊ทธ ํค ์
๋ ฅ์ด ์ ๋ถ ํ๋ฌธ์ผ๋ก ๋ณต์๋๋ค๋ ๊ฒ๋ ๊ฐ์ด ๋ด์ผ ํ๋ค. ์ด๋ฒ์ ๋ฐฉํฅํค๋ฟ์ด๋ผ ๋ฌดํดํ์ง๋ง, ๊ฐ์ ์๋ฆฌ์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ณค๋ค๋ฉด usbhid.data ๋ช ์ค๋ก ๊ทธ๋๋ก ์์ด ๋์จ๋ค. ๊ฐ์์ฅ์ด๋ ํ์์ค์ ๋์ธ ์ ์ฒด๋ถ๋ช
์ USB ๋๊ธ์ ๊ทธ๋ฅ ๊ฝ๋ ์ต๊ด์ด ์ ์ํํ์ง์ ๋ํ ๊ฝค ๊ตฌ์ฒด์ ์ธ ๊ทธ๋ฆผ์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ