1、基础
1.1 常见注入点
应用程序和数据交互的地方:
Authentication(认证页面)
Search Fields (搜索页面)
Post Fields (Post请求)
Get Fields (Get请求)
HTTP Header(HTTP头部)
Cookie
1.2 数据库特性
1.2.1 数据库特定表
-
and exists(select count(*) from msysobjects)
mysysobjects
表为access特有,一般返回权限不足 -
and exists(select count(*) from sysobjects)
sysobjects
表为SqlServer特有,一般返回正常 -
and (select count(*) from information_schema.TABLES)>0
information_schema
为MySQL 5.0 及以上版本特有
1.2.2 注释符
-
Mysql支持
/*
如果
/**/
返回错误,说明不是MySQL数据库 -
Sql Server
– 返回正常,说明是MSSQL数据库或者Oracle
;-- 返回正常,说明是MSSQL;错误,基本就是Access数据库
1.2.3 数据库报错
1.2.4 MySQL 5.0 中information_schema表
- information_schema.schemata 存放所有数据库名,schema_name为数据库名字段
- information_schema.tables 存放所有数据库的表名,table_schema存放数据库名, table_name字段存放表名
- information_schema.columns 存放所有数据库表的所有列名,table_schema 库名, table_name表名,column_name列
1.3 SQL注入靶场
SQL注入在线靶场: http://redtiger.labs.overthewire.org/
DVWA: https://dvwa.co.uk/
sqli-labs源码: https://github.com/Audi-1/sqli-labs
1.4 框架
-
SSH组合是Struts+Spring+Hibernate
基于MVC模式的开发,其中Struts2 对应前台的控制层,
Spring负责实体bean的业务逻辑处理,
Hibernate则是负责数据库的交接以及使用Dao接口来完成操作。
-
SSM组合是Spring-MVC+Spring+MyBatis
标准的MVC模式,使用Spring MVC负责请求的转发和视图管理,
Spring实现业务对象管理,
Mybatis作为数据对象的持久化引擎。
-
SSH vs SSM
SSM和SSH不同主要在MVC实现方式,以及ORM持久化方面不同(Hibernate与Mybatis)。
SSM越来越轻量级配置,将注解开发发挥到极致,且ORM实现更加灵活,SQL优化更简便; SSH较注重配置开发,其中的Hibernate对JDBC的完整封装更面向对象, 对增删改查的数据维护更自动化,但SQL优化方面较弱,且入门门槛稍高。
2、JDBC初探
2.1 JDBC执行对象
执行对象是SQL的执行者。 目前常用的执行对象接口有三种:
Statement
PreparedStatement
CallableStatement
2.2 Statement
Statement 主要用于执行静态SQL语句,即内容固定不变的SQL语句。
Statement 每执行一次都要对传入的SQL语句编译一次,效率较低。
String name = "tom";
String sqlString = "select * from student_table where student_name = '" + name + "'"; Connection conn = open();
若 name 为用户可控参数,当用户传入 ’ or 1 = 1 # 则sqlString为:
select * from student_table where student_name = ' ' or 1 = 1 # '
可以看到程序输出 student_table 表中所有条目:
2.3 PreparedStatement(预编译)
- PreparedStatement是
预编译
参数化查询执行SQL语句的方式。
String sql = "select * from student_table where name = ?";
Connection conn = open();
PreparedStatement pstmt = (PreparedStatement) conn.prepareStatement(sql);
//对占位符进行初始化
pstmt.setString(1, "tom");
pstmt.executeQuery();
看到使用PreparedStatement之后,特殊符号被转义,无法按设想的SQL语句执行了。
2.4 CallableStatement
CallableStatement接口提供了执行存储过程的方法。
在线数据库练习平台:http://sqlfiddle.com/
create procedure sp_query @name varchar(50) as begin
declare @sql nvarchar(500)
set @sql = 'select * from ForgeRock where productName =''' + @name + ''''
exec(@sql)
end;
为@name赋值 ' or 1=1 --
,语句成为 exec sp_query " ' or 1=1 --
";
成功查询到该表中所有数据
注意:`--`后面需要有空格。
在存储过程中进行参数化查询
create procedure sp_query @name varchar(50) as begin
select * from ForgeRock where productName = @name
end;
为@name赋值' or 1=1 -- ,语句成为 exec sp_query "' or 1=1 -- ";
但此时没有返回任何数据,说明对SQL已有防御。
3、Mybatis框架安全
3.1 MyBatis使用
3.1.1 MyBatis了解
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML
或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,
普通老式 Java 对 象)为数据库中的记录。
官方教程: https://mybatis.org/mybatis-3/zh/getting-started.html
3.1.2 MyBatis基本使用–基于xml实现
-
实体类Student
各个参数与数据库中目标表的列名一一对应,包括参数名、参数类型
-
Dao接口文件:
package mybatis.dao;
import mybatis.pojo.Student; public interface StudentDao {
public Student selectStudent(@Param("name")String name);
}
-
Mapper xml SQL语句映射文件
StudentMapper.xml
3.2 MyBatis中注入问题(重点)
3.2.1 动态 SQL
动态 SQL
是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,
在查询之前mybatis 会对其进行动态解析。 Mybatis框架中,接受用户参数有两种方式:
通过${param}方式
通过#{param}方式
#{ }会自动传入值加上单引号, 而${ }不会。
即,“ ${} ”更容易导致存在sql注入
3.2.2 不安全的${ }
后端语句:
select * from student_table where student_name = '${name}'
提交
'or1=1#
最终执行:
select * from student_table where student_name = '' or 1=1 #'
SQL注入防御 --> 使用 " #{} "
3.3 MyBatis中注入场景分析(重点)
3.3.1 like 模糊匹配
-
后端代码是;
select * from student_table where student_name like '%#{name}%'
则传入一个“ ? ”很容易引起程序报错,
因为引号内的?会被当作值处理,所以这里会报错找不到占位符。
-
常见的解决方法
把“ #{} ”换为“ ${} ”,如:
select * from student_table where student_name like '%${name}%'
-
新的问题
但是这样又会容易引起注入,
-
正确的做法(
使用concat方法进行连接
)select * from student_table where student_name like
concat('%', #{name}, '%')
3.3.2 IN语句
-
后端代码
select * from student_table where student_id in (#{id})
提交 id=“1,2” ; 实际执行的语句如下:
select * from student_table where student_id in ('1,2’)
后果就是仅仅会查询 student_id = 1
-
常见的解决方法
把“ #{} ”换为“ ${} ”,
-
新的问题
但是这样又会容易引起注入,
-
正确的解决方法
略微复杂,咱不记录。
3.3.3 order by(group by)语句
凡是字符串但又不能加引号的位置都不能预编译参数化,这种场景不仅order by,还有group by。
-
防御
这种场景下,通常采用白名单,只开放有限集合,使用间接对象引用,
如果传来的参数不在白名单列表中,直接返回错误即可。
3.3.4 总结
Mybatis框架下易产生SQL注入漏洞的场景有:
-
Like 模糊查询:
Select * from student where name like ‘%${name}%’
-
IN 查询:
Select * from news where id in (${id})
-
order by、group by 查询:
select * from studentwhere id order by ${id}
4、Sql注入实战项目审计
inxedu 因酷教育软件v2.0:https://www.inxedu.com/downloadCode
4.1 环境搭建
将下载的源代码,按照源代码的教程先将数据库导入。然后将整个项目直接导入IDEA。
等待IDEA右下角的进度条跑完,继续按照教程将数据库链接的问题配置好,
然后右击项目,选择“重构模块”,等待进度条跑完,
这里我一开始报了一个错,
百度之后发现是jdk版本的问题,直接右击项目的设置,调整jdk版本为1.8
继续按照教程进行配置,
然后项目直接跑起来,
4.2 漏洞审计
根据项目的提示得到以下结论:
看到项目使用mybatis框架(resources下边),则使用“ ${ ”这种包括的sql语句都是可能存在注入的。
直接全局搜索,限制文件为“xml”,然后重点关注一些映射文件(Mapper),如下图标记的:
从上边,随意点进去一个,查看代码。
这里,我们可以先记住表名“ EDU_COURSE_FAVORITES ”。
然后根据这个“deleteCourseFavoritesById”关键词,
去查找对应的dao层定义该接口的代码,这里我们再次限制类型为“ .java ”,任意找到一处点击去看看,
看到接收到参数是 String ids,
然后搜索哪里调用过“ deleteCourseFavoritesById ”方法,
选择第一个结果前跟过去,
然后注意到这个237行的路由“/deleteFaveorite”,
在然后接着看该接口所属类最上边的路由“/webapp”,
即我们访问“ url/webapp/delectFaveorite ”路径,就可以走到上述实现接口的方法。
我们看到该方法,需要一个“id”参数,且对这个传入参数进行了一些为空判断等逻辑。
在不为空之后,就调用了我们最开始的“删除”方法,
我们使用burp抓包,修改路径,构造参数去请求, 、、经过测试,这里无需登陆也可以
后台注入立马变为前台注入。
另外,因为接收参数的方法是“ request.getParameter ”,所以我们使用GET/POST请求都可以。
这个sql语句,我们也可以在下边找到对应的记录。
此时我们也可以去看看表里对应的数据是否被删除。
在表内看到,确实被删除了,
这里有一个小知识点:
当我们在次构造传入语句“ 1 and sleep(2) ”会发现他沉睡了2S,
//这里在实际的测试中,发现传入1S则沉睡19S,我们先假设沉睡的是1S。
但是我们构造语句如下图,第七行的payload,会发现他没有沉睡而直接秒返回,
这是因为拼接了第7行之后,sql语句变为第5行,而“ in ”会优先执行,即“ AAA and sleep(2)# ”
但是我们在表中id=1的已经被删除过,所以“ AAA ”是false,即 sleep(2)不会执行。
当然,此时我们可以不闭合或者将“ and ”改为“ or ”;在或者“ id=存在的参数 ”。
此时,在看下边那个方法,先构造路由,
尝试不同的参数,发现也存在注入,
4.3 小结(审计sql注入的思路)
先寻找疑似存在注入的dao层代码,“ ${ ”
、、筛选“ .xml ”文件
根据dao层的id属性的值“ deleteCourseFavoritesById ”去查找这个方法在哪里定义的
、、筛选“ .java ”文件
然后看看是哪里调用过“ deleteCourseFavoritesById ”
、、直接右击“ 查找用法 ”
然后,根据调用的方法,构建路由进行测试
、、此时可以看看idea的日志,执行的sql语句是否为预想的。