webfreeeasy

Free Cloud Storage

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.

$ ls tags/ techniques/
arbitrary_file_writephp_webshell_uploaddecoy_flag_detectionzip_slip_path_traversal

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Free Cloud Storage — TJCTF 2026

Description

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.

Analysis

Application Architecture

  • Server: Apache/2.4.54 (Debian), PHP 7.4.33, running on Kubernetes
  • Framework: Plain PHP with Composer dependency chumper/zipper v1.0.2
  • Functionality: Upload a .zip file → extracted to /var/www/html/uploads/
  • Decoy: flag.php exists but only outputs "Nice try, but there's no flag here!"
  • Real flag: Located at /var/www/html/flag.txt

Source Code Review

upload.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" } }

Vulnerability: Zip Slip (Path Traversal in ZIP Extraction)

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.

...

$ grep --similar

Similar writeups