RSS Feed
更好更安全的互联网
  • CVE-2019-5786 漏洞原理分析及利用

    2020-11-02

    作者:Kerne7@知道创宇404实验室
    时间:2020年6月29日

    从补丁发现漏洞本质

    首先根据谷歌博客收集相关CVE-2019-5786漏洞的资料:High CVE-2019-5786: Use-after-free in FileReader,得知是FileReader上的UAF漏洞。

    然后查看https://github.com/chromium/chromium/commit/ba9748e78ec7e9c0d594e7edf7b2c07ea2a90449?diff=split上的补丁

    对比补丁可以看到DOMArrayBuffer* result = DOMArrayBuffer::Create(raw_data_->ToArrayBuffer()),操作放到了判断finished_loading后面,返回值也从result变成了array_buffer_result_(result的拷贝)。猜测可能是这个返回值导致的问题。

    分析代码

    raw_data_->ToArrayBuffer()可能会返回内部buffer的拷贝,或者是返回一个指向其偏移buffer的指针。

    根据MDN中FileReader.readAsArrayBuffer()的描述:

    FileReader 接口提供的 readAsArrayBuffer() 方法用于启动读取指定的 Blob 或 File 内容。当读取操作完成时,readyState 变成 DONE(已完成),并触发 loadend 事件,同时 result 属性中将包含一个 ArrayBuffer 对象以表示所读取文件的数据。

    FileReader.onprogress事件在处理progress时被触发,当数据过大的时候,onprogress事件会被多次触发。

    所以在调用FileReader.result属性的时候,返回的是WTF::ArrayBufferBuilder创建的WTF::ArrayBuffer对象的指针,Blob未被读取完时,指向一个WTF::ArrayBuffer副本,在已经读取完的时候返回WTF::ArrayBufferBuilder创建的WTF::ArrayBuffer自身。

    那么在标志finished_loading被置为ture的时候可能已经加载完毕,所以onprogress和onloaded事件中返回的result就可能是同一个result。通过分配给一个worker来释放其中一个result指针就可以使另一个为悬挂指针,从而导致UAF漏洞。

    漏洞利用思路

    我选择的32位win7环境的Chrome72.0.3626.81版本,可以通过申请1GB的ArrayBuffer,使Chrome释放512MB保留内存,通过异常处理使OOM不会导致crash,然后在这512MB的内存上分配空间。

    调用FileReader.readAsArrayBuffer,将触发多个onprogress事件,如果事件的时间安排正确,则最后两个事件可以返回同一个ArrayBuffer。通过释放其中一个指针来释放ArrayBuffer那块内存,后面可以使用另一个悬挂指针来引用这块内存。然后通过将做好标记的JavaScript对象(散布在TypedArrays中)喷洒到堆中来填充释放的区域。

    通过悬挂的指针查找做好的标记。通过将任意对象的地址设置为找到的对象的属性,然后通过悬挂指针读取属性值,可以泄漏任意对象的地址。破坏喷涂的TypedArray的后备存储,并使用它来实现对地址空间的任意读写访问。

    之后可以加载WebAssembly模块会将64KiB的可读写执行存储区域映射到地址空间,这样的好处是可以免去绕过DEP或使用ROP链就可以执行shellcode。

    使用任意读取/写入原语遍历WebAssembly模块中导出的函数的JSFunction对象层次结构,以找到可读写可执行区域的地址。将WebAssembly函数的代码替换为shellcode,然后通过调用该函数来执行它。

    通过浏览器访问网页,就会导致执行任意代码

    帮助

    本人在初次调试浏览器的时候遇到了很多问题,在这里列举出一些问题来减少大家走的弯路。

    因为chrome是多进程模式,所以在调试的时候会有多个chrome进程,对于刚开始做浏览器漏洞那话会很迷茫不知道该调试那个进程或者怎么调试,可以通过chrome自带的任务管理器来帮我们锁定要附加调试的那个进程ID。

    这里新的标签页的进程ID就是我们在后面要附加的PID。

    Chrome调试的时候需要符号,这是google提供的符号服务器(加载符号的时候需要翻墙)。在windbg中,您可以使用以下命令将其添加到符号服务器搜索路径,其中c:\Symbols是本地缓存目录:

    因为Chrome的沙箱机制,在调试的过程中需要关闭沙箱才可以执行任意代码。可以在快捷方式中添加no-sandbox来关闭沙箱。

    由于这个漏洞机制的原因,可能不是每次都能执行成功,但是我们可以通过多次加载脚本的方式来达到稳定利用的目的。

    在github上有chromuim的源码,在分析源码的时候推荐使用sourcegraph这个插件,能够查看变量的定义和引用等。

    在需要特定版本Chrome的时候可以自己去build源码或者去网络上寻找chrome历代发行版收集的网站。

    在看exp和自己编写的时候需要注意v8引擎的指针问题,v8做了指针压缩,所以在内存中存访的指针可能和实际数据位置地址有出入。

    参考链接:

    1. https://www.anquanke.com/post/id/194351
    2. https://blog.exodusintel.com/2019/01/22/exploiting-the-magellan-bug-on-64-bit-chrome-desktop/
    3. https://blog.exodusintel.com/2019/03/20/cve-2019-5786-analysis-and-exploitation/

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1257/

    作者:江 | Categories:漏洞分析 | Tags:
  • Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 两次绕过

    2020-11-02

    作者: Badcode and Longofo@知道创宇404实验室 
    时间: 2020年2月9日 
    English Version:https://paper.seebug.org/1261/

    前言

    2019年9月初我们应急了Nexus Repository Manager 2.x 命令注入漏洞(CVE-2019-5475),其大致的原因和复现步骤在 hackerone 上公布了,在应急完这个漏洞之后,我们分析该漏洞的修复补丁,发现修复不完全,仍然可以绕过,本篇文章记录该漏洞的两次绕过。虽然早发布了两次的修复版本,由于官方第二次更新公告太慢https://support.sonatype.com/hc/en-us/articles/360033490774,所以现在才发。

    几次更新时间线:

    • CVE-2019-5475(2019-08-09)
    • 第一次绕过,CVE-2019-15588(2019-10-28)
    • 第二次绕过,未分配CVE,更新了公告影响版本(2020-3-25)

    注:原始漏洞分析、第一次绕过分析、第二次绕过分析部分主要由Badcode师傅编写,第二次绕过分析+、最新版本分析主要由Longofo添加。

    原始漏洞分析

    利用条件

    • 需管理员权限(默认认证:admin/admin123)

    漏洞分析

    以下分析的代码基于 2.14.9-01 版本。

    漏洞点是出现在 Yum Repository 插件中,当配置 Yum 的 createrepo或者mergerepo

    代码层面会跳到 YumCapabilityactivationCondition方法中。

    在上面Path of "createrepo"中设置的值会通过getConfig().getCreaterepoPath()获取到,获取到该值之后,调用this.validate()方法

    传进来的path是用户可控的,之后将path拼接--version之后传递给commandLineExecutor.exec()方法,看起来像是执行命令的方法,而事实也是如此。跟进CommandLineExecutor类的exec方法

    在执行命令前先对命令解析,CommandLine.parse(),会以空格作为分隔,获取可执行文件及参数。

    最终是调用了Runtime.getRuntime().exec()执行了命令。

    例如,用户传入的 command 是cmd.exe /c whoami,最后到getRuntime().exec()方法就是Runtime.getRuntime().exec({"cmd.exe","/c","whoami"})

    所以漏洞的原理也很简单,就是在createrepo或者mergerepo路径设置的时候,该路径可以由用户指定,中途拼接了--version字符串,最终到了getRuntime.exec()执行了命令。

    漏洞复现

    Path of "createrepo"里面传入 payload。

    Status栏可以看到执行的结果

    第一次绕过分析

    第一次补丁分析

    官方补丁改了几个地方,关键点在这里

    常规做法,在执行命令前对命令进行过滤。新增加了一个getCleanCommand()方法,对命令进行过滤。

    allowedExecutables是一个 HashSet,里面只有两个值,createrepomergerepo。先判断用户传入的command是否在allowedExecutables里面,如果在,直接拼接params--version直接返回。接着对用户传入的command进行路径判断,如果是以nexus的工作目录(applicationDirectories.getWorkDirectory().getAbsolutePath())开头的,直接返回 null。继续判断,如果文件名不在allowedExecutables则返回 null,也就是这条命令需要 以/createrepo或者/mergerepo结尾。都通过判断之后,文件的绝对路径拼接--version 返回。

    第一次补丁绕过

    说实话,看到这个补丁的第一眼,我就觉得大概率可以绕。

    传入的命令满足两个条件即可,不以nexus的工作目录开头,并且以/createrepo或者/mergerepo结尾即可。

    看到补丁中的getCleanCommand()方法,new File(command)是关键,new File()是通过将给定的路径名字符串转换为抽象路径名来创建新的File实例。 值得注意的是,这里面路径字符串是可以使用空格的,也就是

    这种是合法的,并且调用file.getName()取到的值是shadow。结合这个特性,就可以绕过补丁里面的判断。

    运行结果

    可以看到,file.getName()的值正是createrepo,满足判断。

    第一次绕过测试

    测试环境
    • 2.14.14-01 版本
    • Linux
    测试步骤

    Path of "createrepo"里面传入 payload。

    Status栏查看执行的结果

    可以看到,成功绕过了补丁。

    在 Windows 环境下面就麻烦点了,没有办法使用cmd.exe /c whoami这种形式执行命令了,因为cmd.exe /c whoami经过new File() 之后变成了cmd.exe \c whoami,后面是执行不了的。可以直接执行exe,注意后面是还会拼接--version的,所以很多命令是执行不了的,但是还是有办法利用能执行任意exe这点来做后续的攻击的。

    第二次绕过分析

    第二次补丁分析

    在我提交上述绕过方式后,官方修复了这种绕过方式,看下官方的补丁

    getCleanCommand() 方法中增加了一个file.exists()判断文件是否存在。之前的/bin/bash -c whoami /createrepo这种形式的肯定就不行了,因为这个文件并不存在。所以现在又多了一个判断,难度又加大了。难道就没有办法绕过了?不是的,还是可以绕过的。

    第二次补丁绕过

    现在传入的命令要满足三个条件了

    • 不以nexus的工作目录开头
    • /createrepo或者/mergerepo结尾
    • 并且这createrepo或者mergerepo这个文件存在

    看到file.exists()我就想起了 php 中的 file_exists(),以前搞 php 的时候也遇到过这种判断。有个系统特性,在 Windows 环境下,目录跳转是允许跳转不存在的目录的,而在Linux下面是不能跳转不存在目录的。

    测试一下

    Linux

    可以看到,file.exists()返回了 false

    Windows

    file.exists()返回了 true

    上面我们说了new File(pathname),pathname 是允许带空格的。在利用上面WIndows环境下的特性,把cmd设置成 C:\\Windows\\System32\\calc.exe \\..\\..\\win.ini

    经过parse() 方法,最终到getRuntime.exec({"C:\\Windows\\System32\\calc.exe","\\..\\..\\win.ini"}),这样就能执行calc了。

    在上面这个测试win.ini是确实存在的文件,回到补丁上面,需要判断createrepo或者mergerepo存在。首先从功能上来说,createrepo 命令用于创建 yum 源(软件仓库),即为存放于本地特定位置的众多rpm包建立索引,描述各包所需依赖信息,并形成元数据。也就是这个createrepo在Windows下不太可能存在。如果这个不存在的话是没有办法经过判断的。既然服务器内不存在createrepo,那就想办法创建一个,我首先试的是找个上传点,尝试上传一个createrepo,但是没找到上传之后名字还能保持不变的点。在Artifacts Upload处上传之后,都变成Artifact-Version.Packaging这种形式的名字了,Artifact-Version.Packaging这个是不满足第二个判断的,得以createrepo结尾。

    一开始看到file.exists()就走进了思维定势,以为是判断文件存在的,但是看了官方的文档,发现是判断文件或者目录存在的。。这点也就是这个漏洞形成的第二个关键点,我不能创建文件,但是可以创建文件夹啊。在Artifacts Upload上传Artifacts 的时候,可以通过GAV Parameters来定义。

    当 Group设置为test123Artifact设置为test123Version设置成1,当上传Artifacts的时候,是会在服务器中创建对应的目录的。对应的结构如下

    如果我们将Group设置为createrepo,那么就会创建对应的createrepo目录。

    结合两个特性来测试一下

    可以看到,file.exists()返回了true,file.getName()返回了createrepo,都符合判断了。

    最后到getRuntime()里面大概就是getRuntime.exec({"C:\Windows\System32\notepad.exe","\..\..\..\nexus\sonatype-work\nexus\storage\thirdparty\createrepo","--version"})

    是可以成功执行notepad.exe的。(calc.exe演示看不到进程哈,所以换成Notepad.exe)

    第二次绕过测试

    测试环境
    • 2.14.15-01 版本
    • Windows
    测试步骤

    Path of "createrepo"里面传入 payload。

    查看进程,notepad.exe启动了

    可以看到,成功绕过了补丁。

    第二次绕过分析+

    经过Badcode师傅第二次绕过分析,可以看到能成功在Windows系统执行命令了。但是有一个很大的限制:

    1. nexus需要安装在系统盘
    2. 一些带参数的命令无法使用

    在上面说到的Artifacts Upload上传处是可以上传任意文件的,并且上传后的文件名都是通过自定义的参数拼接得到,所以都能猜到。那么可以上传自己编写的任意exe文件了。

    第二次绕过分析+测试

    测试环境
    • 2.14.15-01 版本
    • Windows
    测试步骤

    导航到Views/Repositories->Repositories->3rd party->Configuration,我们可以看到默认本地存储位置的绝对路径(之后上传的内容也在这个目录下):

    导航到Views/Repositories->Repositories->3rd party->Artifact Upload,我们可以上传恶意的exe文件:

    该exe文件将被重命名为createrepo-1.exe(自定义的参数拼接的):

    同样在Path of "createrepo"里面传入 payload(这时需要注意前面部分这时是以nexus安装目录开头的,这在补丁中会判断,所以这里可以在最顶层加..\或者弄个虚假层aaa\..\等):

    可以看到createrepo-1.exe已经执行了:

    最新版本分析

    最新版本补丁分析

    第二次补丁绕过之后,官方又进行了修复,官方补丁主要如下:

    删除了之前的修复方式,增加了YumCapabilityUpdateValidator类,在validate中将获取的值与properties中设置的值使用equals进行绝对相等验证。这个值要修改只能通过sonatype-work/nexus/conf/capabilities.xml

    最新版本验证

    前端直接禁止修改了,通过抓包修改测试:

    YumCapabilityUpdateValidator.validate断到:

    可以看到这种修复方式无法再绕过了,除非有文件覆盖的地方覆盖配置文件,例如解压覆盖那种方式,不过没找到。

    不过Artifacts Upload那里可以上传任意文件的地方依然还在,如果其他地方再出现上面的情况依然可以利用到。


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1260/

    作者:江 | Categories:漏洞分析 | Tags:
  • Nexus Repository Manager 2.x command injection vulnerability (CVE-2019-5475) bypassed twice

    2020-11-02

    Author: Badcode and Longofo@Knownsec 404 Team
    Date: 2020/02/09
    Chinese Version: https://paper.seebug.org/1260/

    Foreword

    At the beginning of September 2019, we responded to the Nexus Repository Manager 2.x command injection vulnerability (CVE-2019-5475). The general reason and steps for recurrence are on Hackerone. It was announced that after emergency response to this vulnerability, we analyzed the patch to fix the vulnerability and found that the repair was incomplete and could still be bypassed. This article records two bypasses of the vulnerability. Although the fix version was released twice early, the official second update announcement is too slow https://support.sonatype.com/hc/en-us/articles/360033490774, so now we post this article.

    The timeline:

    • CVE-2019-5475(2019-08-09)
    • Bypassed for the first time, CVE-2019-15588 (2019-10-28)
    • Bypassed for the second time, CVE was not assigned, and the bulletin impact version was updated (2020-3-25)

    Note: The original vulnerability analysis, the first bypass analysis, and the second bypass analysis were mainly written by Badcode, the second bypass analysis+, and the latest version analysis was mainly added by Longofo.

    Original vulnerability analysis

    Conditions of use

    • Requires administrator rights (default authentication: admin/admin123)

    Vulnerability analysis

    The code analyzed below is based on version 2.14.9-01.

    The vulnerability is in the Yum Repository plugin, when configuring Yum's createrepo or mergerepo

    img

    The code level will jump toYumCapabilitactivationConditionmethod:

    img

    The value set in Path of "createrepo" above will be obtained through getConfig().getCreaterepoPath(). After obtaining this value, call the this.validate() method on Path of "createrepo". The value set in will be obtained through getConfig().getCreaterepoPath(). After obtaining this value, call the this.validate() method

    img

    The path passed in is user-controllable, and then the path splicing --version is then passed to the commandLineExecutor.exec() method, which looks like a method of executing commands, and this is also the case. Follow up the exec method of the CommandLineExecutor class

    img

    Parse the command before executing the command. CommandLine.parse() will use spaces as separators to obtain executable files and parameters. Eventually, the call to Runtime.getRuntime().exec() executed the command. For example, the command passed by the user is cmd.exe /c whoami, and finally the method to getRuntime().exec() is Runtime.getRuntime().exec({"cmd.exe","/c" ,"whoami"}). So the principle of the vulnerability is also very simple, that is, when the createrepo or mergerepo path is set, the path can be specified by the user, the --version string is spliced halfway, and finally it is executed at getRuntime.exec() Order.

    Vulnerability reproduction

    Pass the payload in Path of "createrepo".

    img

    You can see the execution result in the Status column

    img

    Bypass analysis for the first time

    First patch analysis

    The official patch has changed a few places, the key point is here

    img

    It is common practice to filter commands before executing them. A new getCleanCommand()method has been added to filter commands.

    img

    allowedExecutables is a HashSet with only two values, createrepo and mergerepo. First determine whether the command passed in by the user is in allowedExecutables, if so, directly splice paramsie --version and return directly. Then determine the path of the command passed in by the user. If it starts with the working directory of nexus (applicationDirectories.getWorkDirectory().getAbsolutePath()), return null directly. Continue to judge, if the file name is not in allowedExecutables then return null, that is, this command needs to end with /createrepo or /mergerepo. After passing the judgment, the absolute path of the file is concatenated and returned by --version.

    First patch bypass

    To be honest, at the first glance at this patch, I felt that there was a high probability that it would be around.

    The incoming command only needs to meet two conditions, not beginning with nexus' working directory, and ending with /createrepo or /mergerepo.

    Seeing the getCleanCommand() method in the patch, new File(command) is the key, and new File()is to create a new File instance by converting the given pathname string into an abstract pathname. It is worth noting that spaces can be used in the path string, which is

    This is legal, and the value obtained by calling file.getName() is shadow. Combined with this feature, you can bypass the judgment in the patch.

    operation result

    img

    It can be seen that the value of file.getName() is exactly createrepo, which satisfies the judgment.

    Bypassing the test for the first time

    Test environment
    • 2.14.14-01 version
    • Linux
    Test procedure

    Pass the payload in Path of "createrepo".

    img

    Check the execution result in the Status column

    img

    As you can see, the patch was successfully bypassed.

    Under the Windows environment, it is a little troublesome. There is no way to execute commands in the form of cmd.exe /c whoami, because cmd.exe /c whoami becomes cmd.exe \c whoami after new File() , which cannot be executed later. You can directly execute the exe. Note that --version will also be spliced later, so many commands cannot be executed, but there is still a way to make use of the ability to execute any exe to carry out subsequent attacks.

    Second bypass analysis

    Second patch analysis

    After I submitted the above bypass method, the official fixed this bypass method, see the official patch

    img

    Added a file.exists() method in the getCleanCommand() method to determine whether the file exists. The previous form of /bin/bash -c whoami /createrepo would definitely not work, because this file does not exist. So now there is another judgment, and the difficulty has increased. Is there no way to bypass it? No, it can still be bypassed.

    Second patch bypass

    Now the incoming command has to meet three conditions

    • Does not start with nexus' working directory
    • End with /createrepo or /mergerepo
    • And this file createrepo or mergerepo exists

    Seeing file.exists(), I remembered file_exists() in php. I also encountered this kind of judgment when I was doing php before. There is a system feature. In the Windows environment, directory jumps are allowed to jump to non-existing directories, while under Linux, you cannot jump to non-existing directories.

    have a test

    Linux

    img

    As you can see, file.exists() returned false

    Windows

    img

    file.exists() returned true

    Above we said new File(pathname), pathname is allowed with spaces. Using the features of the above WIndows environment, set cmd to C:\\Windows\\System32\\calc.exe \\..\\..\\win.ini

    img

    After the parse() method, finally getRuntime.exec({"C:\\Windows\\System32\\calc.exe","\\..\\..\\win.ini"}) , So that you can execute calc.

    In the above test, "win.ini" is a file that does exist. Returning to the patch, you need to determine whether createrepo or mergerepo exists. First of all, from a functional point of view, the createrepo command is used to create a yum source (software repository), that is, to index many rpm packages stored in a specific local location, describe the dependency information required by each package, and form metadata. That is, this createrepo is unlikely to exist under Windows. If this does not exist, there is no way to judge. Since createrepo does not exist on the server, I will try to create one. I first tried to find an upload point and tried to upload a createrepo, but I didn't find a point where the name would remain unchanged after uploading. After uploading at Artifacts Upload, it becomes the name of the form Artifact-Version.PackagingArtifact-Version.Packaging does not satisfy the second judgment and ends with createrepo.

    At the beginning, when I saw file.exists(), I entered the mindset, thinking that it was judged that the file exists, but after reading the official documentation, I found that the file or directory exists. This is the second key point caused by this vulnerability. I can't create files, but I can create folders. When uploading Artifacts in Artifacts Upload, it can be defined by GAV Parameters.

    img

    When Group is set to test123Artifact is set to test123, and Version is set to 1, when uploading Artifacts, the corresponding directory will be created in the server. The corresponding structure is as follows

    img

    If we set Group to createrepo, then the corresponding createrepo directory will be created.

    Combine two features to test

    img

    As you can see, file.exists() returned true, and file.getName() returned createrepo, both of which met the judgment.

    Finally, in getRuntime(), it is probably

    getRuntime.exec({"C:\Windows\System32\notepad.exe","\..\..\..\nexus\sonatype-work\nexus\storage\thirdparty\createrepo","--version"})

    Can successfully execute notepad.exe. (The calc.exe demo cannot see the process, so replace it with Notepad.exe)

    Second bypass test

    Test environment
    • 2.14.15-01 version
    • Windows
    Test procedure

    Pass the payload in Path of "createrepo".

    img

    View the process, notepad.exe started

    img

    As you can see, the patch was successfully bypassed.

    Second bypass analysis+

    After the second bypass analysis by @Badcode, you can see that you can successfully execute commands on the Windows system. But there is a big limitation:

    1. nexus needs to be installed on the system disk
    2. Some commands with parameters cannot be used

    The above-mentioned "Artifacts Upload" upload location can upload any file, and the uploaded file name is obtained by splicing with custom parameters, so you can guess. Then you can upload any exe file you wrote.

    Second bypass analysis + test

    Test environment
    • 2.14.15-01 version
    • Windows
    Test procedure

    Navigate to Views/Repositories->Repositories->3rd party->Configuration, we can see the absolute path of default local storage location (the content uploaded later is also in this directory):

    Navigate to Views/Repositories->Repositories->3rd party->Artifact Upload, we can upload malicious exe files:

    The exe file will be renamed to createrepo-1.exe (spliced by custom parameters):

    Also pass the payload into Path of "createrepo" (at this time, please note that the previous part starts with the nexus installation directory, which will be judged in the patch, so you can add ..\ at the top level or Get a false layer aaa\..\ etc.):

    You can see that createrepo-1.exe has been executed:

    Latest version analysis

    Latest version patch analysis

    After the second patch was bypassed, the official fixed it again. The official patch is as follows:

    Removed the previous repair method and added the YumCapabilityUpdateValidator class. In validate, the obtained value and the value set in the properties are verified using absolutes for equal equality. This value can only be modified through sonatype-work/nexus/conf/capabilities.xml:

    Latest version verification

    The front end is directly prohibited from modification, and the test is modified by capturing packets:

    In YumCapabilityUpdateValidator.validate breaks to:

    It can be seen that this repair method can no longer be bypassed, unless the configuration file is overwritten by the file coverage, such as decompression and overwriting, but I was not found.

    However, the place where Artifacts Upload can upload arbitrary files is still there. If the above situation appears in other places, it can still be used.


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1261/

    作者:江 | Categories:技术分享 | Tags:
  • From Deserialization to Type Confusion Vulnerability —— A Real Use in Ecshop Lastest

    2020-11-02

    Author: LoRexxar'@Knownsec 404 Team
    Date: March 31,2020.
    Chinese Version: https://paper.seebug.org/1267

    This article was originally completed on March 31, 2020. Because it involves 0day utilization, it was reported to the vendor on March 31, 2020, and released after the 90-day vulnerability disclosure period.


    A few days ago, I accidentally saw a vulnerability report submitted on Hackerone. In this vulnerability, the vulnerability discoverer proposed a very interesting use. The author makes use of a type confusion vulnerability of GMP and cooperates with the corresponding utilization chain to construct a code execution of mybb. Here we take a look at this vulnerability.

    https://hackerone.com/reports/198734

    Some details of the following article, thanks to the vulnerability discoverer @taoguangchen for his help.

    GMP type confusion vulnerability

    -https://bugs.php.net/bug.php?id=70513

    Vulnerability conditions

    • php 5.6.x
    • Deserialization entry point
    • The trigger point that can trigger __wakeup (below php < 5.6.11, you can use the built-in class)

    Vulnerability details

    gmp.c

    zend_object_handlers.c

    From the snippet in gmp.c, we can roughly understand the original words of vulnerability discoverer taoguangchen.

    Magic methods such as __wakeup can cause ZVAL to be modified in memory. Therefore, an attacker can convert **object to an integer or bool type ZVAL, then we can access any object stored in the object storage through Z_OBJ_P, which means that any object can be overwritten through zend_hash_copy attributes, which may cause a lot of problems, and can also cause security problems in certain scenarios.

    Perhaps it is impossible to understand the above words only with code snippets, but we can take a look at the actual test.

    First let's look at a test code

    In the code, I show the environment in many different situations.

    Let's see what the result is?

    I successfully modified the first declared object.

    But what happens if I change the deserialized class to b?

    Obviously, it will not affect other class variables

    If we add a __Wakeup function to class b, then it will produce the same effect.

    But if we set the variable in the wakeup magic method to 2

    The results returned can be seen, we successfully modified the second declared object.

    But if we change ryat to 4, then the page will return 500 directly, because we modified the unallocated object space.

    After completing the previous experiments, we can simplify the conditions for exploiting the vulnerability.

    If we have a controllable deserialization entry, the target backend PHP has a GMP plugin installed (this plugin is not installed by default in the original PHP, but some packaging environments will bring it), If we find a controllable __wakeup magic method, we can modify the object properties declared before deserialization and cooperate with the scene to produce actual security problems.

    If the target php version is in 5.6 <= 5.6.11, we can directly use the built-in magic method to trigger this vulnerability.

    Real world case

    After discussing the GMP type confusion vulnerability, we must discuss how this vulnerability is used in real scenarios.

    Taoguang Chen, the discoverer of the vulnerability, submitted a related exploit in mybb.

    https://hackerone.com/reports/198734

    Here we do not continue to discuss this vulnerability, but discuss the use in ecshop from scratch.

    Vulnerable Environment

    • ecshop 4.0.7
    • php 5.6.9

    Deserialization Vulnerability

    First we need to find an entry point for deserialization. Here we can search for unserializeglobally. Looking at each of them, we can find two controllable deserialization entries.

    One of them is search.php line 45

    This is an entrance to the front desk, but unfortunately, the initialization file is introduced after deserialization, which also makes us unable to find a target that can override the properties of class variables, and there is no way to use it further.

    Another one is admin/order.php line 229

    This function of the form page in the background meets our requirements. Not only can it be controlled, but also urlencode can be used to bypass ecshop's filtering of global variables.

    In this way, we have found a controllable and suitable deserialization entry point.

    Find the appropriate class attribute utilization chain

    Before looking for a utilization chain, we can use

    To determine the class that has been declared when deserializing.

    In my local environment, I found 13 classes in addition to the PHP built-in classes

    You can also see from the code that multiple library files are imported in the file header

    Here we mainly focus on init.php, because most common classes of ecshop are declared in this file.

    When looking at the class variables one by one, we can keenly see a special variable. Due to the special background structure of ecshop, most of the page content is compiled from templates, and this template class happens to be in init.php. statement

    Back in order.php, we are looking for methods related to $smarty, it is not difficult to find, mainly concentrated in two methods

    Here we mainly focus on the display method.

    A rough look at the logic of the display method:

    The more important code will be defined in the function make_compiled

    When the process reaches this point, we need to find out what our goal is first?

    Re-examining the code of cls_template.php, we can find that there are only a few functions involved in the code execution.

    get_para is only called in select, but there is no place to trigger the select.

    Then pop_vars

    We can control the $this->_temp_key variable just in conjunction with GMP, so as long as we can call this method anywhere in the above process, we can construct a code execution with variable coverage.

    Looking back at the code flow just now, we found such code from the compiled PHP file

    order_info.htm.php

    After traversing the form, pop_vars will be triggered.

    In this way, as long as we control the _temp_key attribute that overrides the cls_templatevariable, we can complete a getshell

    Final use effect

    Timeline

    • 2020.03.31 Find the vulnerability.
    • 2020.03.31 Report the vulnerability to the manufacturer, CVE, CNVD, etc.
    • 2020.07.08 Announce details after 90-day vulnerability disclosure period.

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1268/

    作者:江 | Categories:漏洞分析 | Tags:
  • Fastjson 反序列化漏洞史

    2020-11-02

    作者:Longofo@知道创宇404实验室 
    时间:2020年4月27日 
    英文版本:https://paper.seebug.org/1193/

    Fastjson没有cve编号,不太好查找时间线,一开始也不知道咋写,不过还是慢慢写出点东西,幸好fastjson开源以及有师傅们的一路辛勤记录。文中将给出与Fastjson漏洞相关的比较关键的更新以及漏洞时间线,会对一些比较经典的漏洞进行测试及修复说明,给出一些探测payload,rce payload。

    Fastjson解析流程

    可以参考下@Lucifaer师傅写的fastjson流程分析,这里不写了,再写篇幅就占用很大了。文中提到fastjson有使用ASM生成的字节码,由于实际使用中很多类都不是原生类,fastjson序列化/反序列化大多数类时都会用ASM处理,如果好奇想查看生成的字节码,可以用idea动态调试时保存字节文件:

    插入的代码为:

    生成的类:

    但是这个类并不能用于调试,因为fastjson中用ASM生成的代码没有linenumber、trace等用于调试的信息,所以不能调试。不过通过在Expression那个窗口重写部分代码,生成可用于调式的bytecode应该也是可行的(我没有测试,如果有时间和兴趣,可以看下ASM怎么生成可用于调试的字节码)。

    Fastjson 样例测试

    首先用多个版本测试下面这个例子:

    说明

    • 这里的@type就是对应常说的autotype功能,简单理解为fastjson会自动将json的key:value值映射到@type对应的类中
    • 样例User类的几个方法都是比较普通的方法,命名、返回值也都是常规的符合bean要求的写法,所以下面的样例测试有的特殊调用不会覆盖到,但是在漏洞分析中,可以看到一些特殊的情况
    • parse用了四种写法,四种写法都能造成危害(不过实际到底能不能利用,还得看版本和用户是否打开了某些配置开关,具体往后看)
    • 样例测试都使用jdk8u102,代码都是拉的源码测,主要是用样例说明autotype的默认开启、checkautotype的出现、以及黑白名白名单从哪个版本开始出现的过程以及增强手段
    1.1.157测试

    这应该是最原始的版本了(tag最早是这个),结果:

    下面对每个结果做一个简单的说明

    JSON.parse(serializedStr)

    在指定了@type的情况下,自动调用了User类默认构造器,User类对应的setter方法(setAge,setName),最终结果是User类的一个实例,不过值得注意的是public sex被成功赋值了,private address没有成功赋值,不过在1.2.22, 1.1.54.android之后,增加了一个SupportNonPublicField特性,如果使用了这个特性,那么private address就算没有setter、getter也能成功赋值,这个特性也与后面的一个漏洞有关。注意默认构造方法、setter方法调用顺序,默认构造器在前,此时属性值还没有被赋值,所以即使默认构造器中存在危险方法,但是危害值还没有被传入,所以默认构造器按理来说不会成为漏洞利用方法,不过对于内部类那种,外部类先初始化了自己的某些属性值,但是内部类默认构造器使用了父类的属性的某些值,依然可能造成危害。

    可以看出,从最原始的版本就开始有autotype功能了,并且autotype默认开启。同时ParserConfig类中还没有黑名单。

    JSON.parseObject(serializedStr)

    在指定了@type的情况下,自动调用了User类默认构造器,User类对应的setter方法(setAge,setName)以及对应的getter方法(getAge,getName),最终结果是一个字符串。这里还多调用了getter(注意bool类型的是is开头的)方法,是因为parseObject在没有其他参数时,调用了JSON.toJSON(obj),后续会通过gettter方法获取obj属性值:

    JSON.parseObject(serializedStr, Object.class)

    在指定了@type的情况下,这种写法和第一种JSON.parse(serializedStr)写法其实没有区别的,从结果也能看出。

    JSON.parseObject(serializedStr, User.class)

    在指定了@type的情况下,自动调用了User类默认构造器,User类对应的setter方法(setAge,setName),最终结果是User类的一个实例。这种写法明确指定了目标对象必须是User类型,如果@type对应的类型不是User类型或其子类,将抛出不匹配异常,但是,就算指定了特定的类型,依然有方式在类型匹配之前来触发漏洞。

    1.2.10测试

    对于上面User这个类,测试结果和1.1.157一样,这里不写了。

    到这个版本autotype依然默认开启。不过从这个版本开始,fastjson在ParserConfig中加入了denyList,一直到1.2.24版本,这个denyList都只有一个类(不过这个java.lang.Thread不是用于漏洞利用的):

    1.2.25测试

    测试结果是抛出出了异常:

    从1.2.25开始,autotype默认关闭了,对于autotype开启,后面漏洞分析会涉及到。并且从1.2.25开始,增加了checkAutoType函数,它的主要作用是检测@type指定的类是否在白名单、黑名单(使用的startswith方式)

    以及目标类是否是两个危险类(Classloader、DataSource)的子类或者子接口,其中白名单优先级最高,白名单如果允许就不检测黑名单与危险类,否则继续检测黑名单与危险类:

    增加了黑名单类、包数量,同时增加了白名单,用户还可以调用相关方法添加黑名单/白名单到列表中:

    后面的许多漏洞都是对checkAutotype以及本身某些逻辑缺陷导致的漏洞进行修复,以及黑名单的不断增加。

    1.2.42测试

    与1.2.25一样,默认不开启autotype,所以结果一样,直接抛autotype未开启异常。

    从这个版本开始,将denyList、acceptList换成了十进制的hashcode,使得安全研究难度变大了(不过hashcode的计算方法依然是公开的,假如拥有大量的jar包,例如maven仓库可以爬jar包下来,可批量的跑类名、包名,不过对于黑名单是包名的情况,要找到具体可利用的类也会消耗一些时间):

    checkAutotype中检测也做了相应的修改:

    1.2.61测试

    与1.2.25一样,默认不开启autotype,所以结果一样,直接抛autotype未开启异常。

    从1.2.25到1.2.61之前其实还发生了很多绕过与黑名单的增加,不过这部分在后面的漏洞版本线在具体写,这里写1.2.61版本主要是说明黑名单防御所做的手段。在1.2.61版本时,fastjson将hashcode从十进制换成了十六进制:

    不过用十六进制表示与十进制表示都一样,同样可以批量跑jar包。在1.2.62版本为了统一又把十六进制大写:

    再之后的版本就是黑名单的增加了

    Fastjson漏洞版本线

    下面漏洞不会过多的分析,太多了,只会简单说明下以及给出payload进行测试与说明修复方式。

    ver<=1.2.24

    从上面的测试中可以看到,1.2.24及之前没有任何防御,并且autotype默认开启,下面给出那会比较经典的几个payload。

    com.sun.rowset.JdbcRowSetImpl利用链

    payload:

    测试(jdk=8u102,fastjson=1.2.24):

    结果:

    触发原因简析:

    JdbcRowSetImpl对象恢复->setDataSourceName方法调用->setAutocommit方法调用->context.lookup(datasourceName)调用

    com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl利用链

    payload:

    测试(jdk=8u102,fastjson=1.2.24):

    结果:

    触发原因简析:

    TemplatesImpl对象恢复->JavaBeanDeserializer.deserialze->FieldDeserializer.setValue->TemplatesImpl.getOutputProperties->TemplatesImpl.newTransformer->TemplatesImpl.getTransletInstance->通过defineTransletClasses,newInstance触发我们自己构造的class的静态代码块

    简单说明:

    这个漏洞需要开启SupportNonPublicField特性,这在样例测试中也说到了。因为TemplatesImpl类中_bytecodes_tfactory_name_outputProperties_class并没有对应的setter,所以要为这些private属性赋值,就需要开启SupportNonPublicField特性。具体这个poc构造过程,这里不分析了,可以看下廖大师傅的这篇,涉及到了一些细节问题。

    ver>=1.2.25&ver<=1.2.41

    1.2.24之前没有autotype的限制,从1.2.25开始默认关闭了autotype支持,并且加入了checkAutotype,加入了黑名单+白名单来防御autotype开启的情况。在1.2.25到1.2.41之间,发生了一次checkAutotype的绕过。

    下面是checkAutoType代码:

    在上面做了四个位置标记,因为后面几次绕过也与这几处位置有关。这一次的绕过是走过了前面的1,2,3成功进入位置4加载目标类。位置4 loadclass如下:

    去掉了className前后的L;,形如Lcom.lang.Thread;这种表示方法和JVM中类的表示方法是类似的,fastjson对这种表示方式做了处理。而之前的黑名单检测都是startswith检测的,所以可给@type指定的类前后加上L;来绕过黑名单检测。

    这里用上面的JdbcRowSetImpl利用链:

    测试(jdk8u102,fastjson 1.2.41):

    结果:

    ver=1.2.42

    在1.2.42对1.2.25~1.2.41的checkAutotype绕过进行了修复,将黑名单改成了十进制,对checkAutotype检测也做了相应变化:

    黑名单改成了十进制,检测也进行了相应hash运算。不过和上面1.2.25中的检测过程还是一致的,只是把startswith这种检测换成了hash运算这种检测。对于1.2.25~1.2.41的checkAutotype绕过的修复,就是红框处,判断了className前后是不是L;,如果是,就截取第二个字符和到倒数第二个字符。所以1.2.42版本的checkAutotype绕过就是前后双写LL;;,截取之后过程就和1.2.25~1.2.41版本利用方式一样了。

    用上面的JdbcRowSetImpl利用链:

    测试(jdk8u102,fastjson 1.2.42):

    结果:

    ver=1.2.43

    1.2.43对于1.2.42的绕过修复方式:

    在第一个if条件之下(L开头,;结尾),又加了一个以LL开头的条件,如果第一个条件满足并且以LL开头,直接抛异常。所以这种修复方式没法在绕过了。但是上面的loadclass除了L;做了特殊处理外,[也被特殊处理了,又再次绕过了checkAutoType:

    用上面的JdbcRowSetImpl利用链:

    测试(jdk8u102,fastjson 1.2.43):

    结果:

    ver=1.2.44

    1.2.44版本修复了1.2.43绕过,处理了[

    删除了之前的L开头、;结尾、LL开头的判断,改成了[开头就抛异常,;结尾也抛异常,所以这样写之前的几次绕过都修复了。

    ver>=1.2.45&ver<1.2.46

    这两个版本期间就是增加黑名单,没有发生checkAutotype绕过。黑名单中有几个payload在后面的RCE Payload给出,这里就不写了

    ver=1.2.47

    这个版本发生了不开启autotype情况下能利用成功的绕过。解析一下这次的绕过:

    1. 利用到了java.lang.class,这个类不在黑名单,所以checkAutotype可以过
    2. 这个java.lang.class类对应的deserializer为MiscCodec,deserialize时会取json串中的val值并load这个val对应的class,如果fastjson cache为true,就会缓存这个val对应的class到全局map中
    3. 如果再次加载val名称的class,并且autotype没开启(因为开启了会先检测黑白名单,所以这个漏洞开启了反而不成功),下一步就是会尝试从全局map中获取这个class,如果获取到了,直接返回

    这个漏洞分析已经很多了,具体详情可以参考下这篇

    payload:

    测试(jdk8u102,fastjson 1.2.47):

    结果:

    ver>=1.2.48&ver<=1.2.68

    在1.2.48修复了1.2.47的绕过,在MiscCodec,处理Class类的地方,设置了cache为false:

    在1.2.48到最新版本1.2.68之间,都是增加黑名单类。

    ver=1.2.68

    1.2.68是目前最新版,在1.2.68引入了safemode,打开safemode时,@type这个specialkey完全无用,无论白名单和黑名单,都不支持autoType了。

    在这个版本中,除了增加黑名单,还减掉一个黑名单:

    这个减掉的黑名单,不知道有师傅跑出来没,是个包名还是类名,然后能不能用于恶意利用,反正有点奇怪。

    探测Fastjson

    比较常用的探测Fastjson是用dnslog方式,探测到了再用RCE Payload去一个一个打。同事说让搞个能回显的放扫描器扫描,不过目标容器/框架不一样,回显方式也会不一样,这有点为难了...,还是用dnslog吧。

    dnslog探测

    目前fastjson探测比较通用的就是dnslog方式去探测,其中Inet4Address、Inet6Address直到1.2.67都可用。下面给出一些看到的payload(结合了上面的rand:{}这种方式,比较通用些):

    一些RCE Payload

    之前没有收集关于fastjson的payload,没有去跑jar包....,下面列出了网络上流传的payload以及从marshalsec中扣了一些并改造成适用于fastjson的payload,每个payload适用的jdk版本、fastjson版本就不一一测试写了,这一通测下来都不知道要花多少时间,实际利用基本无法知道版本、autotype开了没、用户咋配置的、用户自己设置又加了黑名单/白名单没,所以将构造的Payload一一过去打就行了,基础payload:

    下面是个小脚本,可以将基础payload转出各种绕过的变形态,还增加了\u\x编码形式:

    例如JdbcRowSetImpl结果:

    有些师傅也通过扫描maven仓库包来寻找符合jackson、fastjson的恶意利用类,似乎大多数都是在寻找jndi类型的漏洞。对于跑黑名单,可以看下这个项目,跑到1.2.62版本了,跑出来了大多数黑名单,不过很多都是包,具体哪个类还得去包中一一寻找。

    参考链接

    1. https://paper.seebug.org/994/#0x03
    2. https://paper.seebug.org/1155/
    3. https://paper.seebug.org/994/
    4. https://paper.seebug.org/292/
    5. https://paper.seebug.org/636/
    6. https://www.anquanke.com/post/id/182140#h2-1
    7. https://github.com/LeadroyaL/fastjson-blacklist
    8. http://www.lmxspace.com/2019/06/29/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/#v1-2-47
    9. http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/
    10. http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
    11. http://gv7.me/articles/2020/several-ways-to-detect-fastjson-through-dnslog/#0x03-%E6%96%B9%E6%B3%95%E4%BA%8C-%E5%88%A9%E7%94%A8java-net-InetSocketAddress
    12. https://xz.aliyun.com/t/7027#toc-4
    13. https://zhuanlan.zhihu.com/p/99075925
    14. ...

    太多了,感谢师傅们的辛勤记录。

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1192/

    作者:江 | Categories:漏洞分析 | Tags:
  • Fastjson Deserialization Vulnerability History

    2020-11-02

    Author:Longofo@Knownsec 404 Team 
    Time: April 27, 2020 
    Chinese version:https://paper.seebug.org/1192/

    Fastjson doesn't have a cve number, so it's difficult to find the timeline. At first,I wrote something slowly. Fortunately, fastjson is open source and there are hard work records of other security researchers. This article will give the key updates and vulnerability timelines related to Fastjson and the vulnerabilities,I will test and explain some of the more classic vulnerabilities, and give some check payloads and rce payloads.

    Fastjson Parsing Process

    You can refer to fastjson process analysis written by @Lucifaer. I will not write it here, and it will occupy a lot of space. In this article said that fastjson has byte code generated using ASM. Since many classes are not native in actual use, fastjson serializes/deserializes most classes will be processed by ASM. You can use idea to save byte files during dynamic debugging:

    The inserted code is:

    Generated class:

    But this class cannot be used for debugging, because the code generated by ASM in fastjson does not have linenumber, trace and other information.However, it should be feasible to generate bytecode by rewriting part of the code in the Expression window.(I have not tested it. If you have enough time or interest, you can see how ASM generates bytecode that can be used for debugging).

    Fastjson Demo Test

    First test the following example with multiple versions:

    Tips:

    • @Type here corresponds to the commonly autotype function , simply understood that fastjson will automatically map the value of key: value of json to the class corresponding to @type.
    • Several methods of the sample User class are relatively common methods, the naming and return values are all conventionally written in accordance with the requirements of the bean, so some special calls in the following sample test will not be covered, but in the vulnerability analysis , We can see some special cases.
    • Parse uses four types of writing, all of which can cause harm (however, whether it can actually be used depends on the version and whether the user has turned on certain configuration switches, see later).
    • The sample tests all use jdk8u102, and the code is the source code test. It mainly uses samples to explain the process of autotype default opening, the appearance of checkautotype, and the version of the black list and white list from which it appears and enhancement methods.
    1.1.157 Test

    This should be the original version (the earliest tag is this), the result:

    Below is a brief explanation of each result.

    JSON.parse(serializedStr)

    When @type is specified, the default constructor of the User class is automatically called. The setter method(setAge, setName) corresponding to the User class is the final result. It is an instance of the User class, but it is worth noting that the public sex is successfully assigned,while private address is not successfully assigned, but after 1.2.22, 1.1.54.android, a SupportNonPublicField feature is added. If this feature used, private address can be successfully assigned even without setter, getter, this feature is also related to a later vulnerability. Pay attention to the order of the default constructor and setter method. The default constructor comes first. At this time, the property value has not been assigned, so even though there are dangerous methods in the default constructor, the harmful value has not been passed in.The default constructor is logical and will not be a method of exploit, but for the inner class, the outer class first initializes some of its own attribute values, but the inner class default constructor uses some values of the attributes of the parent class, which may still cause harm.

    It can be seen that the autotype function has been available since the original version, and autotype is enabled by default. At the same time, there is no blacklist in the ParserConfig class.

    JSON.parseObject(serializedStr)

    When @type is specified, the default constructor of the User class is automatically called, the setter method (setAge, setName) corresponding to the User class and the corresponding getter method (getAge, getName), and the final result is a string. There are more getter methods (note that the bool type starts with is), because parseObject calls JSON.toJSON (obj) when there are no other parameters, and the obj property value will be obtained through the gettter method later:

    JSON.parseObject(serializedStr, Object.class)

    When @type is specified, there is no difference between this type of writing and the first type of JSON.parse(serializedStr).

    JSON.parseObject(serializedStr, User.class)

    When @type is specified, the default constructor of the User class is automatically called, the setter method (setAge, setName) corresponding to the User class, and the final result is an instance of the User class. This way of writing clearly specifies that the target object must be of type User. If the type corresponding to @type is not User or its subclass, a mismatch exception will be thrown. However, even if a specific type is specified, there is still a way before the type matches To trigger the vulnerability.

    1.2.10 Test

    For the above User class, the test result is the same as 1.1.157, so I won't write it here.

    In this version, autotype is still enabled by default. However, from this version, fastjson added denyList in ParserConfig, until version 1.2.24, this denyList has only one class (however, this java.lang.Thread is not used for exploits):

    1.2.25 Test

    The test result is that an exception is thrown:

    Starting from 1.2.25, autotype is turned off by default. For autotype to be turned on, later vulnerability analysis will be involved. And from 1.2.25, the checkAutoType function is added. Its main function is to detect whether the class specified by @type is in the white list or black list (using the startswith method)

    And whether the target class is a subclass or subinterface of two dangerous classes (Classloader, DataSource), where the whitelist has the highest priority, and the whitelist does not detect blacklists and dangerous classes if allowed, otherwise it continues to detect blacklists and dangerous classes:

    The number of blacklist classes and packages has been increased, and the whitelist has also been added. Users can also call related methods to add blacklist/whitelist to the list:

    Many of the latter vulnerabilities are due to the repair of checkautotype and some of its own logical defects, as well as the increasing blacklist.

    1.2.42 Test

    As with 1.2.25, autotype is not enabled by default, so the result is the same, directly throwing the exception that autotype is not enabled.

    From this version, the denyList and acceptList have been replaced with decimal hashcode, which makes the security research more difficult (however, the calculation method of hashcode is still public. If you have a large number of jar packages, such as maven warehouse, you can crawl the jar package, run the class name and package name in batches.But if the blacklist is the package name, it will take some time to find the specific available class):

    The detection in checkAutotype has also been modified accordingly:

    1.2.61 Test

    As the 1.2.25, autotype is not enabled by default, so the result is same, directly throwing the exception that autotype is not enabled.

    From 1.2.25 to 1.2.61, a lot of bypasses and blacklists have actually been added, but this part of the vulnerability version line is written specifically. The 1.2.61 version is written here mainly to illustrate the blacklist defens means. In version 1.2.61, fastjson changed the hashcode from decimal to hexadecimal:

    However, the hexadecimal representation is the same as the decimal representation, and jar packages can also be run in batches. In version 1.2.62, hex capital was added for uniformity:

    The later version is the increase of blacklist.

    Fastjson vulnerability version line

    The following vulnerabilities will not be analyzed too much. Too many will only briefly explain and give the payload to test and explain the repair method.

    ver<=1.2.24

    As you can see from the above test, there are no defense in 1.2.24 and before, autotype is enabled by default. Below a few classic payloads.

    com.sun.rowset.JdbcRowSetImpl

    payload:

    Test(jdk=8u102,fastjson=1.2.24):

    result:

    Brief analysis of triggering reasons:

    JdbcRowSetImpl object recovery-> setDataSourceName method call-> setAutocommit method call-> context.lookup (datasourceName) call

    com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

    payload:

    Test(jdk=8u102,fastjson=1.2.24):

    result:

    Brief analysis of triggering reasons:

    TemplatesImpl object recovery-> JavaBeanDeserializer.deserialze-> FieldDeserializer.setValue-> TemplatesImpl.getOutputProperties-> TemplatesImpl.newTransformer-> TemplatesImpl.getTransletInstance-> Through defineTransletClasses, newInstance triggers the static code block of our own constructed class

    Brief description:

    This vulnerability needs to enable the SupportNonPublicField feature, which was also mentioned in the sample test. There is no corresponding setter for _bytecodes,_tfactory_name,_outputProperties and _class in the TemplatesImpl class, so to assign values to these private properties, you need to enable the SupportNonPublicField feature. The specific construction process of the poc will not be analyzed here, you can see Master Liao's this, involving some details.

    ver>=1.2.25&ver<=1.2.41

    Before 1.2.24, there are no autotype restriction. Starting from 1.2.25, autotype support was turned off by default, and checkAutotype was added. A blacklist and whitelist was added to prevent autotype from being turned on. Between 1.2.25 and 1.2.41, a checkAutotype bypass occurred.

    The following is checkAutoType code:

    Four position marks were made on it, because the following bypasses are also related to these positions. This time the bypass is through the previous 1, 2, 3 and successfully entered the location 4 to load the target class. Position 4 loadclass is as follows:

    Removed the L and ; before and after className, in the form of Lcom.lang.Thread;, this representation method is similar to the representation method of classes in the JVM, and fastjson handles this representation method. The previous blacklist detection was startedwith detection, so you can add L and ; to the class specified by @type to bypass the blacklist detection.

    Use the above JdbcRowSetImpl:

    Test(jdk8u102,fastjson 1.2.41):

    result:

    ver=1.2.42

    In 1.2.42, the checkAutotype bypass of 1.2.25 ~ 1.2.41 was fixed, the blacklist was changed to decimal, and the checkAutotype detection was changed accordingly:

    The blacklist has been changed to decimal, and the detection has been hashed accordingly. However, it is consistent with the detection process in 1.2.25 above, except the tests with startswith are replaced with hash operations. The fix for bypassing checkAutotype of 1.2.25 ~ 1.2.41 is the red box, judging whether the className is L and;, if it is, then intercept the second character and the penultimate character . Therefore, the bypass of checkAutotype in version 1.2.42 is to double write LL and;;. After interception, the process is the same as that of versions 1.2.25 ~ 1.2.41.

    Use the above JdbcRowSetImpl:

    Test(jdk8u102,fastjson 1.2.42):

    结果:

    ver=1.2.43

    1.2.43 For the bypass repair method of 1.2.42:

    Under the first if condition (beginning with L and ending with;), a condition starting withLL is added. If the first condition is met and starting with LL, an exception is thrown directly. So this repair method cannot be bypassed. In addition to the special processing of L and;[ is also treated specially, checkAutoType is bypassed again:

    Use the above JdbcRowSetImpl:

    Test(jdk8u102,fastjson 1.2.43):

    result:

    ver=1.2.44

    The 1.2.44 version fixes 1.2.43 bypass and handles [:

    Deleted the previous judgment of the beginning of L, the end of;, and the beginning ofLL, changed it to an exception of [ at the beginning or an exception at the end of ;, So the previous bypasses were fixed.

    ver>=1.2.45&ver<=1.2.46

    During these two versions, a blacklist was added and no checkAutotype bypass occurred. Several payloads in the blacklist are given in the RCE Payload at the back, so I won't write them here.

    ver=1.2.47

    This version has been successfully bypassed without enabling autotype. Analyze this bypass: 1. The use of java.lang.class, this class is not in the blacklist, so checkAutotype can be over. 2. The deserializer corresponding to this java.lang.class class is MiscCodec. When deserialize, it will take the val key value in the json string and load the class corresponding to this val. If fastjson cache is true, it will cache the class corresponding to this val to In the global map 3. If the class with val name is loaded again, autotype is not enabled (because it will detect the black and white list first, so this vulnerability autotype is turned on but not successful), the next step is try to obtain this class from the global map, if it is , return directly.

    There have been many analysis of this vulnerability. For details, please refer to this article.

    payload:

    Test(jdk8u102,fastjson 1.2.47):

    result:

    ver>=1.2.48&ver<1.2.68

    Fixed the bypass of 1.2.47 in 1.2.48. In MiscCodec, where the loadClass is processed, the cache is set to false:

    Between 1.2.48 and the latest version 1.2.68, there are added blacklist categories.

    ver=1.2.68

    1.2.68 is the latest version at present. Safemode was introduced in 1.2.68. When safemode is turned on, @type this specialkey is completely useless. Both whitelist and blacklist do not support autoType.

    In this version, in addition to adding a blacklist, a blacklist is also subtracted:

    I don't know if there is any other security personnel running out of this blacklist, whether it is a package name or a class name, and then it can be used for malicious exploitation. It is a bit strange anyway.

    Detect Fastjson

    The more commonly method of detecting Fastjson is to use the dnslog. After detecting it, use RCE Payload one by one. Colleagues said that it is possible to get the paylaod of the echo, but the target container/framework is different, and the echo method will be different. This is a bit difficult ..., let's use dnslog.

    dnslog detect

    At the present, fastjson detection is common to detect by dnslog mode, in which Inet4Address and Inet6Address are available until 1.2.67. Here are some payloads to be seen (combined with the rand: {} method above, which is more general):

    Some RCE Payload

    I didn't collect the payload about fastjson before, and I didn't run the jar package .... The following lists are the payloads circulated on the network and some of them deducted from marshalsec and transformed into a payload suitable for fastjson. The jdk version for each payload will not be tested one by one, I don't know how much time it takes to test this. The actual use basically can't be know in this version, whether autotype is turned on or not, the user's configuration, and the user added the blacklist/white or not. so just pass the constructed payload one by one. The basic payload:

    The following is a small script that can transfer the basic payload out of various bypass variants, and also adds \u,\x encoding forms:

    For example JdbcRowSetImpl results:

    Some people also scan maven warehouse packages to find malicious exploits to conform jackson and fastjson. It seems that most of them are looking for jndi-type vulnerabilities. For the blacklist, you can look at this project, it ran to version 1.2.62, most blacklists ran out, but many were package, which specific class still have to look for one by one in the package.

    Reference

    1. https://paper.seebug.org/994/#0x03
    2. https://paper.seebug.org/1155/
    3. https://paper.seebug.org/994/
    4. https://paper.seebug.org/292/
    5. https://paper.seebug.org/636/
    6. https://www.anquanke.com/post/id/182140#h2-1
    7. https://github.com/LeadroyaL/fastjson-blacklist
    8. http://www.lmxspace.com/2019/06/29/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/#v1-2-47
    9. http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/
    10. http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
    11. http://gv7.me/articles/2020/several-ways-to-detect-fastjson-through-dnslog/#0x03-%E6%96%B9%E6%B3%95%E4%BA%8C-%E5%88%A9%E7%94%A8java-net-InetSocketAddress
    12. https://xz.aliyun.com/t/7027#toc-4
    13. <https://zhuanlan.zhihu.com/p/99075925
    14. ...

    Too many, thanks all people for their hard work.


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1193/

    作者:江 | Categories:技术分享 | Tags:
  • 空指针-Base on windows Writeup — 最新版DZ3.4实战渗透

    2020-11-02

    作者:LoRexxar'@知道创宇404实验室
    时间:2020年5月11日 

    英文链接: https://paper.seebug.org/1205/

    周末看了一下这次空指针的第三次Web公开赛,稍微研究了下发现这是一份最新版DZ3.4几乎默认配置的环境,我们需要在这样一份几乎真实环境下的DZ中完成Get shell。这一下子提起了我的兴趣,接下来我们就一起梳理下这个渗透过程。

    与默认环境的区别是,我们这次拥有两个额外的条件。

    1、Web环境的后端为Windows
    2、我们获得了一份config文件,里面有最重要的authkey

    得到这两个条件之后,我们开始这次的渗透过程。

    以下可能会多次提到的出题人写的DZ漏洞整理

    authkey有什么用?

    authkey是DZ安全体系里最重要的主密钥,在DZ本体中,涉及到密钥相关的,基本都是用authkey和cookie中的saltkey加密构造的。

    当我们拥有了这个authkey之后,我们可以计算DZ本体各类操作相关的formhash(DZ所有POST相关的操作都需要计算formhash)

    配合authkey,我们可以配合source/include/misc/misc_emailcheck.php中的修改注册邮箱项来修改任意用户绑定的邮箱,但管理员不能使用修改找回密码的api。

    可以用下面的脚本计算formhash

    当我们发现光靠authkey没办法进一步渗透的时候,我们把目标转回到hint上。

    1、Web环境的后端为Windows
    2、dz有正常的备份数据,备份数据里有重要的key值

    windows短文件名安全问题

    在2019年8月,dz曾爆出过这样一个问题。

    在windows环境下,有许多特殊的有关通配符类型的文件名展示方法,其中不仅仅有 <>"这类可以做通配符的符号,还有类似于~的省略写法。这个问题由于问题的根在服务端,所以cms无法修复,所以这也就成了一个长久的问题存在。

    具体的细节可以参考下面这篇文章:

    配合这两篇文章,我们可以直接去读数据库的备份文件,这个备份文件存在

    我们可以直接用

    拿到数据库文件

    从数据库文件中,我们可以找到UC_KEY(dz)

    pre_ucenter_applications的authkey字段找到UC_KEY(dz)

    至此我们得到了两个信息:

    当我们有了这两个key之后,我们可以直接调用uc_client的uc.php任意api。,后面的进一步利用也是建立在这个基础上。

    uc.php api 利用

    这里我们主要关注/api/uc.php

    通过UC_KEY来计算code,然后通过authkey计算formhash,我们就可以调用当前api下的任意函数,而在这个api下有几个比较重要的操作。

    我们先把目光集中到updateapps上来,这个函数的特殊之处在于由于DZ直接使用preg_replace替换了UC_API,可以导致后台的getshell。

    具体详细分析可以看,这个漏洞最初来自于@dawu,我在CSS上的演讲中提到过这个后台getshell:

    根据这里的操作,我们可以构造$code = 'time='.time().'&action=updateapps';

    来触发updateapps,可以修改配置中的UC_API,但是在之前的某一个版本更新中,这里加入了条件限制。

    由于过滤了单引号,导致我们注入的uc api不能闭合引号,所以单靠这里的api我们没办法完成getshell。

    换言之,我们必须登录后台使用后台的修改功能,才能配合getshell。至此,我们的渗透目标改为如何进入后台。

    如何进入DZ后台?

    首先我们必须明白,DZ的前后台账户体系是分离的,包括uc api在内的多处功能,login都只能登录前台账户,

    也就是说,进入DZ的后台的唯一办法就是必须知道DZ的后台密码,而这个密码是不能通过前台的忘记密码来修改的,所以我们需要寻找办法来修改密码。

    这里主要有两种办法,也对应两种攻击思路:

    1、配合报错注入的攻击链
    2、使用数据库备份还原修改密码

    1、配合报错注入的攻击链

    继续研究uc.php,我在renameuser中找到一个注入点。

    在函数的最下面,$get[newusername]被直接拼接进了update语句中。

    但可惜的是,这里链接数据库默认使用mysqli,并不支持堆叠注入,所以我们没办法直接在这里执行update语句来更新密码,这里我们只能构造报错注入来获取数据。

    这里值得注意的是,DZ自带的注入waf挺奇怪的,核心逻辑在

    然后config中相关的配置为

    这道题目特殊的地方在于,他开启了afullnote

    由于/**/被替换为空,所以我们可以直接用前面的逻辑把select加入到这中间,之后被替换为空,就可以绕过这里的判断。

    当我们得到一个报错注入之后,我们尝试读取文件内容,发现由于mysql是5.5.29,所以我们可以直接读取服务器上的任意文件。

    思路走到这里出现了断层,因为我们没办法知道web路径在哪里,所以我们没办法直接读到web文件,这里我僵持了很久,最后还是因为第一个人做出题目后密码是弱密码,我直接查出来进了后台。

    在事后回溯的过程中,发现还是有办法的,虽然说对于windows来说,web的路径很灵活,但是实际上对于集成环境来说,一般都安装在c盘下,而且一般人也不会去动服务端的路径。常见的windows集成环境主要有phpstudy和wamp,这两个路径分别为

    找到相应的路径之后,我们可以读取\uc_server\data\config.inc.php得到uc server的UC_KEY.

    之后我们可以直接调用/uc_server/api/dpbak.php中定义的

    构造管理员的sid来绕过权限验证,通过这种方式我们可以修改密码并登录后台。

    2、使用数据库备份还原修改密码

    事实上,当上一种攻击方式跟到uc server的UC_KEY时,就不难发现,在/uc_server/api/dbbak.php中有许多关于数据库备份与恢复的操作,这也是我之前没发现的点。

    事实上,在/api/dbbak.php就有一模一样的代码和功能,而那个api只需要DZ的UC_KEY就可以操作,我们可以在前台找一个地方上传,然后调用备份恢复覆盖数据库文件,这样就可以修改管理员的密码。

    后台getshell

    登录了之后就比较简单了,首先

    修改uc api 为

    然后使用预先准备poc更新uc api

    这里返回11就可以了

    写在最后

    整道题目主要围绕的DZ的核心密钥安全体系,实际上除了在windows环境下,几乎没有其他的特异条件,再加上短文件名问题原因主要在服务端,我们很容易找到备份文件,在找到备份文件之后,我们可以直接从数据库获得最重要的authkey和uc key,接下来的渗透过程就顺理成章了。

    从这篇文章中,你也可以窥得在不同情况下利用方式得拓展,配合原文阅读可以获得更多的思路。

    REF


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1197/

    作者:江 | Categories:技术分享 | Tags:
  • NULL Pointer 3th Web Challenge Writeup — The latest version of DZ3.4 penetration

    2020-11-02

    Author:LoRexxar@Knownsec 404 Team 
    Time: May 11, 2020 
    Chinese version: https://paper.seebug.org/1197/

    I took a look at the third Web Open of the null pointer this weekend, and after a little research, I found that this is the latest version of the DZ3.4 environment with almost default configuration. We need to pwn it in such a DZ under almost real environment. This moment raised my interest, and then we will sort out the penetration process together.

    The difference from the default environment is that we have two additional conditions. 1. The backend of the web environment is Windows; 2. We get a config file which contains an insecure configuration(and authkey)

    After getting these two conditions, we started the penetration.

    The following may be mentioned repeatedly DZ vulnerability written by the author.

    What is the use of authkey

    authkey is the most important master key in the DZ security system. In the DZ Website, the key related is basically constructed with the authkey and saltkey which encrypt in the cookie.

    After we have this authkey, we can calculate the formhash related to various operations of the DZ (all POST-related operations of DZ need to calculate the formhash)

    With authkey, we can cooperate with the function in source / include / misc / misc_emailcheck.php to modify the email for any user, but the administrator cannot use the API to change the password.

    You can use the following script to calculate the formhash

    When we found that authkey alone could not penetrate further, we turned our goal back to hint.

    1. The backend of the web environment is Windows
    2. dz has normal backup data, and there is an important key value in the backup data

    Windows short file name security issue

    In August 2019, dz had such a problem.

    In the windows environment, there are many special methods for displaying file names related to wildcard types, among which are not only <>"This type of symbol can be used as a wildcard, and there is an ellipsis similar to~. This problem is because the server, so cms cannot be repaired, so this has become a long-term problem .

    For specific details, please refer to the following article:

    With these two articles, we can directly read the backup file of the database.

    This backup file exists in

    We can use

    From the database file, we can find UC_KEY (dz) Find UC_KEY (dz) in the authkey field of pre_ucenter_applications

    So far we have got two pieces of information:

    When we have these two keys, we can directly call any api in uc.php. The further use of the latter is also based on this.

    Uc.php api use

    Here we focus on the /api/uc.php.

    Calculate the code through UC_KEY, and then calculate the formhash throughauthkey, we can call any function under the api, and there are several more important operations under this api.

    Let's focus on updateapps first. The special feature of this function is that DZ directly replacesUC_API with preg_replace, which can lead to getshell in the background.

    Specific detailed analysis can be seen, this vulnerability originally came from @dawu, I mentioned this background getshell in my CSS speech:

    According to the operation here, we can construct $ code = 'time ='. Time (). '& Action = updateapps';

    To trigger updateapps, you can modify the UC_API in the configuration, but in a previous version update, conditions were added here.

    Due to the filtering of single quotes, the uc api we injected cannot close the quotes, so we can’t complete the getshell with the api alone.

    In other words, we must login to the background and use the background modification function to cooperate with getshell. So far, our goal of penetration has changed to how to login into the background.

    How to login into the DZ background

    First of all, we must understand that DZ's front-end and back-end account systems are separate. There are many functions including uc api, can only login to the front-end account.

    In other words, the only way to enter the background of DZ is to know the background password of DZ, and this password cannot be changed by forget the password at the front desk, so we need to find a way to change the password.

    There are two main methods here, which also correspond to two attack ideas: 1. Attack chain with error SQL injection 2. Use the database backup to restore and change the password

    1. Attack chain with error SQL injection

    Continue to study uc.php, I found an injection point in function renameuser.

    At the bottom of the function, $get[newusername] is directly spliced into the update statement.

    But unfortunately, the linked database uses mysqli by default, and does not support stack injection, so we can't directly execute the update statement here to update the password. Here we can only construct an error injection to obtain data.

    It is worth noting here that the injection waf that comes with DZ is quite strict, the core logic is in.

    and the configure in:

    It open the afullnote in this challenge.

    Since /**/ is replaced with empty, we can directly add select to the middle, and then replaced with empty, we can bypass the waf here.

    When we got an error injection, we tried to read the file content and found that because mysql is 5.5.29, we can directly read any file on the server.

    When the idea came here, there was a fault, because we couldn't know where the web path was, so we couldn't read the web file directly. Here I was deadlocked for a long time, and finally the password was weak after the first person made the question. I went straight into the background.

    In the process of backtracking, I found that there is still a way. Although the path of the web is very flexible for windows, in fact, for integrated environments, it is generally installed under the c drive, and most people will not move. The server path. Common windows integrated environment mainly includes phpstudy and wamp, these two paths are respectively

    After finding the corresponding path, we can read \uc_server\data\config.inc.php to getUC_KEYof uc server.

    After that we can directly call the one defined in /uc_server/api/dpbak.php

    Construct the administrator's sid to bypass the authorization verification, in this way we can modify the password and login to the background.

    2. Use the database backup to restore and change the password

    In fact, when the last attack method followed the UC server's UC_KEY, it is not difficult to find that there are many operations about database backup and recovery in/uc_server/api/dbbak.php, which is also my previous Not found.

    In fact, there is exactly the same code and function in /api/dbbak.php, and that api only needs DZ ’sUC_KEY to operate, we can find a place to upload at the front desk, and then call backup to restore and overwrite the database , So that the administrator's password can be changed.

    Getshell in backend

    After logging in, it is relatively simple, first

    modify the uc api to

    then, use api to update uc api

    Here return 11 means success

    Finally

    The whole question mainly surrounds the core key security system of DZ. In fact, except for the Windows environment, there are almost no other special conditions. In addition, the short file name problem is mainly on the server side. We can easily find the backup file. After finding the backup file, we can obtain the most important authkey and uc key directly from the database, and the subsequent infiltration process is logical.

    From this article, you can also get a glimpse of the ways in which you can use it in different situations, and you can get more ideas with the original text.

    REF


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1205/

    作者:江 | Categories:技术分享 | Tags:
  • 使用 ZoomEye 寻找 APT 攻击的蛛丝马迹

    2020-11-02

    作者:Heige(a.k.a Superhei) of KnownSec 404 Team 
    时间:2020年5月25日 
    英文链接:https://paper.seebug.org/1220/

    今年一月发布的ZoomEye 2020里上线了ZoomEye的历史数据查询API接口,这个历史数据接口还是非常有价值的,这里就介绍我这几天做的一些尝试追踪APT的几个案例。

    在开始之前首先你需要了解ZoomEye历史api接口的使用,参考文档:https://www.zoomeye.org/doc#history-ip-search 这里可以使用的是ZoomEye SDK https://github.com/knownsec/ZoomEye 另外需要强调说明下的是:ZoomEye线上的数据是覆盖更新的模式,也就是说第2次扫描如果没有扫描到数据就不会覆盖更新数据,ZoomEye上的数据会保留第1次扫描获取到的banner数据,这个机制在这种恶意攻击溯源里其实有着很好的场景契合点:恶意攻击比如Botnet、APT等攻击使用的下载服务器被发现后一般都是直接停用抛弃,当然也有一些是被黑的目标,也是很暴力的直接下线!所以很多的攻击现场很可能就被ZoomEye线上缓存。

    当然在ZoomEye历史api里提供的数据,不管你覆盖不覆盖都可以查询出每次扫描得到的banner数据,但是目前提供的ZoomEye历史API只能通过IP去查询,而不能通过关键词匹配搜索,所以我们需要结合上面提到的ZoomEye线上缓存数据搜索定位配合使用。

    案例一:Darkhotel APT

    在前几天其实我在“黑科技”知识星球里提到了,只是需要修复一个“bug”:这次Darkhotel使用的IE 0day应该是CVE-2019-1367 而不是CVE-2020-0674(感谢廋肉丁@奇安信),当然这个“bug”不影响本文的主题。

    从上图可以看出我们通过ZoomEye线上数据定位到了当时一个Darkhotel水坑攻击现场IP,我们使用ZoomEye SDK查询这个IP的历史记录:

    列举ZoomEye历史数据里收录这个IP数据的时间节点及对应端口服务

    我们再看看被植入IE 0day的进行水坑攻击的时间节点及端口:

    很显然这个水坑攻击的大致时间区间是从2019-10-06 05:24:44到2020-01-28 10:58:02,另外这个IP很显然不是攻击者购买的VPS之类,而是直接攻击了某个特定的网站来作为“水坑”进行攻击,可以确定的是这个IP网站早在2019-10-06之前就已经被入侵了!从这个水坑的网站性质可以基本推断Darkhotel这次攻击的主要目标就是访问这个网站的用户!

    我们继续列举下在2019年这个IP开了哪些端口服务,从而帮助我们分析可能的入侵点:

    很典型的JSP运行环境,在2019年5月的时候开了8009端口,Tomcat后台管理弱口令等问题一直都是渗透常用手段~~

    顺带提一句,其实这次的攻击还涉及了另外一个IP,因为这个IP相关端口banner因为更新被覆盖了,所以直接通过ZoomEye线上搜索是搜索不到的,不过如果你知道这个IP也可以利用ZoomEye历史数据API来查询这个IP的历史数据,这里就不详细展开了。

    案例二:毒云藤(APT-C-01)

    关于毒云藤(APT-C-01)的详细报告可以参考 https://ti.qianxin.com/uploads/2018/09/20/6f8ad451646c9eda1f75c5d31f39f668.pdf我们直接把关注点放在

    “毒云藤组织使用的一个用于控制和分发攻击载荷的控制域名 http://updateinfo.servegame.org

    “然后从 hxxp://updateinfo.servegame.org/tiny1detvghrt.tmp 下载 payload”

    URL上,我们先尝试找下这个域名对应的IP,显然到现在这个时候还没有多大收获:

    在奇安信的报告里我们可以看到使用的下载服务器WEB服务目录可以遍历

    所以我们应该可以直接尝试搜索那个文件名“tiny1detvghrt.tmp”,果然被我们找到了

    这里我们可以基本确定了updateinfo.servegame.org对应的IP为165.227.220.223 那么我们开始老套路查询历史数据:

    继续看看这个tiny1detvghrt.tmp部署的时间区间:

    最起码可以确定从2017年11月底就已经开始部署攻击了,那么在这个时间节点之前还有一个时间节点2017-10-04 05:17:38,我们看看他的banner数据:

    从这个banner数据里可以得出结论,这个跟第一个案例里目标明确的入侵后植入水坑不一样的是,这个应该是攻击者自主可控的服务器,从doajksdlfsadk.tmp这些文件命名方式及文件大小(都为4.9k)基本可以推断这个时间节点应该是攻击者进行攻击之前的实战演练!所以这个IP服务器一开始就是为了APT攻击做准备的,到被发现后就直接抛弃!

    总结

    网络空间搜索引擎采用主动探测方式在网络攻击威胁追踪上有很大的应用空间,也体现了历史数据的价值,通过时间线最终能复盘攻击者的攻击手段、目的及流程。最后感谢所有支持ZoomEye的朋友们,ZoomEye作为国际领先的网络空间测绘搜索引擎,我们一直在努力!


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1219/

    作者:江 | Categories:漏洞分析 | Tags:
  • Liferay Portal Json Web Service Deserialization Vulnerability (CVE-2020-7961) Analysis

    2020-11-02

    Author:Longofo@Knownsec 404 Team 
    Time: March 27, 2020 
    Chinese version:https://paper.seebug.org/1162/

    A vulnerability on Liferay Portal JSON Web Service RCE was previously posted on CODE WHITE. At first, my friends were dealing with this vulnerability, and I went to see it later. Liferay Portal uses Flexjson library for JSON Web Service processing in 6.1 and 6.2 versions, and replaced it with Jodd Json after version 7.

    In summary, the vulnerability is: Liferay Portal provides Json Web Service service. For some endpoints that can be called, if a method provides Object parameter type, then we can construct an exploitable malicious class that conforms to Java Beans and pass the constructed json deserialization string, Liferay will automatically call the setter method of the malicious class and the default constructor when deserializing. However, there are still some details, and I feels quite interesting. In this article, the analysis of Liferay using JODD deserialization.

    JODD Serialization And Deserialization

    Refer to the Official User Manual, first look at the direct serialization and deserialization of JODD:

    TestObject.java:

    TestObject1.java

    Test.java

    Output:

    In Test.java, two ways are used. First one is the commonly used way. The root type is specified when deserializing. The second official does not recommend this,security issues are exist. Assume up this application provides a place to receive JODD Json, and uses the second way, you can deserialize any type specified。The Liferay vulnerability is not caused by this reason, because it does not use setClassMetadataName("class").

    Liferay's Packaging For JODD

    Liferay does not directly use JODD for processing, but repackages some functions of JODD. The code is not long, so we will use JODD separately to analyze Liferay's packaging of JsonSerializer and JsonParser.

    JSONSerializerImpl

    Liferay's wrapper for JODD JsonSerializer is the com.liferay.portal.json.JSONSerializerImplclass:

    It can be seen that some functions of the JODD JsonSerializer are set during serialization.

    JSONDeserializerImpl

    Liferay's wrapper for JODD JsonParser is the com.liferay.portal.json.JSONDeserializerImpl class:

    It can be seen that some functions of the JODD JsonParser are also set when deserializing.

    Liferay Vulnerability Analysis

    Liferay provides hundreds of webservices that can be called in the /api/jsonws API. The servlet responsible for processing the API is also directly configured in web.xml:

    look at this methods:

    Seeing this, something occur.We can pass parameters for method calls. There are p_auth for verification, but deserialization is before verification, so that value does not works for exploit. According to the analysis of CODE WHITE, there are method parameters with parameter type Object, so guess that we can pass in any type of class. We can first debug the normal packet capture call to debug, and here does not write the normal call debugging process, simply look at the post parameters:

    In general, Liferay first finds the method corresponding to /announcementsdelivery/update-delivery-> other post parameters are method parameters-> when each parameter object type is consistent with the target method parameter type-> Restore the parameter object-> call this method with reflection. However, there is no type specification for packet capture, because most types are String, long, int, List, map and others,JODD will automatically handle it when deserializing. In this section,how to specify a specific type?

    The author mentioned in the article that Liferay Portal 7 can only specified rootType for invocation. This is also the case from the above Liferay's JODD JSONDeserializerImpl packaging. If you want to restore a specific object when a method parameter is of type Object,maybe it will parse the data, obtain the specified type, and then call the parse (path, class) method of JODD using specific type to restore it ;maybe Liferay not do. However, it can be seen from the author's analysis that Liferay did. The author looked up the call graph of jodd.json.Parser#rootType (envy such a tool):

    looking up this, the author found a place where a root type could be specified. In com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#JSONWebServiceActionImpl call the com.liferay.portal.kernel.JSONFactoryUtil#looseDeserialize(valueString, parameterType ), looseDeserialize calls JSONSerializerImpl, and JSONSerializerImpl calls JODD's JsonParse.parse.

    And the call on com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#JSONWebServiceActionImpl is the process of Liferay parsing the Web Service parameters. Its upper levelJSONWebServiceActionImpl#_prepareParameters(Class <?>), The JSONWebServiceActionImpl class has a _jsonWebServiceActionParameters attribute:

    This property also holds a JSONWebServiceActionParametersMap. In its method, when the parameter starts with+, its put method splits the passed parameters with:, before : is parameter name,after : is type name:

    The put parsing operation is completed in com.liferay.portal.jsonwebservice.action.JSONWebServiceInvokerAction # _executeStatement:

    Through the above analysis and author's article, we can know the following points:

    • Liferay allows us to call web service methods via /api/jsonws/xxx
    • The parameter can start with +, use : to specify the parameter type
    • JODD JsonParse will call the class's default constructor and the setter method corresponding to the field

    So we need to find the class that has malicious operations in the setter method or the default constructor. Look at the exploitation chain that marshalsec has provided,we can directly find the inherited exploitation chain. Most of them are also suitable for this vulnerability,it also depends on whether it exists in Liferay. Here are the test com.mchange.v2.c3p0.JndiRefForwardingDataSource, use the service/expandocolumn/add-column, because it has java.lang.Object parameter:

    Payload is as follows:

    Parsed the parameter type, deserialized the parameter object, and finally reached the jndi query:

    Patch Analysis

    Liferay patch adds type checking, in com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl # _checkTypeIsAssignable:

    _JSONWS_WEB_SERVICE_PARAMETER_TYPE_WHITELIST_CLASS_NAMES contains all whitelist classes in portal.properties. Basically, all whitelist classes start with com.liferay.


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1163/

    作者:江 | Categories:技术分享 | Tags: