JAVA中的SQL注入

渗透技巧 2年前 (2022) admin
609 0 0

JAVA中的SQL注入

环境

直接用的 springboot 搭建的测试环境

jdbc 字符串拼接

JDBC 有两种方式执行 SQL 语句,分别为 PreparedStatementStatement

  • Statement 方法在每次执行时都需要编译
  • PreparedStatement 会对SQL语句进行预编译,后续无需重新编译

Statement 会直接拼接 sql 语句造成 SQL 注入漏洞

Statement

@RestController
@RequestMapping("/test1")
public class test {
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/test")
public String jdbc_sql(@RequestParam("id") String id){
String sql = "select * from tb_user where id =" + id;
System.out.println(sql);
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
return "result:"+maps;
}
}

这里存在 sql 注入 直接用拼接的方式:http://127.0.0.1:8080/test1/test?id=1%20or%201

JAVA中的SQL注入
JAVA中的SQL注入

PreparedStatement

预编译会将传入的值用单引号包裹起来,利用占位符 ? 及传入的参数,而不是直接 sql 语句拼接到语句 使输入的字符串是 数值 而不是 关键字

@GetMapping("/test1")
public String jdbc_presql(@RequestParam("id") String id){
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3307/txh", "root", "root");
String sql = "select * from tb_user where id = ?";
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1,id);
System.out.println(st.toString());
String res = "";
ResultSet rs = st.executeQuery();
while (rs.next()){
res += rs.getString("username")+" ";
}
return res;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}

}
JAVA中的SQL注入
JAVA中的SQL注入
JAVA中的SQL注入
JAVA中的SQL注入

这里肯定会想到包裹了单引号 用单引号来进行闭合

id=5%27%20or%20%271%27=%271

控制台打印

JAVA中的SQL注入

这里显示是闭合了对吧 然后这语句复制到 navicat 是可以执行的

JAVA中的SQL注入

但是页面没有任何东西

JAVA中的SQL注入

因为 jdbc 对一些特殊字符进行了转义

JAVA中的SQL注入

但是即使用了预编译还是可能存在 sql 注入的风险

在没有使用占位符而是使用拼接的时候

拼接输入
@GetMapping("/test2")
public String jdbc_presql1(@RequestParam("id") String id){
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3307/txh", "root", "root");
String sql = "select * from tb_user where id =" + id;
PreparedStatement st = conn.prepareStatement(sql);
// st.setString(1,id);
System.out.println(st);
String res = "";
ResultSet rs = st.executeQuery();
while (rs.next()){
res += rs.getString("username")+" ";
}
return res;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}

}
JAVA中的SQL注入
JAVA中的SQL注入
JAVA中的SQL注入

是可以进行注入的

In

String sql = “delete from user where id in(” + Id + “);

因为一般使用 in 的时候因为不确定 id 有多少个 相当于也是用的拼接的方式 所以易造成 sql 注入

like 语句

也和 in 类似 都是进行拼接

String sql = “select * from user where name like ‘%” + a + “%'”;

Order by

在使用 order by 的时候是无法进行预编译的,因为 order by 后面要跟字段名,预编译之后会包裹上单引号就不会被识别为字段名 使用在用 order by 的时候就只能不用预编译 易存在 sql 注入

框架使用不当造成SQL注

Mybatis

#$的区别
  • #号会点语句进行预编译
  • ${ } 只是进行string 替换,动态解析 SQL 的时候会进行变量替换
${}

pojo

import lombok.Data;

@Data
public class User {
private Integer id;
private String username;
private String password;
}

Dao

@Mapper
public interface UserDao {
@Select("select * from tb_user where id =${id}")
public List<User> getById(String id);
}

controller

@Autowired
private UserDao userDao;
@GetMapping("/test1")
public List<User> mybatis_sql(@RequestParam String id){
return userDao.getById(id);
}
JAVA中的SQL注入
JAVA中的SQL注入

进行注入

http://127.0.0.1:8080/test2/test1?id=1%20or%201

JAVA中的SQL注入
JAVA中的SQL注入

${} 也是进行语句拼接 加上过滤不严格存在sql注入

#{}

dao

