← Back to Writeups
CUSTOMMediumForensics

Учебная граната (Training Grenade)

XESXOR9/2/20263 min read
#forensics#custom#medium#filename_metadata_stego#in_memory_archive_traversal#recursive_zip_descent#linux

Учебная граната (Training Grenade)

Platform: bug-makers | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2025-10-04 | Status: Solved Techniques: filename_metadata_stego, in_memory_archive_traversal, recursive_zip_descent

Summary

Task: a recursively nested ZIP bomb (~1014 levels), each level holding a 1MB 'a'-filled decoy plus the next inner zip. Solution: descend the nesting safely in memory with Python zipfile+BytesIO and concatenate the decoy filenames across all levels — the filenames spell a paragraph ending with the flag.

Учебная граната (Training Grenade) — bug-makers

Description

Хоть она и учебная - это не значит, что она не может навредить, будь аккуратнее!

English summary: A file named bomb is provided. It is a recursively nested ZIP archive (a zip bomb). The hint ("Although it's a training one, that doesn't mean it can't hurt — be careful!") warns that naively extracting it can blow up your disk. The goal is to find the flag hidden somewhere inside the nesting.

Analysis

file bomb identifies it as a Zip archive (deflate). Unzipping one level produces: ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  • a ~1MB decoy text file filled entirely with the letter a, named with a single-character name (e.g. .Z, .I, ...),
  • an inner boom.zip that repeats the same structure.

This is a classic recursively nested zip bomb. The archive size shrinks by exactly 1180 bytes per nesting level (1197620 → 1196440 → ...), which implies roughly 1014 levels of nesting before reaching the bottom.

Three obvious places to look for the flag all turn out to be dead ends:

  • Decoy file contents — every decoy at every level is pure a.
  • Innermost file — at depth 1014 the final zip contains only file.txt (1,000,000 bytes of a). No flag.
  • Archive comments / per-file comments — all empty. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key insight: the only thing that varies between levels is the decoy filename. The flag (and an introductory paragraph) is hidden in the ZIP metadata — specifically the decoy file names — not in any file content. The zip bomb itself is misdirection: the identical a-filled payloads draw attention away from the filenames.

Solution

  1. Confirm the format and structure:

    bash
    file bomb            # Zip archive data (deflate)
    unzip -l bomb        # shows a 1MB decoy + boom.zip

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  1. Do not extract recursively to disk — it would explode. Instead descend the nesting entirely in memory using Python's zipfile over io.BytesIO. At each level, identify the inner zip member (boom.zip / *.zip) to continue, and collect the other member's filename (the decoy) into an ordered list.

  2. Concatenate the collected decoy filenames in nesting order. The result spells out, one informative character per level, a Wikipedia-style paragraph about the ZIP file format, ending with: ...Your flag is BugCTF{REDACTED}. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solver script (meta.py)

python
#!/usr/bin/env python3
import zipfile, io

data = open("boom.zip", "rb").read()
depth = 0
fnames = []
while True:
    try:
        zf = zipfile.ZipFile(io.BytesIO(data))
    except Exception:
        break
    inner = None
    for info in zf.infolist():
        n = info.filename
        if n.endswith(".zip") or n == "boom.zip":
            inner = n            # the next nested archive
        else:
            fnames.append(n)     # collect decoy filename at this level
    if inner is None:
        break
    data = zf.read(inner)
    depth += 1
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

print("depth:", depth)
print("".join(fnames))           # reveals: ...Your flag is BugCTF{REDACTED}

The concatenated filenames end with the flag. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