转载

Fastjson 1.2.24 反序列化分析

FastJson 库是 Java 的一个 Json 库,其作用是将 Java 对象转换成 json 数据来表示,也可以将 json 数据转换成 Java 对象。使用非常方便,号称是执行速度最快的库,看了一下使用的那个算法的分析,确实挺快的。

在 1.2.24 版本的 Fastjson 出现了一个反序列化的漏洞,本篇将写一下具体的利用和漏洞的分析,先写利用。

运行环境:

apache-tomcat-7.0.85
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)NGLIU.TTC,PMingLiUMSGOTHIC.TTC,MS UI GothicMALGUN.TTF,128,9
windows7

开始写之前吐槽一下 firefox 的新版 hackbar ,对于比较多的字节, POST 不过去,搞得我以为 payload 有问题,纠结了半天,结果用 burp 成功了。

将 FastJson 跑起来,打开是这样的。

Fastjson 1.2.24 反序列化分析  

然后我们将 POC 用 JavaC 编译成 class 文件并且转换成 base64 编码。

这里不要用 IDE 的编译器去编译,结果是不一样的,具体 javac 的编译器与 ide 的编译器的差异性,还没开始了解,已经加入日程。

编译后的 class 文件 base64 处理

Fastjson 1.2.24 反序列化分析

使用 burp 发一个 post 包 。

Fastjson 1.2.24 反序列化分析

然后接下来看看漏洞分析

我用 IDEA 导入了 war ,把网站架了起来,这里讲一下细节吧,主要是怕自己会忘。

#1 首先将 war 包放到 tomcat 的 webapp 目录下,让 tomcat 去解压出来,然后会看到网站正常打开。
#2 关掉 tomcat ,把解压出来的目录复制到另外一个盘下。
#3 用 IDEA 打开这个目录,创建 web.xml 文件 IDEA 会自动提醒你创建
#4 然后添加 SDK 的外部 lib 和 tomcat7.0 的 lib

根据 fastjson 官方对这个 bug 的修复,我们可以准确定位到相关类文件

https://github.com/alibaba/fastjson/commit/d52085ef54b32dfd963186e583cbcdfff5d101b5

Fastjson 1.2.24 反序列化分析

那么根据官方的修复,加了一个 autotype 的黑名单验证,如果反序列化类在黑名单里的,就禁止反序列化。

根据官方的修复方案,断点分别如下:

Fastjson 1.2.24 反序列化分析

Fastjson 1.2.24 反序列化分析

可以看到这里我们提交的 json 进来了,那么进来之后,要做的是 decode ,然后是反序列化,这样才行执行我们的恶意代码。

然后分析就陷入了僵局,很蛋疼,最后看了一下绿盟的师傅的分析,发现自己还是太菜了,直接在 exec 下断点,看调用栈发现三个方法

    public synchronized Properties getOutputProperties() {
        try {
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return null;
        }
    }

在这个类中,会调用 newTransofrmer ,看看 newTransofrmer 方法

public synchronized Transformer newTransformer()
        throws TransformerConfigurationException
    {
        TransformerImpl transformer;
 
        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);
 
        if (_uriResolver != null) {
            transformer.setURIResolver(_uriResolver);
        }
 
        if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
            transformer.setSecureProcessing(true);
        }
        return transformer;
    }

在这个方法看到了 getTransletInstance 方法,看看这个方法

private Translet getTransletInstance()
        throws TransformerConfigurationException {
        try {
            if (_name == null) return null;
 
            if (_class == null) defineTransletClasses();
 
            // The translet needs to keep a reference to all its auxiliary
            // class to prevent the GC from collecting them
            AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
            translet.postInitialization();
            translet.setTemplates(this);
            translet.setServicesMechnism(_useServicesMechanism);
            translet.setAllowedProtocols(_accessExternalStylesheet);
            if (_auxClasses != null) {
                translet.setAuxiliaryClasses(_auxClasses);
            }
 
            return translet;
        }
        catch (InstantiationException e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
        catch (IllegalAccessException e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
    }

发现这个方法比较牛逼,这个方法是 newinstance 的, new 完之后直接调用 exec 执行外部命令了,但是在这之前,要先调用 defineTransletClasses 方法,我们看看这个方法

private void defineTransletClasses()
        throws TransformerConfigurationException {
 
        if (_bytecodes == null) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
            throw new TransformerConfigurationException(err.toString());
        }
 
        TransletClassLoader loader = (TransletClassLoader)
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
                }
            });
 
        try {
            final int classCount = _bytecodes.length;
            _class = new Class[classCount];
 
            if (classCount > 1) {
                _auxClasses = new Hashtable();
            }
 
            for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);
                final Class superClass = _class[i].getSuperclass();
 
                // Check if this is the main class
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }
 
            if (_transletIndex < 0) {
                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
                throw new TransformerConfigurationException(err.toString());
            }
        }
        catch (ClassFormatError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
        catch (LinkageError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
    }

这个方法直接 new 了一个类,从 _bytecodes 里, _bytecodes 存放的是我们的 evilcode ,然后利用 ACC 去提升了一下权限, doPrivileged 的成功执行,然后 new 了一个类。

然后回到 getTransletInstance 方法,执行外部命令

Fastjson 1.2.24 反序列化分析

Fastjson 1.2.24 反序列化分析

Fastjson 1.2.24 反序列化分析

https://www.secpulse.com/archives/72391.html

原文  https://www.secpulse.com/archives/72391.html
正文到此结束
Loading...