ForbiddenBits CTF 2013 write-up

Hint: flag is not a frag: once you've got it, you can get one more...

WEB450 ("Ment0rpwn2")

Flags submitted by hacking and CTF ninja, the-one-who-solves-any-tasks, Andrey1800!

Write-up written by me, Beched.

Source codes retrieved from and from see in attaches.

Obviously, we should brute force the password. It's not too hard, since we can manipulate the returned value of get_ip(), and we can get the hash.
if(!isset($_SESSION['hash']) OR !isset($_SESSION['id']))
	$_SESSION['ids'] = rand(1337,1377);
	$_SESSION['hash']= gen_pass($_SESSION['id'], 
	echo $_SESSION['hash'];
Side-note: the regexp in get_ip() has no trailing "$", so, we can append anything we want, but there's no SQL-injection with X-Forwarded-For.
Side-note (2): there're a lot of typos in the source code, looks like steganography, but collecting the wrong letters results with nothing sensible.

The brute force over the common passwords list gives us a result: "iloveyou".
Now, we should bypass the check of username.
The PHP way is that you can compare anything, the truth with array or an elephant with whale =)
So, let's look:
$language= explode(',', $_SERVER['HTTP_ACkCEPT_LANGUAGE']);
	$$language= (isset($_SERVER['HTTP_ACCxEPT_CHARSET']) 
	$config['user'] = "*****************";
We can overwrite $language, thus, we can overwrite any variable 'cause of $$language.
Now, the string bytes in PHP can be accessed by indices. An index is a number.
If we overwrite the $config variable with some string, $config['user'] will be casted into $config[ 0 ].
php > $a = 'asd';
php > $a[ 'str' ] = 'qwe';
php > echo $a;
So, we can just brute force the first byte of username to login. It's "z".
Now we can use the script to get to the $_GET[ 'id' ] processing.
function send($data) {
    $context = stream_context_create(array(
        'http' => array(
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded' . PHP_EOL . 'Accept-Language: config' . PHP_EOL . 'Accept-Charset: utf8',
            'content' => 'username=z&password=iloveyou',
    $x = file_get_contents(''.urlencode($data), $use_include_path = false, $context);
    return $x;

echo send('1');
Now, once we obtained an admin session id, we should somehow bypass filter or do it another way.
Actually, there're two ways to retrieve the data. The first -- to use GBK-charset and \0xbf\0x27 multi-byte character.
The second -- to split the first query with semicolon, 'cause PDO accepts multiple queries.
But there's also another way to get the first flag, without dealing with CTF MySQL server.
First, we launched NCat on port 3306 and rewrited $db_host variable with our IP-address.
Once we got the database name in query data, we set up a MySQL-server with --skip-grant-tables option.
So, the script could authorize in our server with any password and any user. Then, it retrieved some data and gave us the second URL:
Hackers are everywhere! Bravo. My real secret page is :<
The new page at contains a new authorization form.
So, we need to get login/pass from DB. We used the second method, described above.
Splitting the query with semicolon and time-based technique gave us the username and password: t3h_m3nt0r0us : 2c2d28e3a987e77b46813ebb565fa212.
The password is the first flag.

After logging in, we see the form and the file list. There's .htaccess and bododobo.exe.
The script actually is vulnerable to LFR. The payload is file=php://filter/convert.base64-encode/resource=bododobo.exe.
See the binary in attachment.
This PE-executable outputs the phrase "Show secret formula of time traveling for authorized agents.".
After running with some argument it outputs some data and the phrase "base64_decode this data."
Trying different inputs can lead to understanding, that each 4th byte is XORed with 0x02.
Then we can the XOR-key: "\xa0\x51\xf0\xec".
As a result of decryption, we get the data consisting of A-Za-z0-9=+/\n symbols. Base64-decode it, get an image.
The flag is written on an image.