简述
RMI 数据传输基于反序列化,如果服务中实现了 RMI 的调用且具有 Object 类型参数,那么就会造成反序列化(实际攻击中需要反序列化链)
JMX 服务是基于 RMI 实现,且将凭证对象作为Object 参数传入,如果使用恶意构造的对象替代凭证传入,是否能攻击需要凭证认证的 JMX 服务了。
针对 JMX 的攻击
YSO 中有一个针对JMX
的攻击模块,当JMX
服务器存在反序列化链时,可以对JMX服务进行攻击
java -cp ysoserial.jar ysoserial.exploit.JMXInvokeMBean 192.168.8.2 1689 CommonsCollections1 gnome-calculator
JMX 服务基于 RMI 实现,在分析其源码时发现,JMX RMIServer 中,存在有 newClient 方法的参数为 Object 对象,存在 RMI 反序列化条件。
且将newClient 参数为 credentials 凭证对象,那么是否可以使用恶意对象替代凭证传入,攻击需要凭证认证的 JMX 服务了。
具有Object 类型参数,会参数进行反序列化
环境搭建
环境配置
启动参数中添加
-Dcom.sun.management.jmxremote.port=2222 -Djava.rmi.server.hostname=192.168.8.1 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true
修改对应 java 版本中 jre 中的 jmx 的用户名密码及 访问权限
/Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/management
1. 复制 模版文件生成 jmxremote.password
2. 添加 用户名及密码 test test
3. 添加对应用户的权限,修改文件 jmxremote.access
monitorRole readonly
controlRole readwrite \
create javax.management.monitor.*,javax.management.timer.* \
unregister
test readwrite \
create javax.management.monitor.*,javax.management.timer.* \
unregister
jmx Server
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
public class JMXServerWithAuth {
public static void main(String[] args) throws Exception {
// Create a new MBean instance from Hello (HelloMBean interface)
Hello mbean = new Hello();
// Create an object name,
ObjectName mbeanName = new ObjectName("de.mogwailabs.MBeans:type=HelloMBean");
// Connect to the MBean server of the current Java process
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
server.registerMBean(mbean, mbeanName);
// Keep the application running until user enters something
System.out.println("Press any key to exit");
System.in.read();
}
}
调试细节
需要凭证的 JMX 服务
JDK 版本 8u291
需要凭证认证调试, 在进行反序列化之前进行了类型检查,需要在白名单之类,只能为 String 或者 String字符串类型
调用栈如下
关键函数
不需要凭证的 JMX 服务
调用栈
在 var1 instanceof DeserializationChecker 判断时产生分歧,需要权限的 JMX-Server 是 RMIJRMPServerImpl$ExportedWrapper 类型,进入 unmarshalParameterUnchecked 函数,而在 无需凭证时,进入 checked 函数
回溯
回溯一下 var1 生成
newRMIJRMPServerImpl , 需要 凭证的server env是存储了凭证信息的
RMIJRMPServerImpl 构造方法,当 types(允许的类型)不为空时,返回 内部类 ExportedWrapper
真相大白,对于开启了凭证认证的 JMX 服务 会使用 exportedWrapper 对 RMIJRMPServerImpl 实例进行包装,而不需要凭证认证的 JMX 服务则直接返回RMIJRMPServerIMPL实例,不会进行类型检查
对比oracle jdk, 在 jdk8u77 之后,8u91 更新了这个补丁
总结
在 JAVA 版本低的情况下(8u77),是可以直接攻击 JMX 服务(存在反序列化链)的;但是当版本高之后,则不可以。
在现行版本(jdk8u291)情况下,对于开启了凭证认证的 JMX 服务 会使用 exportedWrapper 对 RMIJRMPServerImpl 实例进行包装,而不需要凭证认证的 JMX 服务则直接返回RMIJRMPServerIMPL实例,不会进行类型检查。
使用凭证认证基本保证了 JMX 服务的安全性,但是 JAVA 版本低的情况下(8u77)仍然能够使用 RMI 其他的攻击方式。
在 JMX 不需要认证的情况下,只要对应主机有相应攻击链就可以满足攻击条件。