This challenge was firstly deployed with a bug which allowed us to solve it in a matter of seconds.

The goal was to create a file names is_admin in your $USER_DIR, and this is the code which handeled the file creation.

<?php
include('config.php');

$filename = strtolower($_POST['filename']);
if ($filename == 'is_admin' || preg_match('/\./', $filename)) {
  die('Hello hacker :)');
}

@mkdir($USER_DIR);
file_put_contents($USER_DIR . '/' . basename($_POST['filename']), $_POST['contents']);

header('Location: /');

It was simple as sending a POST request with the filename parameter set to /is_admin and boom we received the flag.

CBCTF{plz fix PHP Bug #72374}

As it obviously wasn’t the intended way, we reported the bug to the admins and they took down the challenge. After 1 hour they put a fixed version online and gave everyone the old flag as an hint.

By opening the bug tracker of PHP we understood that when remove-path or add-path are used the first char of the first zipped file is removed.

In the WebApp we had an export function which uses remove-path based on our input, than signs the name of the output zip. On the other hand we had an import function which verified the signature and put the file in our $USER_DIR.

export.php

<?php
include('config.php');

$tmpfile = tempnam('/tmp', 'cbs');

if (preg_match('/\.|\\\\|^\//', $_GET['dir']) === 1) {
  die('hello hacker :(');
}

$zip = new ZipArchive();
$zip->open($tmpfile, ZipArchive::CREATE);
$options = array('remove_path' => $_GET['dir']);

$dir = trim($_GET['dir'], '/');
$zip->addGlob($dir . '/*', 0, $options);

$zip->close();

$hmac = hash_hmac('sha256', file_get_contents($tmpfile), $MY_SECRET);
header("Content-Disposition: attachment; filename='${hmac}.zip'");
readfile($tmpfile);

unlink($tmpfile);

import.php

<?php
include('config.php');

$tmpfile = $_FILES['file']['tmp_name'];
$hmac = hash_hmac('sha256', file_get_contents($tmpfile), $MY_SECRET);
if ($_FILES['file']['name'] !== "${hmac}.zip") {
  die('hello hacker :)');
}

$zip = new ZipArchive();
$zip->open($tmpfile);
$zip->extractTo($USER_DIR);

$zip->close();

header('Location: /');

Having all these information it was trivial to get the new flag.

We created a file named ais_admin, then we used the export function by setting as dir our $USER_DIR + %2F, which is / urlencoded, this gave us a zip containing a file named is_admin, because of the PHP bug we mentioned before. Finally we uploaded the exported zip and boom^2 we received the flag.

CBCTF{sorry-we-had-a-pitty-bug;;}

We want to thank the organizers who gave us 100 bonus points for reporting the initial and unintended bug.