转载

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...