@Select("select * from tb_user where id =#{id}")
public List<User> getById1(String id);

controller

@GetMapping("/test2")
public List<User> mybatis_sql2(@RequestParam String id){
return userDao.getById1(id);
}
JAVA中的SQL注入
JAVA中的SQL注入

可以看到是采用的占位符的方式 无法注入成功

易存在注入的情况

和 jdbc 差不多  在使用 in  like  orderby 的时候 因为无法使用 #{},在用 ${} 进行拼接的时候易造成 sql 注入

<select id="getById" parameterType="String" resultMap="User">
select * from user where id like '%${id}%'
</select>

这种情况就易出现

可以用这种方式写进行防止

<select id="getById" parameterType="String" resultMap="User">
select * from user where id like concat('%',#{id}, '%')
</select>

In也是如此

<select id="getById" parameterType="String" resultMap="User">
select * from user where id in (${id})
</select>

这种写法是危险的

较好的可以用 foreach

mybatis-plus

关于

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

相关sql注入场景

mybatis-plus 已经对之前一些可能存在 sql 注入的场景进行了预编译处理

比如 like in 等

但是还是依旧存在一些拼接的情况在开发的过程中需要注意

配置环境

pojo

@Data
public class User {
private Integer id;
private String username;
private String password;
}

pom.xml

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>

dao

@Mapper
public interface UserDao1 extends BaseMapper<User> {
}
like模糊查询
@RestController
@RequestMapping("/test3")
public class test3 {
@Autowired
private UserDao1 userDao1;
@GetMapping("/test1")
public List<User> mybatis_plus(@RequestParam String name){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("*").like("username",name);
List<User> user = userDao1.selectList(queryWrapper);
return user;
}
}
JAVA中的SQL注入
JAVA中的SQL注入

可以看到是用占位符

in
@GetMapping("/test2")
public List<User> mybatis_plus1(@RequestParam String name,String name1){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("*").in("username",name,name1);
List<User> user = userDao1.selectList(queryWrapper);
return user;
}
JAVA中的SQL注入
JAVA中的SQL注入
注入场景

以 QueryWrapper 为例,在开发的时候有些场景需要用到拼接的地方,在官方的文档(https://baomidou.com/pages/10c804/#apply)也给出了警示

apply
JAVA中的SQL注入

如果直接使用 apply() 拼接 sql 语句,则存在 sql 注入

@GetMapping("/test3")
public List<User> mybatis_plus2(@RequestParam String name,String password){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",name).apply("password= "+ password);
List<User> user = userDao1.selectList(queryWrapper);
return user;
}
JAVA中的SQL注入

http://127.0.0.1:8080/test3/test3?name=admin&password=%27admin%27%20or%201

JAVA中的SQL注入
JAVA中的SQL注入

造成注入

使用 {} 进行预编译就不存在注入

    @GetMapping("/test3")
public List<User> mybatis_plus2(@RequestParam String name,String password){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("username",name).apply("password= "+ password);
queryWrapper.eq("username",name).apply("password={0}",password);
List<User> user = userDao1.selectList(queryWrapper);
return user;
}
JAVA中的SQL注入
JAVA中的SQL注入
last
last(String lastSql)
last(boolean condition, String lastSql)
  • 无视优化规则直接拼接到 sql 的最后

    注意事项:

    只能调用一次,多次调用以最后一次为准,有sql注入的风险,请谨慎使用

  • 例:last("limit 1")

exists
exists(String existsSql)
exists(boolean condition, String existsSql)
  • 拼接 EXISTS ( sql语句 )
  • 例:exists("select id from table where age = 1")—>exists (select id from table where age = 1)
notExists
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
  • 拼接 NOT EXISTS ( sql语句 )
  • 例:notExists("select id from table where age = 1")—>not exists (select id from table where age = 1)

这几个都是进行 sql 拼接的 如果输入可控没有用 {} 进行预编译的话 就存在sql注入的风险

参考:https://www.sec-in.com/article/1073

原文始发于微信公众号(海狮安全团队):JAVA中的SQL注入

版权声明:admin 发表于 2022年11月27日 上午10:01。
转载请注明:JAVA中的SQL注入 | CTF导航

相关文章

暂无评论

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