[备份]谈谈最近失败的挖洞经历

开始

之前被几个人喷,且找实习过程中总是一面挂,对自己产生了很大的质疑,冲动之下做了些不应该的事,写了些不该写的文章。七月没有学习,八月底开始简单的学习,尝试继续挖洞,不过并没有什么太大成果。

这些挖洞过程还是值得总结下。

一次失败的SQL注入

首先谈谈前两周擦肩而过的一个SQL注入吧,大致过程如下:

历史上爆出过几个SQL注入漏洞,由于是H2所以比较容易从注入到RCE,打算从这方面下手看看

简单审计后发现下面的代码(节选关键部分)

涉及到的build方法

可以发现其中executeQuery方法没有采用预编译,判断是否使用预编译其实也简单:

之所以直接上报给Skywalking官方,理由如下:

根据我Java开发经验和直接,我觉得这里80%以上是存在漏洞的,然而Skywalking的做法和普通的Web项目并不类似,它的id并不完全由前端控制,会造进入DAO层之前对id进行base64编码。显然传入的Payloadbase64编码过后就会失效。

参考Skywalking主席的修复方案:https://github.com/apache/skywalking/pull/9561

其实官方的想法是让我提一个PR修复,不过需要forkclone改了后再提pr有点麻烦,所以我推给了官方

回复截图:

总结:

深入谈谈Zip Slip漏洞

上个月向Spring CloudApache Flink提交了多份Zip Slip漏洞,由于一些原因最终官方选择了修复(防御性编程)但由于各种原因(无法在生产环境利用或无法提升权限)不发布CVE,这个漏洞其实很有必要来谈一谈

(1)什么是Zip Slip漏洞

该漏洞在18年左右由国外安全公司提出:https://github.com/snyk/zip-slip-vulnerability

简单来说,就是代码解压缩时,压缩子项的名可以是../../test这样的文件,这时候如果有写文件的操作,将会导致任意文件写入漏洞。主流的解压缩软件其实会自动处理这种情况,想要制作这种恶意压缩包有两种方式:高端操作可以直接编辑二进制来修改已有压缩包中的子项文件名,简易方式可以用代码构造并通过ZipOuputStream生成

(2)场景和利用

JavaGolang基础库中,不存在高级的压缩文件API。但是在Python等语言中,基础库已经考虑到并修复了这种情况。值得一说的话,在Apache Commons库中,也没有高级API所以导致这种漏洞在Java项目中尤其常见

在陈师傅(chybeta)星球有两篇文章提到该攻击:

(1)利用Zip Slip漏洞RCE:https://t.zsxq.com/05m2NJYbY

(2)陈师傅挖洞中遇到的:https://t.zsxq.com/05eUBY3vB

可能有师傅没有加星球,所以我简单总结下陈师傅分享的两种利用:服务端做更新操作的时候会拉取远程的更新压缩包并解压和更新,假设这个压缩包可控,将可以写入SSH Key或者PHP/JSP等后门以此达到RCE效果;另外各种CMS/OA系统的后台大概率存在上传解压文件的功能,例如备份和恢复功能,在解压的时候类似的思路实现RCE效果

(3)一些尝试

之前向Apache Flink提交了一份Zip Slip漏洞:https://issues.apache.org/jira/browse/FLINK-29122

其实提交的时候已经觉得不会认了,因为无法做到超越当前权限的事情,官方回复印证了我想法:有权访问 Flink 集群的用户无论如何都可以在那里执行任意代码,所以这更像是一个错误(因为意外行为)而不是漏洞

我向Spring Cloud Contract提交的Zip Slip漏洞,的确可以利用,但最终被特殊原因拒绝了,给出修复链接

这个漏洞在Spring Cloud Contract中应该如何利用就不多说了,鸡肋且不认可所以没必要研究。官方的理由是:这仅是一个测试框架,不会在生产环境中使用(之前给Spring Cloud Contract报告了一些其他漏洞,有高危也有鸡肋,全部都被一样的原因拒绝了,测试框架的漏洞难道不应该算漏洞嘛)

最后官方还是在文档中明确写出了:您永远不应该下载来自不受信任位置的合约,参考链接

简单来看下代码,一些重点内容写在注释中:

修复代码如下,包含了..抛出了异常:

可以看下另一种修复方式,参考Tomcat在解压时候的代码(并不是漏洞)

这里的getCanonicalFile效果如下,如果绝对路径开头不匹配目标,那么抛出异常

其实如果分析了上文Spring Cloud Contract的修复方式,会发现和这种方式类似

(4)如何挖掘该漏洞

简单来说一下Zip Slip漏洞的挖掘思路:确定漏洞触发点并向上分析

对于确定漏洞触发点(sink)的方式有很多,我简单谈一谈。开源项目的角度可以直接搜索ZipInputStreamJarInputStream关键字,因为这是解压zipjar包必须的类,但是实践发现JarInputStream更多情况下并不实际写文件,而是遍历其中的压缩项并提取关键配置文件等信息。另外一个思路是搜索FileOutputStream或者Files.write等写文件的代码,实践中发现接近三分之一的情况中FileOutputStream配合解压文件使用。非开源的Java项目一般是提供Jar包,批量反编译是一种方案,进阶情况可以写字节码分析的工具。

基于Golang的项目也存在大量类似的漏洞,不过起点是archive/zip包,根据这个点寻找不难,比如之前对Apache OpenWhisk简单分析后,得到三处潜在漏洞,但分析后都无法直接利用:

进一步的分析类似其他漏洞,主要是找到每一条调用链:A->B->C->D->解压文件操作,选择codeql或肉眼分析都是办法。不过我之前是自己写工具来做的,因为有时候需要做一件小事,为了小事去学一个大领域有点杀鸡用牛刀感觉,当然从长久角度来看,学好静态分析和codeql之类的总没错。

(5)总结

由于Java语言标准库和Apache Commons库都不存在高级API导致这类漏洞在Java项目中极其常见,但绝大多数情况下都无法利用,由于各种各样奇特的理由:比如无法提升任何权限,或者项目仅用于测试不用于生产环境等理由。所以将思路放在自动工具上是不妥的,终究还得结合实际情况来人工调试分析。

其实我挖到的Zip Slip洞并不是全部都被拒绝的,还是有几个项目认可并可能在未来发布CVE和安全公告,这里就不多说了。无论如何,也算是对多个知名项目的代码做出了安全方面的贡献。

再谈正则DOTALL绕过

这其实是之前搞正则DOTALL绕过时候顺便搞的东西,分析了TomcatHTTPD中是否存在这样的绕过可能。

Tomcat好几次了,每次都没有什么收获。不过有一次,我将TomcatApache HTTPD做对比的时候,发现了一处有趣的地方:介于漏洞和Bug之间

注意到Apache HTTPDNginx等组件都存在rewrite模块,主要用于处理重定向。以Apache HTTPD为例: https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html

假设我们配置这样的rewrite规则:

结果应该是所有/foo/下的请求重定向到/bar,其实实际上不这么用,也许会把/foo/(.*)取出来作为变量传递到类似/bar?id=$1这样的地方。之前有个Shiro的垃圾绕过洞,就是绕过.*这样的正则,通过%0a和%0d即可绕过没有设置DOTALL选项的正则。

类似的原理,假设配置了以上的规则,可能存在一种方式使访问/foo/?可以不被重定向到/bar。按照漏洞的一种定义:产生意外行为的代码可以被定义的漏洞,这将是一个配置漏洞或者逻辑漏洞,虽然想不到太多的危害。除非/foo下有什么受保护的资源,或者需要授权才能访问的东西,假设这种情况,也需要有restful或者对url有特殊处理的情况下才可以有意义地访问。

最先并没有分析Tomcat而是从HTTPD入手,在Apache HTTPDrewrite中:对正则表达式进行编译的函数如下,其中cflags是编译正则表达式的选项,在mod_rewrite中该选项为newrule->flags & RULEFLAG_NOCASE)? AP_REG_ICASE : 0,虽复杂,但可以发现仅与用户配置和大小写case有关,暂时说明默认不配置DOTALL,存在绕过规则的可能。

跟入util.c

跟入发现下面这样的代码:不难发现,首先和默认规则进行某种操作,然后根据cflags设置参数。在其中找到了DOTALL选项,因此分析AP_REG_NO_DEFAULT是什么东西。

ap_regex.h头文件中发现:

分析得出的结论是: 从mod_rewrite传入的cflags会变成默认的0x240数值,强制设置了DOTALL选项和另一个选项,之后的&运算一定为1。

Apache Tomcat中同样存在rewrite模块,不过不像HTTPD一样以mod命名而是一个Valvehttps://tomcat.apache.org/tomcat-9.0-doc/rewrite.html

分析Tomcat代码之后,其中rewrite rule解析核心代码节选如下:

发现仅设置了CASE_INSENSITIVEflag然后直接Pattern.compile操作,显然这里存在绕过。一点题外话,不难发现Java代码的确比C代码分析起来更简单,至少Tomcat找核心代码一步到位,而HTTPD跳了多步。

简单对Tomcat进行配置: (1)在server.xml中开启RewirteValve功能

(2)在conf\Catalina\localhost中新建rewrite.config文件

(3)启动Tomcat测试绕过

我将自己对于Apache HTTPDTomcat的对比分析过程和结果报告到Tomcat官方,询问他们对于此问题的看法,以及是否认为这是安全漏洞。

Apache Tomcat官方对于此问题的看法:

总结:

解析库中的通用DoS

之前有多篇文章来谈论DoS拒绝服务攻击,这次再谈一个特殊的,解析和协议层面的拒绝服务

很多文件结构和协议都有table的概念:

常见的class结构中有多种这样的表结构,在elf/pe等文件结构中也类似。这样的结构在解析库中,大概率会存在内存拒绝服务漏洞,我用一段伪代码来解释:

如果没有对表长度进行验证,将会导致数组可能使用2G以上内存(由于四字节int类型最大0xffffffff)

当目标系统的某程序内存超过限制时,将会导致某程序被被kill(OOM-Killer)

垃圾DoS没有太多必要进行分析,所以简单提一下实践的情况:

之所以说这种DoS是通用的,因为绝大多数解析库中不会对表长度进行验证。但也应该注意一个问题,只有四字节int和八字节long类型的表长是可能存在DoS的,两字节short类型能够支持最大值也不过65535,仅分配64K的内存如何DoS呢?

结束

并非一直失败,总有成功的例子;并非都是垃圾鸡肋洞,也有RCE等高危,后续文章再谈吧。