webfreehard

clankers-market

b01lersc

Task: a Flask upload feature lets authenticated users place files inside a temporary Git repository that is later served and re-dumped with git-dumper. Solution: inject a crafted Git index v4 plus a loose object so `git checkout .` recreates and executes a hidden post-checkout hook, which writes the real flag to flag.txt.

$ ls tags/ techniques/
arbitrary_git_metadata_writegit_checkout_hook_executiongit_index_v4_path_compression_bypassloose_object_injection

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

clankers-market — b01lers CTF 2026

Description

Upload a repo artifact and inspect extraction output.

The challenge gives source for a Flask app with a "Clanker Feature" upload endpoint. After login, a user may upload up to two files, the server places them into /tmp/git_storage, sanitizes the directory, exposes it with python3 -m http.server, and then runs git-dumper to reconstruct the repository into /tmp/dump.

The goal is to turn that pipeline into code execution so the app itself writes the real flag into /tmp/dump/flag.txt, which is later read and shown in the response.

Challenge Summary

The exploit chain is:

  1. Register any new account; authentication is not a barrier.
  2. Abuse file upload paths to write arbitrary files inside /tmp/git_storage, including .git/index and loose objects under .git/objects/.
  3. Let the app sanitize and serve the repository, then let git-dumper recursively download the exposed /.git/ directory.
  4. Make dump-side git checkout . recreate .git/hooks/post-checkout from a malicious index entry and execute it.
  5. Have the hook run /usr/local/bin/read-flag > flag.txt, so the application reads back the real flag.

The only real obstacle is the source-side sanitizer: it deletes any file whose raw bytes contain lowercase git. A plain .git/hooks/post-checkout index entry is therefore removed before the repo is served. The bypass is to use Git index version 4 pathname compression so Git reconstructs .git/hooks/post-checkout during parsing even though the uploaded raw index bytes never contain the contiguous substring git.

Relevant Source Analysis

1. Authentication is trivial

/register simply inserts a new username/password pair into an in-memory dictionary and logs us in immediately:

elif username in USERS: error = "Username already exists." else: USERS[username] = password session["username"] = username return redirect(url_for("listing"))

So the exploit begins by registering a fresh random account.

...

$ grep --similar

Similar writeups