0ctf 2015 quals – forward (web250)

March 30, 2015

At the start we’ve only got an url to our target webserver:

Bildschirmfoto 2015-03-30 um 18.14.33

When we click on “Login” we get a javascript popup which tells us “You Are Not Authorized!”. Then we click on “FLAG”, because that’s what we want. Unfortunately we don’t get a flag yet, but the source code of admin.php is revealed.

    if (isset($_GET['view-source'])) {
    include("./inc.php"); // key & database config

    function err($str){ die("<script>alert(\"$str\");window.location.href='./';</script>"); }

    $nonce = mt_rand();

    extract($_GET); // this is my backdoor :)
    if (empty($_POST['key'])) {

        err("Parameter Missing!");

    if ($_POST['key'] !== $key) {
        err("You Are Not Authorized!");

    $conn = mysql_connect($host, $user, $pass);

    if (!$conn) {
        err("Database Error, Please Contact with GameMaster!");

    $query = isset($_POST['query']) ? bin2hex($_POST['query']) : "SELECT flag FROM forward.flag";
    $res = mysql_query($query);
    if (FALSE == $res) {
        err("Database Error, Please Contact with GameMaster!");

    $row = mysql_fetch_array($res);

    if ($debug) {
        echo "HOST:\t{$host}<br/>";
        echo "USER:\t{$user}<br/>";

    echo "<del>FLAG:\t0ctf{</del>" . sha1($nonce . md5($row['flag'])) . "<del>}</del><br/>"; // not real flag


Now we inspect the code and find the comment “this is my backdoor :)”. After looking up what extract($_GET) does, it was clear that we can (re)define our own variables and values here using GET parameters. We’re also able to overwrite values which were set earlier. Inserting our own sql query however is not possible. If we alter the query in any way it is replaced by its hex-representation. Since it should be impossible to generate a valid query using the “bin2hex” php function, this is also a dead end.

Now we asked ourselves why it is possible to display the IP and the username of the mysql connection. If we change the host-parameter for the mysql_conenct() function maybe we can get mysql credentials. It really seemed like this was the case, because the remote host had an open mysql port. It turns out we can recover the username, but it was impossible to get the password (Checking the protocol specification would have helped).

So what do we have? We can make mysql user connect (with the correct password) to a server of our choice but not recover the password and also can’t directly alter the query. This was the point when we finally came up with the right idea: Use a proxy which forwards the mysql connection back to the original server.

php with our $host and unaltered user/password -> mysql-proxy (WE) -> remote host -> “select flag from forward.flag” -> [plaintext flag] -> mysql-proxy -> php

Because we didn’t think the flag is really in “forward.flag” we set up “mysql-proxy” to alter the query if we need to. After changing the host value via the get parameter to our server ip, we got a result from the php script, which means the connection was successful by using our mysql-proxy. To get the result of the query we just had to start tcpdump and capture all traffic from and to the mysql-proxy.

After inspecting the dump in wireshark we found this string “0ctf{w3ll_d0ne_guY}”

This is (roughly) how everything looked like in the end (The Firefox addon “HttpRequester” on the right to alter http requests) :


Leave a Reply