CodeShell.kr - Terminal

Challenge

687 条记录,题面说只剩一个包。难点在于要认出 from 字段构成的血缘链,而不是把 687 条记录当成平铺数据。

One packet remains

只剩一个包。

1
https://codeshell.kr/challenges/misc-terminal/

Solution

附件 misc-terminal-player.zipreadme.txtcapture.jsonl(687 行)。

Step 1:readme 给出目标包 id 1aaa01930941,并定义:id 是「去掉 id 字段后、按键排序的紧凑 JSON」的 SHA-256 前 12 个 hex;Origin 是 000000000000;字节掩码是 SHA256(bytes.fromhex(from))[0]。校验 id 公式 687/687 全部匹配。

Step 2:687 个 id 与 164 个 from 中,163 个 from 恰好等于某条记录的 id,唯一例外是 000000000000(Origin)。即 from 是「父包 id」,数据构成以 Origin 为根的唯一血缘链

Step 3:从目标包按 from → 父包 id 回溯到 Origin,再反向输出明文(data XOR mask(from)),得到 37 字符;目标包自身明文为 },正是 flag 结尾。

1
2
4dd809f1f06c from=000000000000 data=f3 -> 'C'   (Origin 包)
1aaa01930941 from=a1f64d4fc7c6 data=1a -> '}' (目标包)

Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python3
"""CodeShell.kr - Terminal (Misc, 400p) solver.

Each record's `from` field is the parent packet id, so the 687 records form a
lineage rooted at the Origin id 000000000000. The plaintext byte of a record is
data XOR SHA256(bytes.fromhex(from))[0]. Walking from the target packet back to
the Origin and printing in reverse recovers the flag.
"""
import hashlib
import json

CAPTURE = "extracted/misc-terminal/capture.jsonl"
TARGET = "1aaa01930941"
ORIGIN = "000000000000"


def mask(frm: str) -> int:
return hashlib.sha256(bytes.fromhex(frm)).digest()[0]


def main():
recs = [json.loads(line) for line in open(CAPTURE)]
by_id = {r["id"]: r for r in recs}

# the id formula is checkable, so verify it before trusting the lineage
ok = 0
for r in recs:
body = {k: v for k, v in r.items() if k != "id"}
h = hashlib.sha256(json.dumps(body, sort_keys=True,
separators=(",", ":")).encode()).hexdigest()
ok += h[:12] == r["id"]
print(f"id formula matches {ok}/{len(recs)}")

cur, lineage = by_id[TARGET], []
while True:
lineage.append(cur)
if cur["from"] == ORIGIN:
break
cur = by_id[cur["from"]]
print("lineage length:", len(lineage))
print("".join(chr(int(r["data"], 16) ^ mask(r["from"]))
for r in reversed(lineage)))


if __name__ == "__main__":
main()
CodeShell{THE_LAST_PACKET_CAME_FIRST}