3kCTF-2020 - reporter writeup

25-07-2020 - rekter0

Summary

498pts - 4 Solves

Reporter is an online markdown reporting tool. it's free to use for everyone. there's a secret report we need located here

source code is provided, dns rebind, LFD

Vulnerability

the challenge is mainly a web app that converts markdown to html and you can save your reports. it has 2 parts, as description says we need to access a secret report located at /secret_report that returns 403 when opening it, so we need some internal access

the first snippet from backend.php looks interesting

if(@$_POST['deliver']){
    $thisDoc=file_get_contents($dir.'/file.html');
    $images = preg_match_all("/<img src=\"(.*?)\" /", $thisDoc, $matches);
    foreach ($matches[1] as $key => $value) {
        $thisDoc = str_replace($value , "data:image/png;base64,".base64_encode(fetch_remote_file($value)) , $thisDoc ) ;
    }

basically it downloads the images in the report and embed them into the html using fetch_remote_file function

function fetch_remote_file($url) {
    $config['disallowed_remote_hosts'] = array('localhost');
    $config['disallowed_remote_addresses'] = array("0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/29", "192.0.2.0/24", "192.88.99.0/24", "192.168.0.0/16", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4",);
    $url_components = @parse_url($url);
    if (!isset($url_components['scheme'])) {
        return false;
    }
    if (@($url_components['port'])) {
        return false;
    }
    if (!$url_components) {
        return false;
    }
    if ((!empty($url_components['scheme']) && !in_array($url_components['scheme'], array('http', 'https')))) {
        return false;
    }
    if (array_key_exists("user", $url_components) || array_key_exists("pass", $url_components)) {
        return false;
    }
    if ((!empty($config['disallowed_remote_hosts']) && in_array($url_components['host'], $config['disallowed_remote_hosts']))) {
        return false;
    }
    $addresses = get_ip_by_hostname($url_components['host']);
    $destination_address = $addresses[0];
    if (!empty($config['disallowed_remote_addresses'])) {
        foreach ($config['disallowed_remote_addresses'] as $disallowed_address) {
            $ip_range = fetch_ip_range($disallowed_address);
            $packed_address = my_inet_pton($destination_address);
            if (is_array($ip_range)) {
                if (strcmp($ip_range[0], $packed_address) <= 0 && strcmp($ip_range[1], $packed_address) >= 0) {
                    return false;
                }
            } elseif ($destination_address == $disallowed_address) {
                return false;
            }
        }
    }
    $opts = array('http' => array('follow_location' => 0,));
    $context = stream_context_create($opts);
    return file_get_contents($url, false, $context);
}

the function checks if the url given has a valid http/https protocol and checks if the address is not blacklisted or the host doesnt point to a of blacklisted ip addresses then send it to file_get_contents

$config['disallowed_remote_hosts'] = array('localhost');
    $config['disallowed_remote_addresses'] = array("0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/29", "192.0.2.0/24", "192.88.99.0/24", "192.168.0.0/16", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4",);

so this is vulnerable to dns rebind attack since there's ip resolved in blacklist check, then file_get_contents will resolve again
so you can setup a host for dns rebind, there's plenty of public services like requestrepo.com or rbndr.us
so now we can browse the 403 directory

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
 <head>
  <title>Index of /secret_report</title>
 </head>
 <body>
<h1>Index of /secret_report</h1>
  <table>
   <tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
   <tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/back.gif" alt="[PARENTDIR]"></td><td><a href="/">Parent Directory</a></td><td>&nbsp;</td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[   ]"></td><td><a href="3ac45ca05705d39ed27d7baa8b70ecd560b69902.php">3ac45ca05705d39ed27d7baa8b70ecd560b69902</a></td><td align="right">2020-07-23 12:53  </td><td align="right"> 50 </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[   ]"></td><td><a href="63b4bacc828939706ea2a84822a4505efa73ee3e.php">63b4bacc828939706ea2a84822a4505efa73ee3e.php</a></td><td align="right">2020-07-13 13:24  </td><td align="right"> 14 </td><td>&nbsp;</td></tr>
   <tr><th colspan="5"><hr></th></tr>
</table>
</body></html>

there's directory listing enabled, we can see there's 2 files and their size, if we fetch them both with dns rebind attack from previous step, we still have no flag, but we notice that file 3ac45ca05705d39ed27d7baa8b70ecd560b69902.php has 50 bytes but only 9 bytes are printed, so this means there's more to this file in it's php code

we know that file_get_contents can read files given an absolute or relative path, but the function has a strict check that the URL must have a valid http/s protocol

if ((!empty($url_components['scheme']) && !in_array($url_components['scheme'], array('http', 'https')))) {
        return false;
    }

funny enough, when passing an url like http:/AAAAAAAAAAAAAA/AAAAA it has a correct protocol prepended but file_get_contents will go to http only if its http(s)://, so only http:/AAA will consider it as a directory called http: which doesnt exist, so we got some path traversal here !~
if we fetch the file http:/../../secret_report/3ac45ca05705d39ed27d7baa8b70ecd560b69902.php we will pass all the checks as valid URL and file_get_contents will get us the flag

<?php

//3k{ssrf_bug_f068b29b58ccd0}

?>

secret2

web - lfd - dns rebind - ssrf - 3kctf - php

CONTACT



rekter0 © 2022