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/
$ 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:
- Register any new account; authentication is not a barrier.
- Abuse file upload paths to write arbitrary files inside
/tmp/git_storage, including.git/indexand loose objects under.git/objects/. - Let the app sanitize and serve the repository, then let
git-dumperrecursively download the exposed/.git/directory. - Make dump-side
git checkout .recreate.git/hooks/post-checkoutfrom a malicious index entry and execute it. - 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
- [web][Pro]Where's Your HEAD At?— kalmarctf
- [misc][Pro]Git Hoarder— kalmarctf
- [web][Pro]Guild— hackthebox
- [web][free]Prison Pipeline— hackthebox_business_ctf_2024
- [web][Pro]Photo Storage— miptctf