flag format: tjctf{...} đây là 2 challenge độc lập...

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

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

Câu hỏi

image.png
image.png
chall.7z

flag format: tjctf{...}
đây là 2 challenge độc lập, không liên quan tới nhau
tìm ra flag rồi viết write up chi tiết kèm câu lệnh + script đã dùng

Suy nghĩ

bash -lc cp /mnt/data/solve_loud_clean.py /mnt/data/solve_loud_packets.py cp /mnt/data/solve_glitch_test.py /mnt/data/solve_glitch.py ls -l /mnt/data/solve_loud_packets.py /mnt/data/solve_glitch.py /mnt/data/recovered_loud.wav /mnt/data/loud_packets_spectrogram_mono.png

Thought for 10m 45s

Trả lời

Flags

forensics/loud-packets
tjctf{v3ry_l0ud_pc4p_file}

misc/glitch
tjctf{D3S1GN+TECH_:)}

Các file/script mình tạo để tái hiện: [solve_loud_packets.py]
solve_glitch.py
recovered_loud.wav
spectrogram.png


1. forensics/loud-packets

Ý tưởng

File chall.pcap chứa nhiều packet UDP có payload bắt đầu bằng magic BTAV. Sau BTAV là sequence number 4 byte. Payload còn lại chính là các chunk của một file.

Khi sort theo sequence rồi nối payload lại, byte đầu ra bắt đầu bằng:

text
RIFF....WAVE

=> Đây là file WAV. Mở spectrogram của WAV thì thấy flag được vẽ bằng font 5x7.

Lệnh đã dùng

bash
python3 -m pip install py7zr numpy scipy pillow python3 solve_loud_packets.py

Tạo spectrogram:

bash
sox recovered_loud.wav -n spectrogram -x 3000 -y 700 -z 80 -r -m -o spectrogram.png

Script chính

python
#!/usr/bin/env python3 import struct, wave, numpy as np PCAP = "chall.pcap" OUT_WAV = "recovered_loud.wav" with open(PCAP, "rb") as f: gh = f.read(24) endian = ">" if gh[:4] == b"\xa1\xb2\xc3\xd4" else "<" frames = [] with open(PCAP, "rb") as f: f.read(24) while True: ph = f.read(16) if len(ph) < 16: break ts_sec, ts_usec, incl_len, orig_len = struct.unpack(endian + "IIII", ph) pkt = f.read(incl_len) if len(pkt) < 42 or pkt[12:14] != b"\x08\x00": continue ihl = (pkt[14] & 0x0f) * 4 if pkt[23] != 17: continue udp = 14 + ihl sport, dport, ulen, _ = struct.unpack("!HHHH", pkt[udp:udp+8]) payload = pkt[udp+8:udp+ulen] if payload.startswith(b"BTAV"): seq = struct.unpack("!I", payload[4:8])[0] frames.append((seq, payload[8:])) frames.sort() wav_bytes = b"".join(p for _, p in frames) with open(OUT_WAV, "wb") as f: f.write(wav_bytes) print(wav_bytes[:12]) print(f"wrote {OUT_WAV}")

Output quan trọng:

text
b'RIFF\x143\x04\x00WAVE'

Sau khi mở spectrogram, text hiện ra là:

text
tjctf{v3ry_l0ud_pc4p_file}

2. misc/glitch

Ý tưởng

Hint “Do not resist” trỏ tới resistor color code:

text
black = 0 brown = 1 red = 2 orange = 3 yellow = 4 green = 5 blue = 6 violet = 7 gray = 8 white = 9

Ảnh là các dải màu bị nhiễu. Sau khi median filter để bỏ noise, mỗi hàng chính có 2 màu lớn ở vùng bên trái/middle. Đổi 2 màu đó thành 2 chữ số rồi parse thành ASCII decimal.

Ví dụ:

text
blue gray -> 68 -> 'D' green brown -> 51 -> '3' gray orange -> 83 -> 'S' yellow white -> 49 -> '1' ...

Chuỗi nhận được:

text
D3S1GN+TECH_:)

Challenge yêu cầu wrap trong tjctf{} nên flag là:

text
tjctf{D3S1GN+TECH_:)}

Lệnh đã dùng

bash
python3 solve_glitch.py

Script chính

python
from PIL import Image, ImageFilter import numpy as np IMG = "glitch.png" img = Image.open(IMG).convert("RGB").filter(ImageFilter.MedianFilter(7)) arr = np.array(img).astype(float) refs = { "K": (0, 0, 0), # black 0 "N": (95, 45, 35), # brown 1 "R": (255, 0, 0), # red 2 "O": (255, 125, 0), # orange 3 "Y": (255, 230, 0), # yellow 4 "G": (50, 255, 0), # green 5 "B": (0, 0, 255), # blue 6 "V": (255, 0, 255), # violet 7 "E": (128, 128, 128), # gray 8 "W": (245, 245, 245), # white 9 } keys = list(refs) ref = np.array([refs[k] for k in keys], float) flat = arr.reshape(-1, 3) cls = np.argmin(((flat[:, None, :] - ref[None, :, :]) ** 2).sum(axis=2), axis=1) letters = np.array(keys)[cls].reshape(arr.shape[:2]) def row_pair(y, min_run=350): row = letters[y, :1600] # bỏ phần border đen/vàng bên phải raw = [] cur = str(row[0]) start = 0 for x, c0 in enumerate(row[1:], 1): c = str(c0) if c != cur: if x - start >= 25: raw.append((cur, start, x)) cur, start = c, x if len(row) - start >= 25: raw.append((cur, start, len(row))) merged = [] for c, s, e in raw: if merged and merged[-1][0] == c and s - merged[-1][2] < 120: merged[-1] = (c, merged[-1][1], e) else: merged.append((c, s, e)) major = [(c, s, e) for c, s, e in merged if e - s >= min_run] if len(major) >= 2: return major[0][0] + major[1][0] return None pairs = [] prev = None for y in range(0, letters.shape[0], 3): p = row_pair(y) # same-color pairs chỉ là artifact do glitch/noise split dải màu if p and p[0] != p[1] and p != prev: pairs.append(p) prev = p digit = { "K": "0", "N": "1", "R": "2", "O": "3", "Y": "4", "G": "5", "B": "6", "V": "7", "E": "8", "W": "9", } digits = "".join(digit[c] for p in pairs for c in p) text = "".join(chr(int(digits[i:i+2])) for i in range(0, len(digits), 2)) print("pairs :", pairs) print("digits:", digits) print("text :", text) print("flag :", "tjctf{" + text + "}")

Output:

text
pairs : ['BE', 'GN', 'EO', 'YW', 'VN', 'VE', 'YO', 'EY', 'BW', 'BV', 'VR', 'WG', 'GE', 'YN'] digits: 6851834971784384696772955841 text : D3S1GN+TECH_:) flag : tjctf{D3S1GN+TECH_:)}

Chia sẻ Q&A này