$ cat writeup.md…
$ cat writeup.md…
broncoctf2026
Task: a mislabeled challenge.zip is actually a 1250-layer deep chain of recursively nested archives (gzip -> tar -> bzip2 -> password-protected 7z -> zip -> repeat). Solution: script 7z as a universal driver to list+extract each layer, deriving each 7z password from the first member's filename, filtering the tar @PaxHeader artifact, and recursing until flag.txt.
"I was trying to compress my files and my script got a little carried away... Can you help me find my original file?"
Hint: the 7z files are password protected, and the password is the name of the first file inside.
Given a single challenge.zip downloaded from the CTFd instance. Goal: recover the original file (which holds the flag) from a runaway recursive compression chain.
Despite the .zip extension, file challenge.zip reports gzip-compressed data (was "layer1.tar"), so the extension is misleading — this is a matryoshka-style nested archive.
The chain cycles the same compression pattern once per "round", repeating for hundreds of rounds:
gzip (.tar.gz) -> tar (.tar) -> bzip2 (.bz2) -> 7z (password-protected AES) -> zip -> (back to gzip)
Two gotchas per round:
@PaxHeader: tar extraction emits an extra @PaxHeader pseudo-file that must be filtered so the "single next file" logic keeps working.layer290 contains layer292.zip, so its password is layer292.zip.7z handles gzip, bzip2, tar, zip and 7z transparently, so a single loop using 7z as a universal driver can walk every layer type.
Automate the traversal:
7z l -slt <archive> lists contents. The first Path = line is the archive itself; subsequent Path = lines are members. Type = gives the format.7z x -y -o<dir> <archive> extracts. If it fails and the type is 7z, retry with -p<candidate> where the candidate is the first member's name (.name, the full filename with extension, is what works; .stem tried as fallback).@PaxHeader, then recurse into the single remaining extracted file.The script traversed 1,250 archive layers. At level 1250, layer1000.zip extracted to flag.txt containing the flag.
#!/usr/bin/env python3 from pathlib import Path import re, shutil, subprocess root = Path(__file__).resolve().parent current = root / "challenge.zip" work = root / "layers" shutil.rmtree(work, ignore_errors=True) work.mkdir() for level in range(1, 10000): listing = subprocess.run(["7z", "l", "-slt", str(current)], capture_output=True, text=True) archive_type = re.search(r"^Type = (.+)$", listing.stdout, re.M) if listing.returncode or not archive_type: print(f"Stopped at level {level}: {current} is not an archive") print(current.read_bytes()[:1000]) break paths = re.findall(r"^Path = (.+)$", listing.stdout, re.M)[1:] # skip the archive itself out = work / f"{level:04d}" out.mkdir() command = ["7z", "x", "-y", f"-o{out}", str(current)] result = subprocess.run(command, capture_output=True, text=True) password = None if result.returncode and archive_type.group(1).lower() == "7z" and paths: for candidate in (Path(paths[0]).name, Path(paths[0]).stem): shutil.rmtree(out); out.mkdir() result = subprocess.run(command + [f"-p{candidate}"], capture_output=True, text=True) if result.returncode == 0: password = candidate break if result.returncode: raise RuntimeError(f"Extraction failed at {current}") files = [p for p in out.rglob("*") if p.is_file() and p.name != "@PaxHeader"] print(level, archive_type.group(1), current.name, "password=" + repr(password), "->", [p.name for p in files]) current = files[0]
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar