前言
之前我们讲了JAVA的一些组件安全,比如Log4j,fastjson。今天讲一下框架安全,就是这个也是比较常见的SpringBoot框架。
SpringBoot框架
Spring Boot是由Pivotal团队提供的一套开源框架,可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持,可以帮助开发者更轻松快捷地构建出企业级应用。Spring Boot通过自动配置功能,降低了复杂性,同时支持基于JVM的多种开源框架,可以缩短开发时间,使开发更加简单和高效。
先来了解一下这个框架里面常见的东西
路由映射
@RequestMapping, @GetMapping, 和 @PostMapping 注解用于定义HTTP请求的映射路径。
@RequestMapping 是通用注解,而 @GetMapping 和 @PostMapping 是其简化形式,分别用于处理GET和POST请求。
参数传递
@RequestParam 注解用于从HTTP请求中提取参数,使得控制器方法可以访问并使用这些参数。
数据响应
@RestController 注解用于标识一个类是RESTful风格的控制器,它包含了 @ResponseBody 和 @Controller 的功能。
@ResponseBody 表示方法的返回值将直接作为HTTP响应体返回给客户端。
@Controller 通常用于标识传统的MVC控制器,而 @RestController 更适用于RESTful风格的控制器。
项目构建
新建一个项目。

这个服务器URL是默认从官网获取架构,如果说你觉得网络卡的话,你可以改为https://start.aliyun.com,去从阿里云获取架构。

版本的话目前最新的就是3.4.0了。

如果你前面用的是阿里云的架构网址,那么就会只显示2.x的版本,就是说阿里云认为这几个版本比较稳定,所以只提供这几个。

我这里网速慢,就用阿里云的2.3.16版本,依赖选择Spring Web。

创建成功之后可以看到这个框架自带了很多外部库,什么Log4j啊啥的,就不需要我们自己去下载了,比较方便。

新建一个类叫controller.IndexController。

我们写入以下代码。
package com.sf.maven.springboot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@RequestMapping("/wlw")
public String index(){
return "wlw";
}
}
接着回到Application.java这里运行起来。

此时便可以在控制台这里看到启动了8080端口也就是Tomcat服务,这是它自带的我并没有去配置它。

如果端口被占用的话,你可以在resources目录下的application.properties配置文件进行修改。

访问8080端口,返回的确是我们写入的东西。

我们稍微修改一下,添加POST请求和GET请求。
package com.sf.maven.springboot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@RequestMapping(value = "/wlwget",method = RequestMethod.GET) //get请求
public String getmethod(){
return "wlwget";
}
@RequestMapping(value = "/wlwpost",method = RequestMethod.POST) //post请求
public String postmethod(){
return "wlwpost";
}
}
我们运行起来再访问一下,可以看大此时我们再访问wlw的话会报错,这个就是典型的SpringBoot报错页面。

get提交wlwget,则是正常返回。

我们用postman这个工具去请求wlwpost,也是正常返回信息。

现在我们修改代码添加参数来试一下,稍微修改一下代码,传递参数name。

get请求。
post请求。

MyBatis注入
MyBatis是SpringBoot中用的较多的一个数据库驱动,它存在的一个安全问题就是,使用MyBatis的时候遇到了#{}和${}可能导致sql注入的问题。
先创建一个项目重新来吧,命名为MyBatis-demo。

依赖项就选择Spring web,MyBatis Framework,MySQL Driver。

数据库的话我这里是用phpstudy,简单方便,我是在user库里面新建一个user表来进行测试。

在pom文件这里可以看到,外部包都被导入了。

接着再来配置一下数据库连接,把src/main/resources/ 目录下的 application.properties文件改为application.yml

清空里面的内容。写入以下的内容。
spring:
datasource:
url: jdbc:mysql://localhost:3306/user
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

新建一个类叫entity.User。

接着我们先写入以下代码,要和数据库的字段对应。

然后右键选择生成,选择Setter和Getter,把显示的三个东西都选上确定即可。

自动生成了方法代码。

同理选择toStron()方法自动生成。

再新建一个类叫mapper.Usermapper。

