跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

MyBatis Plus SQL注入防御机制浅析(一)

精选回复

发布于

## 1\\.问题背景

这篇文章源于一个生产问题。我们在生产环境的用户信息表的create\\_by字段中看到用户端的输入,并且没有过滤特殊字符(注意符号“+”的存在),我们怀疑它可能成为SQL注入点。1704792444_659d117caa9ea92b55206.png!small

开发老大汇报,使用MyBatis-Plus来插入/更新该列的数据。但什么是MyBatis-Plus?以下引用自官方介绍: **什么是MyBatis-Plus?** MyBatis-Plus是MyBatis的一个强大的增强工具包,用于简化开发。这个工具包为MyBatis提供了一些高效、有用、开箱即用的功能,使用它可以有效节省你的开发时间。

那么,为MyBatis提供便捷实现的MyBatis-Plus是否可以在方便开发者的同时引入SQL注入呢?本文将从这个生产示例中回答以下问题:

- MyBatis Plus如何防御SQL注入;

## 2\\.测试环境准备

为了不影响生产数据,使用官方提供的mybatis-plus-sample-crud快速搭建本地测试环境。

mysql版本:8.0.35

mybatis-plus-spring-boot3-starter 版本:3.5.4

## 3\\.问题分析

### **1) 确认生产数据来源**

根据表的“create\\_by”字段搜索源码发现使用了MyBatis Plus注解,它是基于iBatis实现的(具体参见第4节)。该字段来自用户传入的请求参数,没有经过任何过滤。

MemberInfo类定义了数据库字段create\\_by对应的属性:1704792586_659d120a5a41a4c1be3ca.png!small

查看参考。 InfoMessagesSAOImpl.java不涉及数据库操作。注意MemberServiceImpl.java的214行和1910行:1704809620_659d5494ad0557281b3ae.png!small?1704809631228

mybatis-plus的insert接口分别在220/1915使用;1704809641_659d54a9d3c17ffe64c2e.png!small?1704809652457

1704792623_659d122fd98846b82833b.png!small

插入接口来自mybatisplus

### ** 1704809667_659d54c3923257bf5a3f5.png!small

**

### **2) 测试环境重现**

由于生产中使用的是insert方式,所以本地测试也使用insert,观察是否有注入。

测试数据表如下:1704792727_659d1297ab627191cc44a.png!small?1704792726378

接下来,直接在mysql控制台中执行包含注入payload的语句:

````

插入sys_user(id,姓名,年龄,电子邮件)值(124,'Jasdf',42,'[email protected]');/**/DROP/**/TABLE/**/sys_user;/**/--#',25,'[email protected]');

````

可以发现执行恶意语句并删除sys\\_user表:1704792732_659d129c6e523050ff0be.png!small

重建表,将payload放入参数“name”中,观察MyBatisPlus如何处理恶意payload:

````

Jasdf',42,'[email protected]');/**/DROP/**/TABLE/**/sys_user;/**/--#

``1704792889_659d1339e796207ea728b.png!small?1704792888642

在Application.xml中添加配置并打印MyBatis-Plus构造的SQL语句,方便观察

````

# MyBatis-Plus 配置

mybatis-plus:

配置:

log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #启用sql日志

````

注意,经过MyBatis-Plus处理后,payload仍然是String类型:1704792944_659d13705082dd4e0da1f.png!small?1704792943089

Jasdf',42,'[email protected]');/\\*\\*/DROP/\\*\\*/TABLE/\\*\\*/sys\\_user;/\\*\\*/--# **(String)**

查询数据:1704792954_659d137ad4e4670f00ebf.png!small?1704792953349

Payload以完整的String形式插入到数据库中,说明注入不成功。

## 4\\.防御方法分析

在第3节中,我们的注射不成功。下面通过调试来展示mybatis-plus对攻击者的payload做了什么。

在SqlSourceBuilder.java 中

