neven@dimitri-dev:~$
cd ../hacknite-2021

$cat loading-picture-php.mdWebHacknite 2.0

Loading picture.php

The upload form only checks the file type in the browser, so a PHP file renamed as an image gets through and runs commands on the server.

  • #file-upload
  • #rce
  • #php
  • #postman

$man loading-picture-php

What this one teaches:

  • Why a check done in the browser can always be skipped.
  • Sending a request directly, without the form, using a tool like Postman.
  • Why uploaded files must never be allowed to run as code on the server.

$hint --one-at-a-time

Stuck? Open one hint, then go back and try again before reading the next.

Hint 1 of 2show

The task says the size limit stops non-images. Where is that limit enforced?

Hint 2 of 2show

If you can upload a file the server will run, you can run commands on the server.

Full solution below. Spoilers ahead.

Task description

To teach himself to draw, Mario redraws nature photos. He ran out of ideas for photos, so he built a site where others can upload pictures for him to use. Mario was careful and limited the file size so malicious people couldn’t upload something that isn’t a photo.

The original was in Croatian; this is my translation.

The upload form

The page lets you upload an image. Mario’s safeguard is a size and type check, but the task hints at where it runs: in the browser. A check that runs in the browser only guards the form on the page. If you send the upload yourself, without the form, there’s nothing to stop you.

A file that runs

The site runs PHP, so a .php file placed on it will be executed by the server when visited. The plan is to upload one that runs whatever command we ask for. A tiny PHP file does it:

<?php echo shell_exec($_GET['cmd']); ?>

shell_exec runs a system command and $_GET['cmd'] is a value we pass in the address, so visiting the uploaded file with ?cmd=... runs that command on the server. We disguised it as an image so it looked like an allowed upload.

Skipping the browser check

Because the type check only lives in the page, we sent the upload straight to the server with Postman, a tool for making web requests by hand. With no form in the way, the disguised file uploaded fine.

Then we visited the uploaded file and used the cmd parameter to run ordinary Linux commands: ls to list files and cat to print one. A short look around found the flag file, and reading it gave the flag.

Two things went wrong here, and both matter. A security check has to run on the server, since the browser can be bypassed. And uploaded files must be stored where they can’t run as code, so a disguised script is just an inert file.

$cat flag.txt revealCTF2021[750396018838]

Written by Neven Marić. This writeup was written with the help of Claude Code (Anthropic).