Spring Data REST远程代码执行漏洞(CVE-2017-8046)

Artio 于 2022-02-14 发布

一:漏洞简介

Spring Data REST是Spring Data项目的一部分,可以在Spring Data存储库之上构建超媒体驱动的REST Web服务。Spring Data REST存在远程代码执行漏洞,攻击者通过构造恶意的PATCH请求提交给spring-data-rest服务器,使用特制的JSON数据来运行任意的Java代码,从而实现远程代码执行攻击。

二:影响版本

三:漏洞复现

使用官方demo:https://github.com/spring-guides/gs-accessing-data-rest.git

下载后如图所示:

1644482933_6204d175ac5764ee25ac3.png!small?1644482933855

直接idea导入complete文件夹让它自动下载依赖。

下载完成后如图所示:

1644482997_6204d1b51e17c3a6b30b6.png!small?1644482997393

接下来我们需要将pom.xml文件中的springboot的版本修改为含有漏洞的版本,springboot是一个父依赖,其中含有spring-data-rest-webmvc这个核心组件。

1644483064_6204d1f8706c1d32fecd6.png!small?1644483064697

然后直接运行AccessingDataRestApplication类就可以启动,启动成功后访问127.0.0.1:8080返回如下代表启动成功:

1644483085_6204d20d5571e5a943e53.png!small?1644483085714

接下来先简单说一下Patch方法(已有的上传数据的方法有POST和PUT,但是功能上有些不足):

Patch方法是新引入的对PUT方法的补充,用来对已知资源进行局部更新。Patch请求方法有一个标准,必须包含一个path和op字段, op表示具体操作, path用于定位准确数据字段。

Patch方法的content-type类型为: application/json-patch+json

上边说到Patch方法的主要作用是用来对已知的资源进行更新,我们来举个例子:

已知json数据:

{

“baz”: “qux”,

“foo”: “bar”

}

发送下边的请求:

[

{ “op”: “replace”, “path”: “/baz”, “value”: “boo” },

{ “op”: “add”, “path”: “/hello”, “value”: [“world”] },

{ “op”: “remove”, “path”: “/foo” }

]

最初的json数据会变成(修改baz的值,新增hello值为world,删除foo):

{

“baz”: “boo”,

“hello”: [“world”]

}

接下来我们开始复现:

使用POST方式为系统新增一个用户:

1644484107_6204d60bb24faf689c0a3.png!small?1644484108095

可以看到用户新增成功,然后我们需要使用PATCH方式对该用户的信息进行修改:

1644484127_6204d61f9f6bf56b44a6a.png!small?1644484127762

可以看到用户信息修改成功。

漏洞点就在PATCH请求的path参数里,我们把path参数的值修改为恶意代码,如下:

注:

  • 必须将Content-Type指定为application/json-patch+json。
  • 请求数据必须是json数组。

1644484234_6204d68a4379ad2bcd691.png!small?1644484234511

弹计算器操作:

这里需要改变一下编码,可以使用下边这种方式:

1644484391_6204d7276af4afcfb238e.png!small?1644484391650

[{ “op”: “replace”, “path”: “T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{99, 97, 108, 99, 46, 101, 120, 101}))/lastName”, “value”: “vulhub” }]

执行完后可以看到成功弹出计算器:

1644484283_6204d6bb9468c0570b7f3.png!small?1644484283972

四:漏洞分析

结合网上大佬们的文章,我们payload提交的是json格式,所以先从处理json的地方开始入手:

org.springframework.data.rest.webmvc.config.JsonPatchHandler:apply()

1644484509_6204d79db482383c8091c.png!small?1644484510282

首先,我们提交json数据后程序会先判断请求头中的content-type是否为application/json-patch+json,请求方式是否为PATCH。如果符合的话调用applyPatch方法并传入请求体,否则调用applyMergePatch方法。

其中isJsonPatchRequest方法中是这样判断请求头和请求方法:

1644484558_6204d7ce86dfc5861f924.png!small?1644484558639

然后判断完请求方法和请求头后进入下一步的applyPatch方法。

1644484579_6204d7e3d2f1a60525907.png!small?1644484579964

继续往下,会进入getPatchOperations方法,该方法首先会利用mapper初始化JsonPatchPatchConverter对象之后调用convert方法:

1644484608_6204d800755dcaff04e85.png!small?1644484608898

继续跟进convert方法,可以看到convert方法会返回Patch对象,其中ops包含了我们发送的payload:

1644484624_6204d8109c32a264d6cf3.png!small?1644484625075

继续往下,进入Patch

1644484639_6204d81f245118f141b66.png!small?1644484639617

通过上一步的返回结果我们可以看到ops是一个List对象,每一个PatchOperation对象中包含了op、path、value三个内容,我们进入PatchOperation分析下:

1644484671_6204d83f0889eb33cf2df.png!small?1644484678575

Path传到了pathToExpression,直接进去可以看到这是对spel表达式的解析操作,但是解析前调用了pathToSpEL,进去看下:

1644484687_6204d84f259a02d4d31e2.png!small?1644484687553

可以看到pathToSpEL方法中先对path进行分割操作(通过/进行分割)

1644484706_6204d862083cebb5ab07d.png!small?1644484706235

然后pathNodesToSpEL方法做了简单的字符串重组

1644484720_6204d870397fd1db641cc.png!small?1644484720516

并没有做任何的检验,

然后继续执行,回到applyPatch方法中,然后继续执行发现PatchOperation是一个抽象类,实际上应该调用其实现类的perform()方法。通过动态调试分析,此时的operation实际是ReplaceOperation类的实例:

1644484739_6204d883269c2662cec63.png!small?1644484739506

然后进入perform方法,继续执行,在setValueOnTarget()中会调用spelExpression对spel表示式进行解析从而触发该漏洞:

1644484754_6204d8927cfd6a2b73746.png!small?1644484754860

注:这篇文章分析时参考了4ra1n大佬的文章,菜鸟第一次写文,大佬们多多指教。

五:参考链接

https://4ra1n.love/post/wQearOYqr/