初探Java反序列化漏洞

访客4年前关于黑客接单676

一、基本概念

Java序列化:就是将内存中的Java对象转换为字节序列的过程,可以理解为对Java对象打个快照。通过序列化,可以方便将Java对象保存在内存、文件、数据库等媒介中,也便于在 *** 中传输和共享Java对象。

Java反序列化:就是Java序列化的逆过程,将字节序列恢复为Java对象的过程。

序列化/反序列化并不是Java语言的独有特性,像PHP、Python、Ruby等动态语言也有类似的特性。序列化/反序列化的主要目的是:

1、远程过程调用(RPC):为不同系统或不同进程之间提供Java对象数据交互;

2、缓存/持久化存储:可以将Java对象缓存或存储到本地文件、磁盘、数据库等媒介中;

3、会话tokens:用于HTTP cookies、HTML form表单参数、API 认证tokens等场景的交互数据。

在Java中,序列化/反序列化操作主要由 java.io.ObjectOutputStream.writeObject(Object) *** 和 java.io.ObjectInputStream.readObject() *** 实现;在用户代码中,可以通过重写上述 *** 实现自定义操作。

Java序列化数据格式

参考文档:https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html

Java对象经过序列化后得到的数据是个二进制流,以固定的魔数(0xaced)和版本(0x0005)开始;在渗透测试过程中,可以以此来识别应用系统中反序列化的入口点。(0xaced 0005的base64编码以rO0AB开始,通常在Web应用系统中传输的Java序列化数据会经过base64编码)。

Java序列化数据示例:

1615975624_6051d4c876ccfc8464772.png!small

00000000: aced 0005 7400 0d48 656c 6c6f 2c20 776f  ....t..Hello, wo
00000010: 726c 6421                                rld!

在Github上提供了Java对象序列化dump工具,可以对Java对象序列化后的数据进行解析,具体请参考:https://github.com/NickstaDB/SerializationDumper。例如,将上述二进制数据解析后的结果如下:

1615975720_6051d52851bf469290c19.png!small

二、漏洞原理

Java反序列化漏洞产生的原因在于Java应用程序接收来自用户的序列化数据并尝试对其进行反序列化;如果攻击者通过构造恶意输入,让反序列化过程产生非预期的对象,将可能导致各种后果,严重时可能造成远程代码执行。

Java序列化/反序列化代码demo

下面代码演示将一段字符串经序列化保存到本地文件中,然后再从文件中恢复序列化的字符串。

package orz.vuln.poc;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

//将String对象序列化后保存到data.ser文件中
public class Serialization {
    public static void main(String[] args) throws Exception {
        String text="Hello, world!";
        
        FileOutputStream fos=new FileOutputStream("D:/data.ser");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        oos.writeObject(text);
        oos.close();
        fos.close();
    }
}

序列化后的数据:

1615975800_6051d578996b694635d34.png!small

package orz.vuln.poc;

import java.io.FileInputStream;
import java.io.ObjectInputStream;

//将data.ser文件中的数据反序列化为Java对象:
public class Deserialization {
    public static void main(String[] args) throws Exception {
        FileInputStream fis=new FileInputStream("D:/data.ser");
        ObjectInputStream ois=new ObjectInputStream(fis);
        String text=(String) ois.readObject();
        fis.close();
        ois.close();
        System.out.println(text);
    }
}

执行结果:

1615975833_6051d599ed9365e09f459.png!small

在这里,可以通过修改本地文件数据控制反序列化后的字符串的值;例如,将data.ser修改如下:

00000000: aced 0005 7400 0845 7669 6c54 6578 74    ....t..EvilText

执行反序列化代码,结果:

1615975870_6051d5be5fd87cb1b8100.png!small

更进一步Java序列化/反序列化