在ParameterMappingTokenHandler 处理程序处放置一个断点。可以看到原来的语句首先被解析为模板+数据,其中模板为:

\\`INSERT INTO sys\\_user ( id, name,age, email ) VALUES ( #{id}, #{name}, #{age}, #{email} )\\` 1704792975_659d138f71fc58de591ae.png!small?1704792974597

熟悉mybatis的同学都知道,相比于${value}不参与预编译的形式,#{value}形式的变量最终会调用preparedStatement来实现预编译。对于预编译的SQL语句,MySQL会使用开发者预先确定的参数类型构造SQL语句。换句话说,有效负载不会被理解为新的SQL 语句。1704793036_659d13cc9e2d1da557908.png!small?1704793035530

准备声明电话:1704793048_659d13d86d2b100c6be6b.png!small?1704793047190

开始预编译:1704793059_659d13e35db105f5bf899.png!small?1704793058126

handler.parameterize 对预编译语句执行变量初始化。首先是long类型的id,通过ibatis的setLong方法进行处理(由于SQL注入主要针对String类型,本文不再进一步测试Int或long类型的处理方法,只关注String类型):1704793100_659d140c4e724d7652548.png!small?1704793099877

1704793118_659d141ed764c627e09c5.png!small?1704793117854

然后是String类型的名称:1704793128_659d14285c7116bfb3696.png!small?1704793127845

1704793144_659d1438bb09b5fe1a968.png!small?1704793143778

模板SQL语句与boundSql对象中的参数对象一一对应1704793156_659d14445226d58fcf1c4.png!small?1704793155849

typeHandler.setParameter 绑定“名称”和有效负载1704793170_659d14523919d9524345a.png!small

**来到关键方法。 ** Name为字符串类型,setNonNullParameter赋值,ps.setString(i,parameter);方法被调用。该方法判断String类型变量中的特殊符号是否需要转义:1704793196_659d146c7f29174d44ef1.png!small?1704793195344

1704793205_659d147551857ab68b5d9.png!small?1704793203975

可以发现,攻击载荷中包含的‘’在这里会被转义。另外,'\u0000','

','\r',…等符号也会被转义,并在特殊符号后面酌情添加\',

,\r,\Zetc.1704793211_659d147b560db1ed5692b.png!small?1704793210193

1704793219_659d14834b2b1f0d408e2.png!small

最后,经过预编译后,将发送到MySQL的stmt对象变为:1704793227_659d148b8cf9ca3ab2b99.png!small?1704793226475

1704793296_659d14d0497ad5c800828.png!small?1704793295062

````

插入sys_user ( id、姓名、年龄、电子邮件) 值( 1744564513751109634, 'Jasdf'',42,''[email protected]'');/**/DROP/**/TABLE/**/sys_user;/**/--#', 3, '[email protected]' )

````

比较原始有效负载:

Jasdf',42,'[email protected]');/\\*\\*/DROP/\\*\\*/TABLE/\\*\\*/sys\\_user;/\\*\\*/--#

Jasdf' **'**,42,''[email protected]' **'**);/\\*\\*/DROP/\\*\\*/TABLE/\\*\\*/sys\\_user;/\\*\\*/--#

回答第一个问题,String类型的payload经过preparedStatement处理后,会在原来的单引号'后面添加一个'作为转义符',这可能会导致SQL语句被截断,从而避免SQL注入的发生。本文以“insert”语句为例。 Update\Select\Create语句中的setString类型变量也是如此,这里不再赘述。

有这么方便的MyBatis-Plus,不需要人工干预就可以完成参数预处理。那么,只要使用它,是不是就能远离SQL注入,高枕无忧呢?其实是不可能的,我们下次再解释。

## 5\\.参考

1. https://github.com/baomidou/mybatis-plus-samples 转载自freebuf:[https://www.freebuf.com/articles/web/389261.html](https://www.freebuf.com/articles/web/389261.html) 作者:FreeBuf_425926

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。