java安全 反序列化(二)
前言
寻找反序列化链(Gadget)
-
在项目里找漏洞
readObject里的漏洞一般比较少
-
寻找项目的依赖库类中的Gadget
一些依赖也会有反序列化的操作,如果jar包中的某些类在进行反序列化时有可控的点,就可以利用jar包中存在的漏洞来构造调用链
ysoserial工具
ysoserial工具可以帮助我们在依赖库里面找到利用链。
git clone https://github.com/frohoff/ysoserial.git
进入ysoserial目录,编译jar包
mvn clean package -DskipTests
出现BUILD SUCCESS表示编译成功,在target文件夹下
注意:要使用java 1.7+ 的jdk环境
最经典的反序列化利用链
Apache Commons Collections.jar中的一条pop链,这个类库使用广泛,所以很多大型的应用也存在着这个漏洞。
Commons Collections 在3.x < 3.2.2 以及4.0这些版本范围里,存在反序列化漏洞
当目标Java应用依赖库里包含存在漏洞的Commons Collections库,且对由攻击者可控的数据进行反序列化时,即会造成任意代码执行。
我们以ysoserial里CommonsCollections6这个payload为例,进行分析。
先使用ysoserial生成反序列化的payload
使用maven导入commons-collections依赖
pom.xml
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
在创建测试代码
TestCC6
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
public class TestCC6 {
public static void main(String[] args) throws IOException, ClassNotFoundException{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc6.ser"));
objectInputStream.readObject();
objectInputStream.close();
}
}
把生成的cc6.ser放在项目根目录下执行代码,弹出计算机
运行环境为java11
要解析cc6链的结构代码要先学习下java反射相关,我们可以在很多java漏洞的POC中看到反射的利用,所以学习java安全是绕不开反射的。
java反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性(包括私有的方法和属性);这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
涉及到java中的几个类:
Class类 代表类的实体,在运行的java应用程序中表示类和接口
Field类:代表类的成员变量(成员变量也称为类的属性)
Method类:代表类的方法
Constructor类:代表类的构造方法
在Java中你看到的绝大部分成员,其实都可以称之为对象(除了普通数据类型和静态成员)。
类也是对象,类是java.lang.Class类的实例对象
Class类抽象出了java中类的特征并提供了一些方法
有三种方式获得Class类实例:
1.如果知道class的完整类名,可以调用Class类的静态方法Class.forName获取
Class clz = Class.forName("com.dong.User");
2.任何一个类都有一个隐含的静态成员class,这个属性就存储着这个类对应的Class类的实例:
Class clz = com.dong.User.class;
3.调用这个对象的getClass()方法:
Class clz = (new User()).getClass();
测试获取,调用方法
User
public class User {
public void test(String name){
System.out.println("Hello:"+name);
}
}
获取方法:
我们之前已经提到了Method这个类,java中所有的方法都是Method类型,所以我们通过反射机制获取到某个对象的方法也是Method类型的。通过Class对象获取某个方法:
方法的名称和方法的参数列表,两者信息才能确定某一个方法
clz.getMethod(方法名,这个方法的参数类型)
调用方法:
Method类中有一个invoke方法,就是用来调用特定方法的,用法如下:
public Object invoke(Object obj, Object... args)
第一个参数是调用该方法的对象,第二个参数是一个可变长参数,是这个方法的需要传入的参数
testUser
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class testUser {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class user = (new User()).getClass();
//第二个参数要传class类,如果参数有多个传入:new Class[]{String.class,String.class}
Method test = user.getMethod("test",String.class);
//如果第二个参数有多个传入:new Object[]{"1","2"}
test.invoke((new User()),"liangBan");
}
}
修改变量
User
public class User {
private String pass = "123321";
@Override
public String toString() {
return "User{" +
"pass='" + pass + '\'' +
'}';
}
}
testUser
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class testUser {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
Class user = (new User()).getClass();
//获取私有属性对象
Field pass = user.getDeclaredField("pass");
//关闭java访问控制检查,就可以给private属性赋值调用
pass.setAccessible(true);
User u = new User();
System.out.println(u);
pass.set(u,"liangban");
System.out.println(u);
System.out.println(pass.get(u));
}
}