在实际开发中,更多是通过实现Serializable接口并重写readObject() *** 对自定义类对象进行反序列化,以完成更多操作。如下代码示例,我们通过自定义Test类,实现了Serializable接口,并重写readObject() *** ,在readObject() *** 中,我们自定义输出字符串“Oops...”和弹出计算器操作。

package orz.vuln.poc;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DemoCode {
	public static void main(String[] args) throws Exception {
		
		Test test=new Test("calc.exe");
		
		FileOutputStream fos=new FileOutputStream("D:/object.ser");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		oos.writeObject(test);
		oos.close();
		fos.close();
		
		FileInputStream fis=new FileInputStream("D:/object.ser");
		ObjectInputStream ois=new ObjectInputStream(fis);
		Test test2=(Test) ois.readObject();
		ois.close();
	}
}

class Test implements Serializable {
	private String cmd;
	
	public Test(String cmd) {
		this.cmd=cmd;
	}
	
	//重写readObject() *** 
	private void readObject(java.io.ObjectInputStream in) throws Exception {
		in.defaultReadObject();
		System.out.println("Oops...");
		java.lang.Runtime.getRuntime().exec(cmd);//触发代码执行,模拟调用链
	}
}

执行结果:

1615975906_6051d5e24c10c02499c12.png!small

1615975917_6051d5edb99671a961ab8.png!small

调用堆栈如下:

DemoCode [Java Application]	
	orz.vuln.poc.DemoCode at localhost:53445	
		Thread [main] (Suspended (entry into method exec in Runtime))	
			Runtime.exec(String) line: 345	
			Test.readObject(ObjectInputStream) line: 38	
			NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
			NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57	
			DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
			Method.invoke(Object, Object...) line: 601	
			ObjectStreamClass.invokeReadObject(Object, ObjectInputStream) line: 1004	
			ObjectInputStream.readSerialData(Object, ObjectStreamClass) line: 1891	
			ObjectInputStream.readOrdinaryObject(boolean) line: 1796	
			ObjectInputStream.readObject0(boolean) line: 1348	
			ObjectInputStream.readObject() line: 370	
			DemoCode.main(String[]) line: 22	
	D:\Tools\Java\jdk1.7.0_21\jre\bin\javaw.exe (2021年3月17日 下午5:13:24)	

从上述结果可以看出,反序列化将调用重写的readObject() *** ,执行了自定义的字符串输出和弹出计算器。如果重写的readObject() *** 中可以构造出代码执行利用链,将存在远程代码执行漏洞。当然,在实际开发过程中不可能像上述代码一样直接在readObject() *** 内部写上java.lang.Runtime.getRuntime().exec(cmd)这种代码;但是也差不太多,只是实际调用链比较复杂,通过控制反序列化的输入数据,结合Java反射调用机制,寻找可构建远程代码执行的调用链,动态调用java.lang.Runtime.getRuntime().exec()完成代码执行。

下面可以放一个JDK7u21反序列化漏洞的调用堆栈做个对比,只是调用过程更加复杂化:

orz.vuln.poc.JDK7u21Exploit at localhost:53452	
	Thread [main] (Suspended (entry into method exec in Runtime))	
		owns: TemplatesImpl  (id=46)	
		Runtime.exec(String) line: 345	
		EvilCodes.<init>() line: 17	
		NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]	
		NativeConstructorAccessorImpl.newInstance(Object[]) line: 57	
		DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45	
		Constructor<T>.newInstance(Object...) line: 525	
		Class<T>.newInstance0() line: 374	
		Class<T>.newInstance() line: 327	
		TemplatesImpl.getTransletInstance() line: 380	
		TemplatesImpl.newTransformer() line: 410	
		NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
		NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57	
		DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
		Method.invoke(Object, Object...) line: 601	
		AnnotationInvocationHandler.equalsImpl(Object) line: 197	
		AnnotationInvocationHandler.invoke(Object, Method, Object[]) line: 59	
		$Proxy0.equals(Object) line: not available	
		LinkedHashMap<K,V>(HashMap<K,V>).put(K, V) line: 475	
		LinkedHashSet<E>(HashSet<E>).readObject(ObjectInputStream) line: 309	
		NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
		NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57	
		DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
		Method.invoke(Object, Object...) line: 601	
		ObjectStreamClass.invokeReadObject(Object, ObjectInputStream) line: 1004	
		ObjectInputStream.readSerialData(Object, ObjectStreamClass) line: 1891	
		ObjectInputStream.readOrdinaryObject(boolean) line: 1796	
		ObjectInputStream.readObject0(boolean) line: 1348	
		ObjectInputStream.readObject() line: 370	
		JDK7u21Exploit.main(String[]) line: 91	

