167 lines
6.4 KiB
Markdown
167 lines
6.4 KiB
Markdown
---
|
|
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)
|