WEBVERSE

Loading...

mediumFile UploadFree

Calliope Gallery

A small online gallery for emerging painters and printmakers. Artists keep their own portfolio pages, drop in new work as it dries, and the front-of- house team writes the catalog copy. Uploads pass a strict JPEG header check before they hit the wall.

The Scenario

Calliope was founded in 2021 by two former MFA students who couldn't get a toehold on the New York gallery circuit and decided to build their own. Three years on, the roster is about eighty emerging painters, illustrators and printmakers across North America, with bricks-and- mortar viewing rooms in Tribeca and Highland Park. Last spring a contractor wired in a thumbnail-resizer to keep the portfolio dirs fast on mobile and left a small config tweak in the upload tree to make the resizer work; the team has long since forgotten the integration is in there at all.

Challenge Intel

Synopsis

Portfolio uploader validates JPEG magic bytes but the upload directory is configured to execute .jpg files as PHP. A polyglot file slips through the header check and runs as PHP when served.

What It Is

account/portfolio.php?action=add reads the first four bytes of the uploaded file and requires \xFF\xD8\xFF\xE0 (JPEG SOI + JFIF APP0) or \xFF\xD8\xFF\xE1 (JPEG SOI + EXIF APP1). Anything else is rejected with 'Only JPEG images accepted.' The uploaded file is stored under /var/www/html/portfolio/<artist_id>/<safe-basename>.jpg — the saved extension is forced to .jpg regardless of what the player names the upload. The portfolio tree carries an .htaccess at /var/www/html/portfolio/.htaccess containing `AddType application/x-httpd-php .jpg`, left behind from the thumbnail-resizer integration; Apache is configured with AllowOverride All on /var/www/html so the override takes effect. The intended solve is a polyglot: take a valid JPEG (the seeded portfolio images work, or any JPEG with an EXIF comment block), append `<?php echo shell_exec($_GET["c"]); ?>` to the end of the file (or stash it in an EXIF comment via exiftool). The file still begins with FFD8FFE0/FFD8FFE1 so the magic-byte check passes. Once Apache serves the file, the PHP handler executes the embedded block and the player reads /flag.txt via the c query parameter.

Who It's For

A player who has already broken extension-blacklist uploaders and is ready to move to content-validation bypasses. Assumes familiarity with the JPEG file structure, the FFD8FF SOI marker, and basic Apache handler/AddType behaviour.

Skills You'll Practice

  • Recognising a magic-byte content check and constructing a polyglot that satisfies it
  • Appending executable payloads to image files without breaking image parsing
  • Reading Apache handler-mapping behaviour from server response signals

What You'll Gain

  • Pattern for bypassing image-only upload filters that validate header bytes but not the full file
  • Working mental model of why content-type validation alone never prevents server-side code execution when the handler is wrong

Ready to hack Calliope Gallery?

This challenge is free. Sign up and start hacking.

Calliope Gallery — WebVerse Pro File Upload