[强网杯 2019]随便注
打开靶机看到如下一个输入框,根据题目标题可以看出这题应该是一道sql注入的题目
在参数后面加单引号和双引号验证注入点,可以发现单引号引发数据库报错,双引号没有报错,存在注入点,猜测应该是单引号闭合
做进一步测试,执行以下命令,返回了不同的结果,由此断定是单引号闭合方式
1' and '1' = '1 -- 正常回显
1' and '1' = '2 -- 不回显
接下来使用order by
语句判断字段数,本来想使用HackBar插件来构造参数,但是发现每一次参数查询都需要将输入的参数进行url编码后才能正常执行,因此还是在输入框中进行注入吧;测试以下sql语句,根据不同的返回结果可以判断字段数为2
1' order by 1 # -- 正常回显
1' order by 2 # -- 正常回显
1' order by 3 # -- 报错
SQL中order by语句即对结果集中指定的列进行排序,当指定的列不存在时会报错
除了order by
以外,还可以使用union select
语句来判断字段数,测试以下sql语句,发现select
语句被过滤了,好吧,不过确实是可以使用union select
语句来判断字段数的
1' union select 1,2 #
union要求前后两个语句的结果具有相同的列数,不相同时会报错
在知道字段数为2后,接下来要判断回显位置,坏了,前面知道select
被过滤了,从过滤返回的过滤代码来看,preg_match
函数使用了正则表达式来匹配inject
参数值中是否存在select
、update
、delete
、drop
、insert
、where
或.
,并且/i
表示忽视大小写
这里要绕过select的过滤,基于上面的分析结果,大小写绕过是无效的,我还不知道有什么其他绕过方法,直接面向百度做题;尝试了双写、url编码、base64编码、hex编码、内联注释,好像都不行;看下大佬的writeup吧,学习一下绕过技巧
发现需要使用堆叠注入的方式来绕过
堆叠注入:即一堆sql语句或者说多条sql语句一起执行,每一条语句结尾以分号
;
结尾
原理:在sql中分号;
表示一条sql语句的结束,结束一个sql语句后继续构造吓一跳语句会一起执行;union联合注入执行的语句类型是有限的
局限性:堆叠注入并不是在任何环境下都可以执行(比如oracle不能使用堆叠注入),堆叠注入的第二个语句产生的错误或者结果只能被忽略,并且在使用堆叠注入之前需要知道一些数据库相关信息
执行以下命令尝试爆数据库,发现存在六个库如下
1'; show databases; #
执行以下命令尝试爆表,发现存在两个表如下
1'; show tables; #
尝试查看test表内容并没有发现任何东西,则执行以下命令查看1919810931114514表的内容,发现存在flag字符
1'; show columns from `1919810931114514`; #
注意:表名为数字时,要用反引号包起来查询
根据大佬的writeup,存在四种解题思路
1. 修改表名和字段名绕过
该方法的原理是利用程序中已存在的sql语法来帮我们进行查询,因为在初始靶机界面可以看到实际上是回显了两个参数,1就是id值,hahahah就是data值,相当于执行了sql语句select id, data from words where id=1
,的回显结果,为什么一定是在words表里面进行查询呢,因为191那个表只有flag一个字段;
在明白环境中已存在的sql查询语句的具体形式后,我们的目标是使其回显出flag的值,也就是使sql查询语句变成select id, data from words where id=1
,但是这里的data是flag的值,words是表191,即要修改表191的表名为words,给新的words表新建id列,把新的words表中的flag列名改成data,这样就实现了利用环境中已存在的sql查询语句来回显flag的内容,构造sql语句如下
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); #
其中primary key表示这个属性是主键、auto_increment表示这个值是自增的
从而在初始靶场环境下键入1便可查询到flag{ffedcbbf-2200-4d48-be61-a7885db7189a}
2. 预编译+concat函数绕过
构造sql语句如下绕过select的过滤
1';PREPARE hacker from concat('s','elect', ' * from `1919810931114514` ');EXECUTE hacker;#
其中PREPARE hacker FROM ...
表示将from后的sql语句进行预编译并赋给hacker,而EXECUTE
则用来执行预编译语句,concat
函数用于将多个字符串连接成一个字符串
3. 预编译+16进制编码绕过
构造sql语句如下绕过select的过滤
1';PREPARE hacker from 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;EXECUTE hacker;#
其中预编译语句PREPARE ... FROM ...
会进行编码转换,0x73656c656374202a2066726f6d20603139313938313039333131313435313460是select * from
1919810931114514
sql语句的16进制编码
也可以通过先定义一个变量,再对变量进行预编译再执行,即构造sql语句如下
1';Set @jia = 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE hacker from @jia;EXECUTE hacker;#
4. Mysql语法特性绕过
利用Mysql特有的handle语句,其可以逐行浏览某个表中的数据,构造sql语句如下
1';HANDLER `1919810931114514` OPEN;HANDLER `1919810931114514` READ FIRST;HANDLER `1919810931114514` CLOSE;
其中第二个语句即打开表,第三个语句即查看表第一行数据,第四个语句即关闭表
总结:
- 堆叠注入的原理和使用
- show语句也可以爆库、爆表、爆字段
- 可以利用环境中已存在的sql语法来实现想要的sql语句查询,通过使用rename和alter来修改表名和字段名,但是前提是需要知道环境中已存在的sql语法的具体形式
- 预编译相当于定义一个语句相同,参数不同的sql语句模板,可以通过预编译的方式来绕过特定字符的过滤,此外预编译还会进行编码转换,因此可以对预编译的内容进行编码绕过
- handle语句是Mysql特有的,可以用来逐行浏览某个表中的数据,利用方法如下
HENDLER table_name OPEN; -- 打开表
HENDLER table_name next; -- 逐行浏览数据
HENDLER table_name CLOSE; -- 关闭表
[SUCTF 2019]EasySQL
从题目名称看该题应该是一道sql注入的题目,打开靶机可以看到存在一个输入框
首先判断注入点,输入单引号和双引号,发现输入1有显示信息但没用,输入单引号没说nonono,输入双引号显示nonono,也就是说,页面没有回显信息,也没有回显报错信息,无法使用联合查询注入和报错注入,并且是以POST方式传输数据
接下来要判断闭合方式,但是对回显还是不太明白原理,不清楚我的命令是执行失败sql报错,还是某些字符被过滤,没能成功判断闭合方式,使用sqlmap尝试一下,执行以下命令进行注入探测
sqlmap -u http://18267643-2f05-4d4e-9778-56f7ca63c497.node4.buuoj.cn:81/ --data="query=1" --technique B --level 3 --risk 3
结果sqlmap也跑不出东西,不知道是我的sqlmap没有使用正确还是其他的原因,还是看一下大佬writeup学习一下注入手法吧;
原来显示nonono意思是过滤了字符,那么就可以使用burp suite来fuzz看下过滤了什么关键字,使用这个字典,将抓到的包sent to intruder,在intruder模块设置options,设置攻击模式为sniper狙击手模式,进行单字典遍历
Burp Suite的Intruder爆破模式
- sniper狙击手模式:单字典,按顺序一个一个参数依次遍历
- battering ram攻城锤模式:单字典,每个参数同时遍历同一个字典
- pitchfork草叉模式:多字典,多个参数同时进行遍历,一个选择字典1,另一个选择字典2
- cluster bomb集束炸弹模式:多字典,类似嵌套两个for循环遍历
设置payloads,使用字典进行爆破,进程之类的保持默认,开始爆破,可以看到站点还设置了防爆破,到49个请求时开始不响应(后来发现可以通过设置线程为1,可以极大限度减少不响应的情况)
根据length变量的值可以判断哪些变量被过滤哪些没有被过滤,length为507是被过滤的,length为500是没有被过滤的,可以发现分号;是没有被过滤的,因此可以使用堆叠注入
执行以下命令使用堆叠注入爆库报表
1; show databases; # -- 爆库
1; show tables; # -- 爆表
1; show columns from Flag; # -- 爆列,但是被过滤
发现存在Flag表
但是要从Flag表中爆数据时发现过滤了Flag关键字
接下来要再做可以有两种解题方法
1. 猜测源码解法一
根据后端能够做到数字回显字母不回显也没有显示字母被过滤,即query参数的值如果是非数字则无法正确查询得到数据回显,看了很多writeup都说根据这个回显不同可以判断后端语句大概是以下这种形式
select $_POST['query'] || flag from flag
但是很少有说为什么会想到后端语句就是这种形式,可能这就是大佬和新手的区别吧,实际上要能够想到这样的后端语句,需要对||
语句的短路原理有一定的了解,此外要知道mysql中select 1 from xxx
语句和select a from xxx
语句的区别,前者是在xxx表中临时加一列,列名为1,且这一列的所有值都为1;后者并不会增加任何列,它只是从表名为xxx的表中选择已有的a列,并将该列的值返回给结果集。那么输入数字1时,由于非0数字恒真从而造成||
语句短路,后端语句会变成这样的形式select 1 from flag
;而输入字母时,字母也是恒真从而造成||
语句短路,后端语句会变成这样的形式select a from flag
;由前面对这两个语句的区别可以知道输入数字时会回显,输入字母时不会回显(flag表中没有列名为特定字母的列),因而会想到这种形式的后端语句(而且从前面堆叠发现的Flag表可以知道一定是from Flag)
知道了后端语句的构造就可以来构造payload回显flag了,构造payload为*,1
,直接可以回显flag{7443c7d0-c5d8-4b24-aa4f-837f71349a4f},原因在于输入payload后后端语句会变成select *,1 || flag from flag
,其中||
语句实际上会短路,则变成select *,1 from flag
,从而直接把flag显示出来
2. 猜测源码解法二
解法一种实际上是将||
语句作为逻辑或来利用,解法二也是在猜测出后端源码的基础上,通过修改sql_mode
为PIPES_AS_CONCAT
,从而||
语句会变成字符串连接符,相当于concat
函数,从而可以构造payload为1;set sql_mode=PIPES_AS_CONCAT;select 1
,输入后后端语句会变成select 1; set sql_mode=PIPES_AS_CONCAT; select 1 || flag from flag
,也即核心代码是select 1 from flag
和select flag from flag
,从而可以得到flag{7443c7d0-c5d8-4b24-aa4f-837f71349a4f}(PS:分号后面不要加空格,不然会显示Too long)
总结:
- 可以使用burp进行fuzz测试看过滤了哪些字符,方便做进一步注入尝试,但是前提是网站对重放攻击不响应,或者要设置较低的线程数
- 一道题不管会做不会做都要多看几个题解,学习别人的思路和方法
select 1 from xxx
语句和select a from xxx
语句的区别,前者是在xxx表中临时加一列,列名为1,且这一列的所有值都为1;后者并不会增加任何列,它只是从表名为xxx的表中选择已有的a列,并将该列的值返回给结果集- MySQL中默认没有设置PIPES_AS_CONCAT ,此时
||
语句表示逻辑或,通过sql_mode=PIPES_AS_CONCAT 设置后,此时||
语句相当于字符串连接符 ||
语句的短路特性,非0数字和字母都为真(不存在0||flag
这个语句的结果为flag的情况,||
语句结果只会为真1假0)
关于sqlmap的使用
支持类型:布尔盲注、时间盲注、报错注入、联合查询注入、堆叠注入
作用:
- 判断可注入的参数
- 判断可以用哪种sql注入技术进行注入
- 识别数据库
- 根据用户选择,读取哪些数据
语法:sqlmap [参数] [参数] …
常见参数:
# 获取目标方式
-u -- 指定目标url
-m -- 指定目标url文件
-l -- 把burpsuite日志直接倒出来给sqlmap检测
-r -- 从一个文本中获取http请求,https时配合--force-ssl参数使用
-g -- 测试注入google的搜索结果中的get参数
# http数据
--data -- 指定数据以POST方式提交
--cookie -- 指定cookie,在需要登录时需要指定,level参数2及以上才会尝试cookie参数注入
--param-del -- 指定字符分割测试参数
--user-agent -- 指定user-agent头,level参数3及以上才会尝试user-agent参数注入
--referer -- 指定referer头,可伪造,level参数3及以上才会尝试user-agent参数注入
--headers -- 增加额外的http头
# 探测
--level -- 从1-5,影响测试的注入点
# 风险等级
--risk -- 从1-5,影响测试语句
# 注入技术
--technique -- 指定注入技术,B布尔注入、T时间延迟注入、E报错注入、U联合查询注入、S堆叠注入
# 列数据
--dbs -- 列出所有数据库名
--current-db -- 列出当前数据库名
-D dvwa -tables -- 列出dvwa数据库的所有表
-D dvwa -T users –columns -- 列出dvwa数据库users表的所有列
-D dvwa -T users -C “user,password” –dump -- 列出dvwa数据库users表user、password列的所有内容
-b -- 返回数据库版本号
--current-user -- 返回用户名
--users -- 返回数据库管理用户
--passwords -- 列出并破解数据库用户的hash
--privileges -- 列出数据库管理员权限
--roles -- 列出数据库管理员角色
# 更多详见参考文章5
参考文章
- SQL注入针对关键字过滤的绕过技巧. zulk个人博客. Available at here. Accessed: 17 July 2023.
- 堆叠注入详解. 博客园. Available at here. Accessed: 18 July 2023.
- [强网杯 2019]随便注 1【SQL注入】四种解法. 知乎. Available at here. Accessed: 20 July 2023.
- BUUCTF[强网杯 2019]随便注 的三种解法. 优快云. Available at here. Accessed: 20 July 2023.
- sqlmap 中文文档用户手册. 文江博客. Available at here. Accessed: 20 July 2023.
- [Burp Suite完整教程] Intruder Attack type&Payloads – 拥有上千种姿态的攻击模式. 优快云. Available at here. Accessed: 22 July 2023.
- sqli注入fuzz字典—waf fuzz测试,ctf. 优快云. Available at here. Accessed: 22 July 2023.
- [SUCTF 2019]EasySQL 1 Writeup(超级详细). 优快云. Available at here. Accessed: 22 July 2023.
- BUUCTF:[SUCTF 2019]EasySQL. 优快云. Available at here. Accessed: 22 July 2023.
- [SUCTF 2019]EasySQL1. 优快云. Available at here. Accessed: 22 July 2023.