--- title: 'LA CTF 2024: misc/my poor git' date: 2024-02-21 tags: ['ctf', 'ctf-misc', 'git'] --- ## Task > **misc/my poor git** > > My poor git server! I think someone took a hammer to the server and ruined a few of the files! > > The git repo is available at /flag.git > > [poor-git.chall.lac.tf](https://poor-git.chall.lac.tf) - `Author: burturt` - `Points: 465 points` - `Solves: 72 / 1074 (6.704%)` ## Writeup The challenge tells us to get the flag at `/flag.git`, so let's try to clone this repo: ```console $ git clone https://poor-git.chall.lac.tf/flag.git Cloning into 'flag'... remote: error: Could not read b061db539557e1bb4dbcffd936a2d1412eeb1f66 remote: fatal: Failed to traverse parents of commit c2e6e9737a8a666667b27c3a1dc84a76c8f4dab3 remote: aborting due to possible repository corruption on the remote side. fatal: protocol error: bad pack header ``` The clone fails since we get an error because we cannot read `b061db5`, a parent of commit `c2e6e97`. Let's try again with a depth limit on the number of commits, to avoid trying to traverse to a parent we cannot read: ```console $ git clone https://poor-git.chall.lac.tf/flag.git --depth 1 Cloning into 'flag'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), 938 bytes | 938.00 KiB/s, done. ``` This time our clone is successful, let's see what we downloaded: ```console $ cd flag $ ls -a . .. .git nothing_here.txt $ cat nothing_here.txt there's nothing here, go away $ git log --oneline 217ecd3 (grafted, HEAD -> main, origin/main, origin/HEAD) remove flag again uugh ``` Nothing interesting. Let's try again with a higher depth: ```console $ cd .. $ rm -rf flag $ git clone https://poor-git.chall.lac.tf/flag.git --depth 2 Cloning into 'flag'... remote: Counting objects: 6, done. remote: Compressing objects: 100% (2/2), done. remote: Total 6 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (6/6), 1.83 KiB | 1.83 MiB/s, done. $ cd flag $ git log --oneline * 217ecd3 (HEAD -> main, origin/main, origin/HEAD) remove flag again uugh * c2e6e97 (grafted) Merge branch 'fix' $ git checkout c2e6e97 Note: switching to 'c2e6e97' ...blah blah blah something something detached head state... $ ls -a . .. flag.txt .git $ cat flag.txt lactf{not the flag} ``` We get another file, but it is not the real flag. The commit that we switched to, `c2e6e97`, was also the same commit we got an error on when we first tried to clone the repo. If we increase the depth any more, we will get an error: ```console $ git clone https://poor-git.chall.lac.tf/flag.git --depth 3 Cloning into 'flag'... fatal: the remote end hung up unexpectedly ``` It appears we will not be able to get the flag by just running a `git clone` command. Looking into how `git` retrieves data from a Git server leads us to some documentation on Git transfer protocols. We learn that there are two different protocols, the "dumb" protocol and the "smart" protocol. The description of the follow-up challenge to this one mentions the dumb protocol being disabled, so we can assume that this challenge's solution uses it. > Apparently my poor git server didn't like being called "dumb", so it disabled its dumb capabilities. In the dumb protocol, the Git client sends a series of GET requests to clone the repo. We can emulate the protocol using `curl`. First, we get the list of remote references and SHA-1s from `/info/refs`: ```console $ curl https://poor-git.chall.lac.tf/flag.git/info/refs 217ecd3c93b00c6b7404473d3bdfcb222a22edf4 refs/heads/main ``` Now we check the HEAD reference at `/HEAD`: ```console $ curl https://poor-git.chall.lac.tf/flag.git/HEAD ref: refs/heads/main ``` Next, we clone HEAD, which is available at `/objects/{SHA-1[:2]}/{SHA-1[2:]}`. We know the SHA-1 of HEAD from `/info/refs`, so our request will be `GET /objects/21/7ecd3c93b00c6b7404473d3bdfcb222a22edf4`. This will give us zlib-compressed data, so we need to pipe to `zlib-flate -uncompress`: ```console $ curl https://poor-git.chall.lac.tf/flag.git/objects/21/7ecd3c93b00c6b7404473d3bdfcb222a22edf4 | zlib-flate -uncompress commit 1128tree b46f24349a27913ddfa5c8a29bc3bcc8d2722358 parent c2e6e9737a8a666667b27c3a1dc84a76c8f4dab3 author burturt <31748545+burturt@users.noreply.github.com> 1705793830 -0800 committer burturt <31748545+burturt@users.noreply.github.com> 1705793830 -0800 gpgsig -----BEGIN PGP SIGNATURE----- ...omitted for brevity... -----END PGP SIGNATURE----- remove flag again uugh ``` We already were able to get this commit with just `git clone`, let's continue to its parent. ```console $ # but first, let's define a bash function to make it easier to make these requests: $ get () { curl https://poor-git.chall.lac.tf/flag.git/objects/${1:0:2}/${1:2:9999} | zlib-flate -uncompress } $ get c2e6e9737a8a666667b27c3a1dc84a76c8f4dab3 commit 1172tree 47442ca74fffb4c5d1293fbd7bb0bc048d8fdff4 parent ac4d7070179f49c03ed06d98c19068cc8e2d74c5 parent b061db539557e1bb4dbcffd936a2d1412eeb1f66 ...omitted for brevity... Merge branch 'fix' ``` This is the commit giving us errors. We can see that there are two parents of this commit, with `b061db5` being unreadable (we get a 404 if we try). Let's continue to the other parent: ```console $ get ac4d7070179f49c03ed06d98c19068cc8e2d74c5 commit 1117tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 parent 91fede8498f1ffd14699ec8d7f43f383f3147e64 ... remove flag $ get 91fede8498f1ffd14699ec8d7f43f383f3147e64 commit 1135tree 1ee98dd3a67505c02a1ab4739f1a46a25d116599 parent e3fde9187ea42af07d95bb3e891b6338738810ab ... remove newline at end of file $ get e3fde9187ea42af07d95bb3e891b6338738810ab commit 1114tree 75e7c1f3b178941ef76997bc3a9ca19bdc0dda09 parent fd87b3b95fc02fea268ecea9dce20964b285f50b ... add flag ``` This commit seems like it is what we are looking for. Now we will get the commit data instead of continuing to its parent: ```console $ get 75e7c1f3b178941ef76997bc3a9ca19bdc0dda09 | xxd 00000000: 7472 6565 2033 3600 3130 3036 3434 2066 tree 36.100644 f 00000010: 6c61 672e 7478 7400 741f a59a c9ec 45f9 lag.txt.t.....E. 00000020: 78d7 99bd 88b7 290b c304 abdd x.....)..... $ # the data after the second 00 byte tells us what we need to get $ get 741fa59ac9ec45f978d799bd88b7290bc304abdd blob 32lactf{u51n9_dum8_g17_pr070c01z} ``` The last request gets us the flag: `lactf{u51n9_dum8_g17_pr070c01z}` ## Reference - [git transfer protocols](https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols)