Joining 2amResearch for the SEC-T CTF 2017 challenges here is a post from @akrotos solving the Naughty Ads web challenge.

Top of the challenge rabbit hole

Let’s head on over to the challenge page and load up Burp Suite. With a peak at the page’s html we see the the page contains an image with regions (wow, image maps were so 1995) with links to all the *ahem* ads, and a link to an admin page. Clicking on an ad took you to a simple page that has some suggestive description and a phone number. Unfortunately, the admin page doesn’t use the the Equifax default username and password of admin:admin. At this point it’s pretty obvious that we need to figure out a way of getting Agent Gill’s phone number into an ad by probably using the admin page.

Checking the robots.txt gives us the following:

User-agent: *
Disallow: /admin
Disallow: /*.phps

The admin page we already know about but using the *.phps path could be useful. Using admin.phps gives a 404 but index.phps was available, so let’s take a look.

<?php
require_once 'lib.php';
header('X-XSS-Protection: 0');
$cols = array(
"e8c4-437b-9476",
"849e-416e-acf7",
"7f9d-470f-8698",
"c8bb-4695-93f7",
"5fbc-4729-8821",
"3ad3-46c3-b975",
"f44f-4cc9-a5e0",
"0c3f-42c8-a0ae"
);

if(isset($_REQUEST['id'])){
if(preg_match("/'(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['id'])){
die("Attack detected!!!");
}
$ad = get_ad($_GET['id']);
?>
<HTML>
<HEAD>
<TITLE>NAUGHTY ADS ©1994</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<CENTER>
<?php echo $ad['description'] ?><br />
<a href="/">Home</a>
</CENTER>
</BODY>
</HTML>
<?php
die;
}

?>
<HTML>
<HEAD>
<TITLE>NAUGHTY ADS ©1994</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<CENTER>
<img class="ads" src="middle.png" width="800" height="600" usemap="#planetmap">
<map name="planetmap">
<area shape="rect" coords="287,93,523,261" href="?id=<?php echo array_pop($cols); ?>" alt="BDSM hookup">
<area shape="rect" coords="542,93,774,261" href="?id=<?php echo array_pop($cols); ?>" alt="Fat fetish">

<area shape="rect" coords="34,282,269,449" href="?id=<?php echo array_pop($cols); ?>" alt="Dirty mistress">
<area shape="rect" coords="292,282,521,449" href="?id=<?php echo array_pop($cols); ?>" alt="Femdom one night stand">
<area shape="rect" coords="545,282,777,449" href="?id=<?php echo array_pop($cols); ?>" alt="Waterboarding extasy">

<area shape="rect" coords="33,468,266,595" href="?id=<?php echo array_pop($cols); ?>" alt="Kinky nightmare">
<area shape="rect" coords="277,456,534,598" href="?id=<?php echo array_pop($cols); ?>" alt="Food fetish">
<area shape="rect" coords="547,466,780,599" href="?id=<?php echo array_pop($cols); ?>" alt="Whip experience">

<area shape="rect" coords="595,23,619,57" href="/admin" alt="Admin">
</map>
</CENTER>
</BODY>
</HTML>

Combing through the source give an interesting bit of PHP with a bit of Regex used to filter SQL injection attacks.

if(isset($_REQUEST['id'])){
    if(preg_match("/'(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['id'])){
        die("Attack detected!!!");
    }
$ad = get_ad($_GET['id']);

This bit had us stumped for a while, but with help of an old hardcore PHP developer he pointed out there is a subtle difference between $_REQUEST[] and $_GET[]. Thank you Philip Fulcher. We can exploit this difference to bypass the regex by creating two ‘id’ parameters, one in the URL and one in the request body, and change the HTTP GET to a POST. $_REQUEST will take the id that’s in the request body instead of the URL, effectively bypassing the regex filter. Example request here

The application is expecting a single value returned from the query or else it thrown an HTTP 500 error. Setting the malicious id parameter to the string below shows how this works.

id=0000-0000-0000' UNION SELECT USER() %23

‘%23’ is ‘#’ URL Encoded. This is needed or else the server returns a 400. Not sure why, but let’s just roll with it.

With a working Blind SQL injection vuln we can probe the database for more info. Guessing the backend is a MySQL database, because PHP devs don’t know how to use anything else, we can use GROUP_CONCAT() to return the list of tables in a single comma separated value. If we filter out the system tables in the information schema by setting id to the below string, we can see there are two user tables in the database: ads, and login.

id=0000-0000-0000' UNION SELECT GROUP_CONCAT(table_name) FROM INFORMATION_SCHEMA.tables %23

By concatenating all the columns in the login table with a select gives us a very nice reward.

id=0000-0000-0000' UNION SELECT CONCAT(id, ',', username,',', password) FROM login %23

1,webmasterofdoom3755,5ebe2294ecd0e0f08eab7690d2a6ee69


5ebe2294ecd0e0f08eab7690d2a6ee69 is a MD5 hash of the string ‘secret’. Plugging in the username and password into the login prompts gets us into the admin page. We see a simple form to plug in a phone number, description, and image to upload. Taking the phone number from the start of the challenge and submitting the form gives ups the flag.

Flag: SECT{~tr4nsv3stiT3s_w3lc0me_t00~}

Leave a Reply

Your email address will not be published. Required fields are marked *