flag format: FLAG{...} solve challenge rồi viết ch...

Tạo vào: 29 tháng 5, 2026

Trả lời bằng GPT-5.5 bởi Chat01

Câu hỏi

memory_artifact.txt
traffic.pcap

flag format: FLAG{...}
solve challenge rồi viết cho tôi 1 file md hướng dẫn giải lại chi tiết gồm lệnh và script đã dùng

Suy nghĩ

bash -lc cat > /mnt/data/solve_challenge.md <<'MD'

Write-up giải challenge

Flag

text
FLAG{V0l4t1l3_Br0ws3r_St4t3}

Ý tưởng tổng quát

Challenge gồm 2 artifact:

  • memory_artifact.txt: kết quả strings từ memory browser, chứa JWT/resume token, signature fragment, request id đang active và nonce.
  • traffic.pcap: lưu lượng HTTP. Response /api/secret được gzip + chunked, bên trong là JSON chứa ciphertext AES-256-CBC.

Từ memory xác định stream đúng là req-live-4821 vì:

text
window.__TRACE__={"candidateRequestIds":["req-archive-1138","req-telemetry-2244","req-live-4821"],"active":"req-live-4821"} sessionStorage["recoveryNonce"]="nonce-ephemeral-browser-9f3c71" IndexedDB:volatile-auth:{"sigFragment":"c2lnbmF0dXJl","traceRef":"req-live-4821"}

sigFragment base64 decode ra signature, vì vậy token đầy đủ là:

text
<resumeToken>.c2lnbmF0dXJl

Key AES được derive bằng SHA-256 từ chuỗi:

text
<full_jwt>|req-live-4821|nonce-ephemeral-browser-9f3c71

Ciphertext trong JSON có dạng IV || ciphertext, 16 byte đầu là IV.

Các lệnh kiểm tra ban đầu

bash
file /mnt/data/memory_artifact.txt /mnt/data/traffic.pcap head -n 40 /mnt/data/memory_artifact.txt strings -a /mnt/data/traffic.pcap | grep -E 'FLAG|flag|GET|POST|HTTP|token|nonce|req-|Authorization|Cookie|websocket|gzip|chunk|jwt|eyJ' -n

Kết quả đáng chú ý trong pcap:

text
GET /api/archive HTTP/1.1 X-Request-ID: req-archive-1138 Transfer-Encoding: chunked Content-Encoding: gzip GET /api/telemetry HTTP/1.1 X-Request-ID: req-telemetry-2244 Transfer-Encoding: chunked Content-Encoding: gzip GET /api/secret HTTP/1.1 X-Request-ID: req-live-4821 Transfer-Encoding: chunked Content-Encoding: gzip

Script 1: parse pcap, dechunk và gunzip HTTP response

Lưu thành extract_http.py:

python
#!/usr/bin/env python3 import struct import socket import gzip from pathlib import Path def parse_pcap(path): data = Path(path).read_bytes() magic = data[:4] endian = '<' if magic == b'\xd4\xc3\xb2\xa1' else '>' *_, linktype = struct.unpack(endian + 'HHiiii', data[4:24]) off = 24 streams = {} while off + 16 <= len(data): _ts_sec, _ts_usec, incl_len, _orig_len = struct.unpack(endian + 'IIII', data[off:off + 16]) off += 16 pkt = data[off:off + incl_len] off += incl_len # traffic.pcap là Linux cooked capture v2, linktype 276. if linktype != 276 or len(pkt) < 20: continue proto = struct.unpack('!H', pkt[0:2])[0] if proto != 0x0800: continue ip = pkt[20:] if len(ip) < 20: continue ihl = (ip[0] & 0x0f) * 4 if ip[9] != 6: # TCP continue total_len = struct.unpack('!H', ip[2:4])[0] src = socket.inet_ntoa(ip[12:16]) dst = socket.inet_ntoa(ip[16:20]) tcp = ip[ihl:total_len] if len(tcp) < 20: continue sport, dport, seq, _ack, off_flags = struct.unpack('!HHIIH', tcp[:14]) tcp_header_len = (off_flags >> 12) * 4 body = tcp[tcp_header_len:] if body: streams.setdefault((src, sport, dst, dport), []).append((seq, body)) return {k: b''.join(chunk for _, chunk in sorted(v)) for k, v in streams.items()} def dechunk(body): out = b'' i = 0 while True: j = body.find(b'\r\n', i) if j < 0: break size_line = body[i:j].split(b';', 1)[0] try: size = int(size_line, 16) except ValueError: break i = j + 2 if size == 0: break out += body[i:i + size] i += size + 2 return out for stream, payload in parse_pcap('/mnt/data/traffic.pcap').items(): if not payload.startswith(b'HTTP/1.1'): continue header, _, body = payload.partition(b'\r\n\r\n') headers = header.decode('latin1').split('\r\n') request_id = next((h.split(': ', 1)[1] for h in headers if h.lower().startswith('x-request-id:')), '') if b'Transfer-Encoding: chunked' in header: body = dechunk(body) if b'Content-Encoding: gzip' in header: body = gzip.decompress(body) print(f'=== {stream} RID={request_id} ===') print(body.decode('utf-8', 'replace'))

Chạy:

bash
python3 extract_http.py

Output quan trọng:

json
{"type":"encrypted_flag","algorithm":"AES-256-CBC","stream":"live","request_id":"req-live-4821","data":"4FJ8/vLJ5Tf84E1j6Ee2imsCxno4ARV2+7byPofRBHCGbPFGElbYYuiejakKXlTY"}

Script 2: giải mã AES-256-CBC

Lưu thành solve.py:

python
#!/usr/bin/env python3 import base64 import hashlib from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes resume_token = ( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' 'eyJ1c2VyIjoiYW5hbHlzdCIsInNlc3Npb24iOiJlcGhlbWVyYWwiLCJpYXQiOjE3MDAwMDAwMDB9' ) sig_fragment = 'c2lnbmF0dXJl' request_id = 'req-live-4821' nonce = 'nonce-ephemeral-browser-9f3c71' enc_b64 = '4FJ8/vLJ5Tf84E1j6Ee2imsCxno4ARV2+7byPofRBHCGbPFGElbYYuiejakKXlTY' full_jwt = f'{resume_token}.{sig_fragment}' key_material = f'{full_jwt}|{request_id}|{nonce}' key = hashlib.sha256(key_material.encode()).digest() raw = base64.b64decode(enc_b64) iv = raw[:16] ct = raw[16:] cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) pt = cipher.decryptor().update(ct) + cipher.decryptor().finalize() pad = pt[-1] if not 1 <= pad <= 16 or pt[-pad:] != bytes([pad]) * pad: raise ValueError('Invalid PKCS#7 padding') flag = pt[:-pad].decode() print(flag)

Chạy:

bash
python3 solve.py

Kết quả:

text
FLAG{V0l4t1l3_Br0ws3r_St4t3}

Ghi chú kiểm chứng

Có thể decode sigFragment như sau:

bash
python3 - <<'PY' import base64 print(base64.urlsafe_b64decode('c2lnbmF0dXJl' + '===').decode()) PY

Output:

text
signature

MD ls -l /mnt/data/solve_challenge.md

Thought for 6m 10s

Trả lời

Đã giải ra flag:

text
FLAG{V0l4t1l3_Br0ws3r_St4t3}

Mình đã tạo file hướng dẫn chi tiết tại đây: solve_challenge.md

Nguồn memory artifact đã dùng: (memory_artifact.txt)

Chia sẻ Q&A này