impossible stego
impossible stego
Platform: Sekai2026 | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-06-28 | Status: Solved Techniques: api_transcript_analysis, base64_decoding, custom_extractor_execution, replay_tool_writes, source_tree_reconstruction
Summary
Task: recover a message hidden with a Claude-generated custom PNG steganography scheme. Solution: decode the provided Claude Code API transcript, reconstruct the generated Python stego source tree from logged Write/Edit tool calls, then run the recovered extractor on flag.png.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
sekai2026| ID:20260628_sekai2026_impossible_stego - Tags: steganography, base64, lsb, png, hmac, chacha20, custom_stego, ai_transcript, claude_code, source_recovery, jsonl
- Indicators: archive contains flag.png and messages.log, messages.log is JSONL with base64 encoded req_body and resp_body, Claude Code transcript contains Write/Edit tool calls, custom stego package has python3 -m stego extract command
- Source:
20260628_sekai2026_impossible_stego.md
Foothold
Vulnerability / Misconfiguration
- Api_transcript_analysis
- Base64_decoding
- Custom_extractor_execution
- Replay_tool_writes
- Source_tree_reconstruction
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- api_transcript_analysis
- base64_decoding
- custom_extractor_execution
- replay_tool_writes
- source_tree_reconstruction
- Tags: steganography, base64, lsb, png, hmac, chacha20, custom_stego, ai_transcript, claude_code, source_recovery, jsonl
Original Writeup
<details><summary>Click to expand original content</summary>Description
i rolled my own stego and made sure to make it wayyyyy too hard for someone to guess how it works...
er, i mean claude did. i totally read the code tho.
The provided archive contains only two files:
misc_impossible-stego/flag.png misc_impossible-stego/messages.log
flag.png is an RGBA PNG image. messages.log is much more important: it is a JSONL log of API requests and responses to an Anthropic/Claude-compatible gateway.
Analysis
The challenge text says Claude wrote the stego code, so the right first step is not to guess the pixel algorithm. Instead, inspect the log and recover what Claude generated.
Each line of messages.log is a JSON object. The interesting fields are req_body and resp_body; both are base64-encoded. Decoding request bodies reveals Claude Code conversation state, including tool calls such as Write, Edit, Read, and Bash.
A quick parser confirms the late conversation contains the whole source tree creation history:
import json, base64
from pathlib import Path
p = Path("misc_impossible-stego/messages.log")
last = None
for line in p.open():
rec = json.loads(line)
s = rec.get("req_body") or ""
if not s:
continue
try:
body = json.loads(base64.b64decode(s))
except Exception:
continue
if "messages" in body:
last = body
for msg in last["messages"]:
for c in msg.get("content") or []:
if isinstance(c, dict) and c.get("type") == "tool_use":
print(c.get("name"), c.get("input", {}).get("file_path"))
The log shows writes for files like:
stego/secret.py stego/crypto/kdf.py stego/crypto/chacha20.py stego/crypto/csprng.py stego/coding/frame.py stego/image/scatter.py stego/image/embed.py stego/pipeline.py stego/__main__.py
So messages.log effectively leaks the exact steganography implementation.
Solution
Reconstruct the final source tree by replaying logged Write and Edit operations. The following script extracts the last full Claude request body, then applies all file writes/edits whose paths are under /home/claude/projects/impossible-stego/:
#!/usr/bin/env python3
import base64
import json
import shutil
from pathlib import Path
log = Path("misc_impossible-stego/messages.log")
root = Path("recovered_project")
base = "/home/claude/projects/impossible-stego/"
body = None
for line in log.open():
rec = json.loads(line)
s = rec.get("req_body") or ""
if not s:
continue
try:
decoded = json.loads(base64.b64decode(s))
except Exception:
continue
if "messages" in decoded:
body = decoded
if root.exists():
shutil.rmtree(root)
root.mkdir()
def local(path):
if not path or not path.startswith(base):
return None
return root / path[len(base):]
for msg in body["messages"]:
for c in msg.get("content") or []:
if not (isinstance(c, dict) and c.get("type") == "tool_use"):
continue
name = c.get("name")
inp = c.get("input") or {}
p = local(inp.get("file_path"))
if p is None:
continue
if name == "Write":
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(inp.get("content", ""), encoding="utf-8")
elif name == "Edit":
text = p.read_text(encoding="utf-8")
old = inp.get("old_string", "")
new = inp.get("new_string", "")
if old not in text:
raise RuntimeError(f"edit target not found in {p}: {old[:80]!r}")
p.write_text(text.replace(old, new, 1), encoding="utf-8")
elif name == "MultiEdit":
text = p.read_text(encoding="utf-8")
for e in inp.get("edits") or []:
old = e.get("old_string", "")
new = e.get("new_string", "")
if old not in text:
raise RuntimeError(f"edit target not found in {p}: {old[:80]!r}")
text = text.replace(old, new, 1)
p.write_text(text, encoding="utf-8")
print(f"reconstructed in {root}")
After reconstruction, use the recovered command-line extractor against the challenge image:
cd recovered_project python3 -m stego extract ../misc_impossible-stego/flag.png
Output:
SEKAI{REDACTED}
Why this works
The custom stego itself is intentionally over-engineered: image-bound key derivation, ChaCha20, HMAC, S-box/whitening, and keyed scatter over RGB LSB slots. However, the security assumption is "the attacker does not know the source".
The supplied log violates that assumption. It records the complete Claude Code session that generated the package, including all source files and later edits. Reconstructing the source gives the exact keys/constants and extraction order, so no steganalysis or brute force is needed.
</details>Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR