Java代码审计—SQL注入篇

渗透技巧 8个月前 admin
193 0 0

一、典型代码框架简述

Spring是一个开源的Java框架,提供了一系列组件和工具,用于构建企业级应用程序。

审计一个基于Spring的项目,它的目录看起来是这样的

Java代码审计—SQL注入篇

假设我们有一个简单的订单管理系统,包含以下几个层级:

model:数据库实体层,也被称为entity层,pojo层。

一般数据库一张表对应一个实体类,类属性同表字段一一对应。

dao:数据持久层,也被称为mapper层。

访问数据库,向数据库发送sql语句,完成数据的增删改查任务。

Java代码审计—SQL注入篇

service:业务逻辑层。

调用dao层接口,接收dao层返回的数据,完成项目的基本功能设计。

Java代码审计—SQL注入篇

controller:控制层。

功能为请求和响应控制。负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端。

Java代码审计—SQL注入篇

在上述示例中,通过使用Spring的依赖注入(@Autowired 注解)将不同层级的组件连接在一起。控制层的 OrderController 依赖于业务逻辑层的 OrderService,而业务逻辑层的 OrderService 又依赖于数据访问层的 OrderRepository

当用户发起对订单详情的请求时,OrderController getOrderDetails 方法被调用。它使用 OrderService 来获取订单对象。

OrderService 中的 getOrderById 方法使用 OrderRepository 来执行数据库查询,并返回相应的订单对象。

总体而言,Spring框架通过依赖注入的方式,实现了不同层级之间的解耦和灵活的组件替换。每个层级只需要关注自己的责任,而不需要关心具体的依赖实现。这种解耦和抽象使得程序更易于开发、测试和维护,并支持可扩展性和可重用性。

接下来,我们对sql注入的漏洞代码进行审计。

二、JDBC漏洞

JDBCJava DataBase Connectivityjava数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。

实现使用java代码操作数据库

JDBC存在两种方法执行SQL语句,分别为PreparedStatementStatementPreparedStatement会对SQL语句进行预编译,Statement会直接拼接sql语句造成SQL注入漏洞

下图例子为Vuln code,指“敏感代码”,含有漏洞

Java代码审计—SQL注入篇

构建SQL查询语句时,没有对输入的username参数进行任何检查或者过滤,直接将其拼接到SQL语句中。

攻击者可以通过在username中插入恶意的SQL代码来修改原始的查询意图,甚至执行其他未授权的数据库操作。例如,攻击者可以使用‘ OR ‘1’=’1′–作为username的值,这将导致查询语句变为:

select * from users where username = ” OR ‘1’=’1′–‘

这句sql语句的含义是:

SELECT *: 选择所有列,意味着返回users表中符合条件的所有行的所有列数据。

FROM users: 指定要查询的表是users

WHERE username = ” OR ‘1’=’1′–‘: 这是查询的条件部分。

username = ”:检查username列是否等于空字符串。这似乎是要查找username为空的记录。

OR ‘1’=’1′:这部分是一个逻辑表达式,始终返回true。由于‘1’‘1’相等,所以这个表达式永远为真。因此,这个条件可以绕过前一个条件username = ”

–‘:这是一个注释符号,表示在这个符号后面的内容将被视为注释,不会被数据库处理。

综上所述,这个查询的意图似乎是为了获取users表中username为空或者绕过其他条件而返回所有用户的信息。然而,这个查询存在SQL注入的风险。使用‘1’=’1′作为永真条件,可以绕过原有的条件并获取所有用户的信息。

要修复这个漏洞,应该使用参数化查询或预编译语句,而不是直接将用户输入拼接到SQL语句中。具体实现方式取决于编程语言和使用的数据库管理系统

下图例子为安全代码,使用PreparedStatement类的setString方法

Java代码审计—SQL注入篇

Java代码审计—SQL注入篇

PreparedStatement.setString()将指定的参数设置为给定的Java字节数组。驱动程序在将其发送到数据库时将其转换为SQL VARBINARYLONGVARBINARYSQL的数据类型),实现PrepareStatementSQL语句进行预编译预编译的好处不仅在于在一定程度上防止了sql注入,还减少了sql语句的编译次数,提高了性能,其原理是先去编译sql语句,无论最后输入为何,预编译的语句只是作为字符串来执行,而SQL注入只对编译过程有破坏作用,执行阶段只是把输入串作为数据处理,不需要再对SQL语句进行解析,因此解决了注入问题。

PrepareStatement防御预编译的写法是使用?作为占位符然后将SQL语句进行预编译,由于“?”作为占位符已经告诉数据库整个SQL语句的结构,即?处传入的是参数,而不会是sql语句,所以即使攻击者传入sql语句也不会被数据库解析。传入的payload已进行转义安全。

JDBC易产生漏洞点

1.未使用占位符

PreparedStatement只有在使用“?”作为占位符才能预防sql注入,直接拼接仍会存在sql注入漏洞

2.使用in语句

删除语句中可能会存在此类语句,由于无法确定delIds含有对象个数而直接拼接sql语句,造成sql注入。

String sql = “delete from users where id in(“+delIds+”); //存在sql注入

解决方法为遍历传入的对象个数,使用“?”占位符。

3.使用like语句进行模糊查询

LIKE 运算符用于在 WHERE 子句中进行模糊匹配。它可以根据模式匹配字符串,并返回匹配的结果。% 是通配符,表示任意字符的零个或多个实例。所以 ‘%keyword%’ 可以匹配包含 keyword 的任意位置的字符串。

String sql = “select * from users where password like ‘%” + con + “%'”; //存在sql注入

4.%_

预编译是不能处理%的, 所以需要手动过滤%,否则会造成慢查询据库性能下降,影响系统的响应时间

5.Order byfrom等无法预编译

当使用order by语句时是无法使用预编译的,原因是order by子句后面需要加字段名或者字段位置,而字段名是不能带引号的,否则就会被认为是一个字符串而不是字段名,然而使用PreapareStatement将会强制给参数加上,所以,在使用order by语句时就必须得使用拼接的Statement,所以就会造成SQL注入,需要进行手动过滤,否则存在sql注入。

String sql = “Select * from news where title =?” + “order by ‘” + time + “‘ asc”

.Mybatis框架下的sql注入

Mybatis是一款半自动的ORM对象关系映射Java对象和数据库的关系模型之间建立一种对应关系持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,数据库无关性较低

Mybatis使用parameterTypesql语句传参,在sql引用传参可以使用#{Parameter}${Parameter}两种方式

1 .${Parameter}方式

${Parameter}采用拼接的方式构造SQL语句,在对用户输入过滤不严格的前提下,存在sql注入漏洞

假如在controller层使用

userMapper.findByUserNameVuln01(username);进行查表

Java代码审计—SQL注入篇

调用mapper层的Usermapper.java 的接口

Java代码审计—SQL注入篇

2.#{Parameter}方式

#{Parameter}采用预编译的方式构造SQL语句,避免了SQL注入的产生

假如在controller层使用

 userMapper.findByUserName(username);进行查表

Java代码审计—SQL注入篇

调用mapper层的Usermapper.java 的接口

Java代码审计—SQL注入篇

MyBatis易产生SQL注入的三种情况

1.使用like语句进行模糊查询

模糊查询使用#{}程序会报错,如果#号改成了$,java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。

Java代码审计—SQL注入篇

Java代码审计—SQL注入篇

正确写法如下:

Java代码审计—SQL注入篇

其他正确写法:

select * from users where username like concat(‘%’,#{username},’%’)oracle:

select * from users where username like ‘%’||#{username}||’%’sqlserver:

select * from users where username like ‘%’+#{username}+’%’

2.使用in语句

使用in语句时直接使用#{}会报错,如果使用${}直接拼接,造成sql注入

Java代码审计—SQL注入篇

Java代码审计—SQL注入篇

正确写法如下:

使用foreach,而不是将#替换为$

id in<foreach collection=”ids” item=”item” open=”(“separatosr=”,” close=”)”>#{ids} </foreach>

3.使用order by 语句

JDBC同理,使用#{}方式传参会导致order by语句失效,所以使用order by语句的时候还是需要做好过滤

Java代码审计—SQL注入篇

原文始发于微信公众号(瑞不可当):Java代码审计—SQL注入篇

版权声明:admin 发表于 2023年9月1日 上午10:27。
转载请注明:Java代码审计—SQL注入篇 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...