更新时间:2021-11-04 10:08:12 来源:极悦 浏览1429次
此工具不用于查找漏洞。相反,它使用已知的 source->…->sink 技巧或其类似的特性来发现分支利用链或新的利用链。
这个工具在整个应用程序的类路径中寻找一个链。
该工具执行一些合理的风险估计(污点判断、污点转移等)。
这个工具会产生误报而不是漏报(其实还是会漏掉的,这是由作者使用的策略决定的,可以在下面的分析中看到)。
该工具基于字节码分析。对于Java应用,很多时候我们没有源代码,只有War包、Jar包或者class文件。
此工具不会生成可直接使用的 Payload。具体的利用结构也需要人工参与。
在Java学习中,序列化是将对象的状态信息转换为可以存储或传输的形式的过程。转换后的信息可以存储在磁盘上。在网络传输过程中,可以是字节、XML、JSON等形式,将字节、XML、JSON等形式的信息还原为对象的逆过程称为反序列化。
在Java中,对象序列化和反序列化广泛应用于RMI(远程方法调用)和网络传输。
JDK(对象输入流)
XStream(XML,JSON)
杰克逊(XML,JSON)
根森(JSON)
JSON-IO(JSON)
FlexSON(JSON)
Fastjson(JSON)
…
不同的反序列化库在反序列化不同的类时有不同的行为。不同的“魔法方法”会被自动调用,这些自动调用的方法可以作为反序列化的入口点(源码)。如果这些自动调用的方法调用了其他的子方法,那么调用链中的一个子方法也可以作为源,相当于知道了调用链的前端,从一个子方法开始寻找不同的分支。一些危险的方法(sink)可以通过方法的层调用来达到。
对象输入流
比如一个类实现了Serializable接口,那么ObjectInputStream.readobject在反序列化的时候会自动找到该类的readObject、readResolve等方法。
比如一个类实现了Externalizable接口,那么ObjectInputStream.readobject在反序列化的时候会自动找到这个类的readExternal等方法。
杰克逊
ObjectMapper.readValue反序列化一个类时,会自动查找反序列化类的无参构造函数、包含基类型参数的构造函数、属性的setter、属性的getter等。
…
在接下来的分析中,以JDK自带的ObjectInputStream为例。
控制数据类型 => 控制代码
在反序列化漏洞中,如果我们控制了数据类型,我们就控制了代码。这是什么意思?根据理解,写了下面的例子:
public class TestDeserialization {
interface Animal {
public void eat();
}
public static class Cat implements Animal,Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
public static class Dog implements Animal,Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
public static class Person implements Serializable {
private Animal pet;
public Person(Animal pet){
this.pet = pet;
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
Animal animal = new Dog();
Person person = new Person(animal);
GeneratePayload(person,"test.ser");
payloadTest("test.ser");
// Animal animal = new Cat();
// Person person = new Person(animal);
// GeneratePayload(person,"test.ser");
// payloadTest("test.ser");
}
}
为方便起见,将所有类都写在一个类中进行测试。在Person类中有Animal类的属性pet,它是Cat和Dog的接口。在序列化中,我们可以控制Per的pet是Cat对象还是Dog对象,所以在反序列化中,pet.eat()inreadObject的具体方向是不同的。如果pet是Cat类对象,就不会去执行有害代码Runtime.getRuntime().exec("calc");,但是如果pet是Dog类对象,它就会去执行有害代码。
即使有时在声明时为类属性分配了特定对象,它仍然可以通过 Java 中的反射进行修改。如下:
public class TestDeserialization {
interface Animal {
public void eat();
}
public static class Cat implements Animal, Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
public static class Dog implements Animal, Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
public static class Person implements Serializable {
private Animal pet = new Cat();
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
Animal animal = new Dog();
Person person = new Person();
//Modify private properties by reflection
Field field = person.getClass().getDeclaredField("pet");
field.setAccessible(true);
field.set(person, animal);
GeneratePayload(person, "test.ser");
payloadTest("test.ser");
}
}
在Person 类中,您不能通过构造函数或setter 方法或其他方法为pet 赋值。该属性在声明时就已经定义为Cat类的对象,但是使用反射可以将pet修改为Dog类的对象,所以反序列化的时候还是会跑到有害代码中去。
这只是我个人对作者的看法:“控制数据类型,你控制代码”。在Java反序列化漏洞中,很多时候是利用Java的多态特性来控制代码的方向,最终达到作恶的目的。
在上面的例子中,我们可以看到Person的readobject方法在反序列化的时候并没有被手动调用。当 ObjectInputStream 反序列化对象时会自动调用它。作者将自动调用的方法调用为“魔术方法”。
使用 ObjectInputStream 反序列化时的几种常见魔术方法:
Object.readObject()
Object.readResolve()
Object.finalize()
…
一些可序列化的 JDK 类实现了上述方法,并且还会自动调用其他方法(可以用作已知的入口点):
哈希表
Object.hashCode()
Object.equals()
优先队列
Comparator.compare()
Comparable.CompareTo()
…
一些水槽:
Runtime.exec(),在目标环境中直接执行命令的最简单直接的方式
Method.invoke(),需要正确选择方法和参数,并通过反射执行Java方法
RMI/JNDI/JRMP等,通过引用远程对象间接实现任意代码执行的效果
…
以上就是关于“Java反序列化工具:Gadgetinspector First Glimpse”的介绍,大家如果想了解更多相关知识,不妨来关注一下极悦的Java开发工具,里面对于工具的介绍有很多,大家可以多去学习一下。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习