写入以下的代码,创建sql查询的接口。
package com.sf.maven.mybatisdemo.mapper;
import com.sf.maven.mybatisdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface Usermapper {
// ${id} 是拼接写法,#{id} 是预编译写法
@Select("select * from name")//通过ID查询
public List<User> findAll();
@Select("select * from name where ID=1")
public List<User> findID();
}
同理再创建一个constroller类去调用我们的sql查询,新建一个类叫constroller.Getnamecontroller,
这个和上面的那个 web 应用访问一样,就是返回数据为从数据库获取信息。
package com.sf.maven.mybatisdemo.constroller;
import com.sf.maven.mybatisdemo.entity.User;
import com.sf.maven.mybatisdemo.mapper.Usermapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class Getnamecontroller {
@Autowired
private Usermapper Usermapper;
@GetMapping("/Getpasswd")//定义请求路径
public List<User> getpasswd(@RequestParam Integer id) {
List<User> all = Usermapper.findAll();//调用sql查询接口
return all;
}
@GetMapping("/GetID")
public List<User> getID() {
List<User> all = Usermapper.findID();
return all;
}
}
把搞好的项目运行起来,访问路径可以查看到表的内容,说明是执行了我们设定好的sql语句。


接下来就讲一下产生的安全问题,改一下刚才的代码。


运行起来访问一下,这次要加上参数才可以进行查询。

我们设置的sql语句是 select * from name where id like '%${id}%',此时我们只要把 id=1 的查询改为 id=1%' or 1=1# 即可产生sql注入,这是因为当参数拼接上去之后sql语句就变成这样子了
select * from name where id like '%1%' or 1=1#' 。
只是不知道为啥我这里会报错,sql语句没问题呀。

我索性去到数据库那里试一下,发现是可以执行没有报错,你妹的。

关于为啥会产生sql注入,可以看一下这篇文章,在Java中呢,sql注入是非常少的,但是不代表没有。
Mybatis 框架下 SQL 注入攻击的 3 种方式,真是防不胜防! - 知乎
【安全】mybatis中#{}和{}导致sql注入问题及解决办法_{}sql注入-CSDN博客
Thymeleaf模板注入
我们在浏览一些网站时,会发现有些网站的页面十分的精美,这是由于网站开发引用了一些模板。而在SpringBoot中,Thymeleaf是个不安全的模版,日常开发中语言切换页面,主题更换等传参导致的 SSTI 注入安全问题。
新建一个项目叫Thymeleaf demo。

依赖项选择Thymeleaf和Spring web。

新建一个类叫controller.thymeleafcontroller。

我们写入个比较简单的代码,什么意思呢,就是访问index,由于我没有使用模板渲染那么就会返回个Hello World。

运行起来访问index,确实返回了Hello World,我这里就比较简陋的测试一下,实际情况中一般都是十分精美的页面。

那么现在我们使用一下模板渲染,在配置文件中可以看到模板的路径为templates/。

我们在resources目录下新建一个templates目录。

写一个index.html文件当作模板。

修改一下调用文件,调用我们的index模板并输出hello wlw。

运行代码访问index,然而却并没有返回hello wlw,而是返回了index。

这是为啥呢,原来是@RestController包含了 @ResponseBody 和 @Controller 的功能。@ResponseBody index当做字符串显示操作,所以我们更换为@Controller 没有ResponseBody index那么就会当做资源文件去渲染。

改了之后再访问返回了hello wlw,说明成功调用了模板。

查看页面源码,发现和我们写的index.html文件差不多。

OK那么我们现在来看一下安全问题,上面我们说过安全问题就是由于模板的调用,我们可以看到当前的lang=ZH_CN就是调用了中文页面的模板。

当我们把lang的值换为en那么就会调用英文的模板,思考一下我们把lang的值换位Java代码是否能实现rce。

注入漏洞存在与3.0.0---3.0.11版本,2.x版本的应该也会存在漏洞,但是我们的这个包是从阿里云拉取的。阿里云的东西一般都比较稳定没啥漏洞,我们替换一下pom文件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>java-spring-thymeleaf</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--latest-->
<version>2.2.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
替换了之后我们可以看到版本变为了2.2.0release。

稍微修改一下调用文件,使其可以传递参数lang。

我们再新建一个index-en.html模板文件,啥也不用写。

运行代码访问啥也没有,因为我们模板啥也没写。

把index-en换成下面的Java代码,成功弹出计算机。
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x

至于poc怎么构造的后面有机会再说了,或者看一下以下文章。
Thymeleaf模板生成过程以及注入原因分析 - 先知社区
总结
本次从开发的方面讲了两个SpringBoot框架常见的安全问题,感觉有点强度了,哎。
最后,以上仅为个人的拙见,如何有不对的地方,欢迎各位师傅指正与补充,有兴趣的师傅可以一起交流学习。