三、总结

Java反序列化漏洞的根源在于ObjectInputStream.readObject() *** 在进行反序列化时并没有对生成的对象类型做检测和限制,并且当这种反序列化漏洞存在于一些公共类库中时,将造成重大影响。例如Apache Commons Collections中实现的一些类可以被反序列化用来实现任意代码执行。而在WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些应用的反序列化漏洞能够得以利用,就是因为这些应用中使用了Apache Commons Collections类库。这就好像在开启了ASLR地址随机化防御的系统中,出现了一个加载地址固定的共享库,或者类似于C语言中使用的链接库,当这些库存在漏洞时,将对使用了这些库的应用造成重大影响。

相关文章

《2019-2020微信就业影响力报告》发布:微信带动2963万个就业机会

《2019-2020微信就业影响力报告》发布:微信带动2963万个就业机会

今天,腾讯微信与中国信通院共同发布了《2019-2020微信就业影响力报告》。报告显示,由公众号、小程序、微信支付、企业微信等共同构成的微信生态,具有连接超过12亿微信用户的天然优势,通过创造新型就业...

提升用户活跃和留存:百果园、丝芙兰、混沌大学等都做对了什么?

提升用户活跃和留存:百果园、丝芙兰、混沌大学等都做对了什么?

如果一个产品缺乏完善的活跃和留存机制,那么最终用户就会持续流失,那么我们要如何提升用户活跃和留存呢?我们可以百果园、丝芙兰、混沌大学等进行研究分析,看看他们做了一些怎样的措施。 我们在流量运营上有一...

到泰国玩多少钱(去泰国旅游要带多少钱?)

到泰国玩多少钱(去泰国旅游要带多少钱?)现在去泰国玩要花多少钱?那去泰国旅游需要带多少钱?去泰国旅游大家一般会花一周到半个月的时间,主要花费在机票、酒店、餐饮、购物和观光体验上。 泰国旅游 去泰国旅游...

如何实时监控别人的微信聊天内容记录

喝葛粉对于记忆力的提升有帮助,那么宝宝可以喝葛根粉吗?一岁的宝宝吃葛根粉会怎样,小孩喝葛根粉好不好,有哪些注意事项。 一岁宝宝可以吃葛根粉吗 可以的。 葛根粉无明显的毒副作用,在保证葛根粉是正品...

男的摆地摊适合卖什么(男人摆摊卖小吃怎么样

男的摆地摊适合卖什么(男人摆摊卖小吃怎么样

小吃在餐饮市场中真的是很好经营,一个人也是能够做起小吃生意的,这就让投资者发现了这是很好的致富商机。男人摆摊卖什么小吃好?简单适合一个人摆摊的小吃项目,下面我们跟随食为先小编一起来了解一下: 一、杂...

奠定了牛蝎子火锅加盟多久能回本?快来看

奠定了牛蝎子火锅加盟多久能回本?快来看

一个项目是否值得我们去加盟,就要看一看这个加盟项目标回本时间快不快了,奠基了牛蝎子暖锅加盟项目在市场上,创立多年,品牌形象早就已经深入到人心了,深受宽大消费者的喜爱与追捧,选择加盟这样一个好的加盟项目...