小妹妹,我想握着你的手,不为别的,只为给你讲清楚CVE-2020-5902

 

抱歉,来晚了,其实这个漏洞一爆发我就关注了,但是由于上班,空闲时间比较少,就没能自己上手分析,只有看看各大媒体或者安全研究人员公布的漏洞复现方法,但是我发现大家的写的东西都差不多,没什么新意,也没有一篇文章认真讲讲这个漏洞产生的原理,这就让我有点好奇了,于是周末花了一天多时间来复现分析这个漏洞。

本文主要记录在复现分析本漏洞时候遇到的问题及解决思路,希望能够帮到大家

 

环境搭建

之前各大媒体发布的漏洞复现都是直接到zoomeye或者fofa上找的线上目标,我就以为这个产品是需要付费购买的,结果到官网一看,BIG-IP是可以申请试用的,于是就下载了BIG-IP的虚拟机进行复现,也方便后面调试分析。

到官网这个页面选择你想要下载的版本:

https://downloads.f5.com/esd/product.jsp?sw=BIG-IP&pro=big-ip_v15.x

如果没记错,我应该选择的是图中标红的那个版本,点进去,继续选择要下载的版本

因为我的虚拟机是vmware fusion,所以我选择的是图中第一个虚拟机镜像进行下载

在vmvare fusion中直接 “文件->导入” 刚刚下载好的ova文件,然后就可以正常启动BIG-IP虚拟机了,账号密码为root/default,登陆过后应该会提示修改密码,注意,这里修改了密码过后,好像也会修改后面的Web的登陆密码。

启动虚拟机过后还要进行IP的配置,这里我就不展开了,具体操作见

https://blog.csdn.net/ice_age1/article/details/49998059

这篇文章步骤很详细,基本可以解决所有安装问题了8

安装好了过后,在浏览器访问:https://虚拟机IP/ 就可以看到登陆界面了,账号密码默认为admin/admin,如果你修改过密码,密码就是你修改过后的值

登陆上去输入license激活产品,但是其实不激活也不影响我们复现该漏洞。

 

漏洞复现

环境搭好了,现在我们先来复现一下漏洞,只有复现了才知道怎么分析嘛

读文件:

https://192.168.133.128/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd

执行命令:

..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin

该命令会列出登陆了的用户admin的信息,如果此时admin没有登陆,自然就没有信息输出

我这里就简单复现这两处,其他利用方式参见jas502n师傅:

https://github.com/jas502n/CVE-2020-5902

 

漏洞分析

在我看来,学习一个漏洞,不仅仅是学习这个漏洞的产生原理,还要学习作者的挖掘思路,以及这个漏洞对应的应用的特性或者使用场景,这样才可以最大程度地丰富我们的知识储备,所以我每次分析可能都会去揣测漏洞作者是怎么挖掘这个点的

分析过程有点曲折,大家将就着看

我在分析之前,先是看了一篇别人的分析文章(这篇文章后来被我发现完全不正确,也是我在分析本漏洞过程中最大的绊脚石,因为文中错误的分析先入为主了),然后大概明白了问题所在,那篇分析文章里提到了这个漏洞的成因主要是servlet中的鉴权问题,也就是代码实现的问题,但是那篇文章中没有提到作者是怎么发现这个漏洞的,难道真是一行代码一行代码看的?

为了更好的弄清楚作者的挖掘思路,我决定自己看一下那篇文章中提到的漏洞点,动手调试一下,就当是先熟悉BIG-IP

因为BIG-IP这个应用是集成在虚拟机里的,不像是其他应用直接提供源代码或者安装包啥的,所以,我想,第一件事就是从虚拟机中把代码给弄出来吧,BIG-IP部署在虚拟机的/usr/local/www下

这里,引出来第一个问题:

  • tomcat的默认部署目录不是webappps下吗?难道自定义了web根目录?而且tomcat默认的https不是8443端口吗?

经过我的排查,发现tomcat并没有自定义web目录,然后我就开始怀疑难道应用不是部署在tomcat下的?

为了搞清楚应用到底是部署在哪里的,我查了一下端口

netstat -anp | grep 443

原来443端口运行着httpd呀,也就是到这我才明白过来,BIG-IP是一个apache + tomcat的架构,apache作为一个反向代理把请求转发到tomcat进行处理。

然后我去查了一下httpd的配置文件,发现,果然,web目录是配置为/usr/local/www的,也就解决了我的第一个疑问。

然后继续,根据poc可以知道漏洞点出在tmui目录下,所以,我直接把tmui目录给拖了下来,本想着直接部署到本地调试一下,但是代码运行的那一刻我才发现,我没有数据库呀,调试个p

那现在要么远程调试,要么就改一下本地代码的数据库地址,但是想了一下,好像直接改数据库地址可能比较麻烦,主要是我找不到他的数据库配置文件在哪~

于是曲线救国呗,选择远程调试,但是现在又出现了一个问题

  • BIG-IP开启了调试模式了吗?

答案是显而易见的,人家一个产品,怎么会以调试模式打包给你

于是我就只有手动开启调试模式了呗,因为BIG-IP运行在tomcat下,开启tomcat调试不是简简单单吗?

我们都知道,tomcat开启调试都是直接以jpda模式运行catalina脚本

catalina.sh jpda start

但是看到这个虚拟机上的情况,给我整懵了,虚拟机上的tomcat目录下根本就没有catalina.sh脚本,引出下一个问题

  • 那它的tomcat是怎么启动的呢?

查看一下进程信息吧

ps -ef | grep tomcat

可以看到,这里是最可疑的地方了,可能就是这个脚本启动了tomcat

于是我找到了这个脚本,并查看了下它的内容,果然就是一个tomcat启动脚本

脚本是直接使用java运行bootstrap来启动tomcat的,那么我就直接给你开启java调试呗,正当我准备修改脚本的时候,此时,又来了一个问题

  • readonly file system 禁止写入怎么搞?

嗨呀,还好学校里开了Linux这门课,重新挂载下目录,将/usr目录挂载为可写

mount -o remount w /usr

如果你不知道挂载的是哪个目录,可以通过cat /proc/mounts查看,挂载过后,就可以修改dtomcat脚本的内容了,找到如下位置,添加一行

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8080

然后保存修改,重启机器,再次查看进程信息就可以发现,我们的tomcat以调试模式运行了,接下来就是配置idea,准备远程调试代码了。

IDEA远程调试代码配置方法自己搜索下吧,这里的远程调试端口我设置的是8080

但是当我一切都配置好了过后,点击开始调试,又一个问题跳了出来

  • 为什么idea调试会timeout?端口填错了?服务端启动调试模式失败?

我查看了一下服务端,发现8080端口是处于监听状态的,说明tomcat是以调试模式运行着的,那就是说是端口被防火墙过滤了。我祭出我的nmap一扫,发现8080端口处于filtered状态,却是被过滤了呀

我感觉问题变得有点棘手了,尝试使用iptables修改虚拟机防火墙规则,结果提示

Use the TMOS shell utility to make changes to the system configuration.
For more information, see "tmsh help security firewall management-ip-rules"

根据提示,可以看出来,这个虚拟机有一套自己的防火墙管理工具,执行

tmsh help security firewall management-ip-rules

可以查看防火墙策略配置语法,执行完过后,我看着满屏的英文帮助文档,陷入了沉思

没办法,还是得上呀,一番折腾过后,摸清楚了他的配置规则

在终端执行tmsh命令进入一个新的执行环境(这个环境应该是专门用来配置该虚拟机的),依次执行以下命令进入到management-ip-rules模块

然后开始添加防火墙规则

modify rules add { accept-8080 { destination { ports add { 8080 } } action accept place-before first ip-protocol tcp } }

然后输入quit退出tmsh环境,我一开始添加规则时没有指定ip-protocol,一直给我提示

No source or destination port argument allowed for non-port-based protocol (0) on firewall rule

英语好的可以尝试翻译一下,我也是错了好几次才记起No还有禁止的意思。

OK,现在防火墙规则配好了,idea端点击调试,看到连接成功,我直接兴奋的跳了一套全国中小学第三套广播体操

说了这么久的漏洞分析前置操作,现在终于准备看代码了,OK,先看一下web.xml看下servlet配置,那篇分析文章中把关注点放在了ControlServlet上,我也就跟着看了一下这个servlet的配置,看到

然后该servlet是映射到下面的目录的

但是那篇文章中提到controlservlet配置了load-on-startup属性,所以该servlet会在tomcat一启动的时候就初始化,然后后面分析时又提到该servlet重写了doGet/doPost方法,所有的请求都会经过该servlet处理,就是这句话给我整懵逼了,我一个java菜鸟完全不能理解为啥所有请求都会经过该servlet处理,在我的仅有的知识里,只知道配置了servlet-mapping的特定目录才会被该servlet处理,我还以为是load-on-startup属性带来的效果,于是我去问了我身边搞java web开发的同学,他说没这种操作,我不信,还去做了个实验,发现,确实没这种操作。于是我再次陷入了对人生以及社会的思考~

  • 到底是哪里的配置可以让该servlet接受所有的请求?

想不出来,就只能靠实际操作了,眼见为实,我开始在controlServlet处打断点调试,发现,什么所有请求都会经过controlServlet,明明就不是,但是,到这个时候我都还在怀疑是不是我调试器出问题了(因为我之前就遇到过断点不能触发的情况),所以现在还不敢确定是不是那篇文章写错了,毕竟还是一个比较权威的单位发的文章。

我在这里研究了controlServlet很长时间,但是怎么都想不通那篇文章中提到的controlServlet中权限验证机制和这个漏洞有什么关系,因为断点都不会触发,八竿子打不着呀

于是我暂时否定了那篇文章中的分析,换了个思路,我已知这个漏洞就是httpd与tomcat解析差异导致的(但是这个差异体现在什么地方,或者说这个差异为什么导致了权限绕过,我不清楚),我心想,既然是解析差异,那么应该是httpd把我的请求当成一个普通请求放行了,但是在tomcat端它看到的却是一个敏感请求,即

我们发送的:

/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp

httpd看到的:

/tmui/login.jsp/

tomcat看到的:

/tmui/tmui/locallb/workspace/tmshCmd.jsp

上面是我臆想的一个请求过程,具体是不是这样,还有待求证,我需要知道httpdd与tomcat在处理url的具体差异是怎样的,要知道他们的差异,首先,我需要了解他们是怎么配合起来的

通过搜索,了解到他们是通过mod_proxy模块建立起连接的,具体怎么配置,这篇文章中写的很详细:

https://www.cnblogs.com/f-ck-need-u/p/8414043.html

我找到mod_proxy模块的配置文件:

可以看到这就是httd这个代理的转发规则,而且我知道了他们是通过ajp协议通信的,所以,为了搞清楚他们的解析差异,我在和ajp通信相关的代码处下了断点

在调试过程中,我发现了一个现象完全推翻了之前那篇文章中的分析,因为那篇文章分析是说,servlet的权限验证机制导致了漏洞的发生,也就是说他们认为是big-ip这个产品的代码写的有问题导致权限绕过,但是我发现,当我直接在浏览器中输入以下目录并请求时

/tmui/tmui/locallb/workspace/tmshCmd.jsp

ajp处的断点没有触发,而当我请求/tmui/login.jsp时ajp断点触发,我一开始以为是我的调试器又出问题了,所以用tcpdump在虚拟机上抓了下lo网卡下8009端口(ajp监听端口)的数据包

请求login.jsp时

burp下直接请求/tmui/tmui/locallb/workspace/tmshCmd.jsp时,注意,这里不能用浏览器访问,因为浏览器访问会直接跳转到login.jsp,会影响实验结果

可以看到当访问一个需要授权的文件时,没有抓到包,也就是说请求根本就没有到达tomcat,所以,此时我就意识到可能权限验证根本就是在httpd实现的,而不是那篇文章中所说的在controlServlet,终于,感觉看到点曙光了

当然,这只是一个猜测,我还需要找到证据来佐证,于是又开始翻配置文件,找到httpd.conf,可以看到

当我看到auth字样出现时,我就觉得八九不离十了,然后我搜了一下,这个就是httpd使用了pam认证,但是比较奇怪的是,我印象中的pam认证不都是那种 当你访问到需要权限的目录时浏览器弹出个框框让你输入密码吗,为什么BIG-IP这个是直接跳转到login.jsp页面,并且为什么login.jsp不需要登陆就可以访问,httpd.conf中配置的不是/tmui目录下所有的页面都需要权限验证吗?

我猜想可能是做了配置(废话,除了配置还能干嘛)

但是具体在哪里配置了我想不明白,然后就继续网上冲浪呗,看看能不能捡到答案呗

结果还真让我捡到了宝贝

原来,BIG-IP实现了自己的PAM认证模块,mod_f5_auth_cookie.so,在这个模块中设置了login.jsp不需要授权就可访问,且他只对比了前16个字符,这也是绕过比较关键的一点

这就完全说的通了。

到此为止,漏洞原理已经摆在面前了,还记得橘子?师傅blackhat的议题吗?

https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf

小声bb:我前面所有的研究,只证明了一件事:权限验证是在httpd端,我tm直接裂开…

 

漏洞原理

下面具体说一下httpd与tomcat在处理URL时候的差异

tomcat是支持path parameters的,什么是path parameters呢?我们通常传参是类似于下面这种形式的query parameters:

https://xxx/a.jsp?file=xxx

参数通过问号与文件分隔,而path parameters则是通过分号隔离参数与路径,如下:

https://xxx/a.jsp;file=xxx

这个特性可是真的??

然后我们看一下httpd的源码,mod_proxy_ajp.c,代码是关于ajp通信的部分

代码会执行到ap_proxy_canonenc,这个函数在proxy_util.c文件,跟进

从注释中就可以看到,分号是可以作为http路径的一部分的,所以当我们请求

https://xxx/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp

传递到tomcat的还是,不会有任何处理

https://xxx/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp

这个通过抓包或者调试就可以看到

接下来就是tomcat的事儿了,tomcat通过ajp协议在拿到httpd传过来的请求时,会对该请求进行一系列处理,其中在catalina.jar!/org/apache/catalina/connector/CoyoteAdapter.class的parsePathParameters对path parameters进行了处理,直接把path parameters删除掉了,就是下面这个for循环去掉了path param

然后,把值赋给uriBC,可以看到赋值过后,uriBC的值为:

uriBC从

/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp

变成了

/tmui/login.jsp/../tmui/locallb/workspace/tmshCmd.jsp

少了个分号,接下来tomcat会对这个uri进行规范化,也就是规范成

/tmui/tmui/locallb/workspace/tmshCmd.jsp

接着,就是根据web.xml中的配置找到该URL对应的servlet,并把请求交给这个servlet,/tmui/tmui/locallb/workspace/tmshCmd.jsp对应的servlet为

然后在tmshCmd_jsp执行了我们传入的命令

至此整个流程结束。

 

其他

可以看到,漏洞原理其实很简单,只不过你永远不知道你会被什么东西绊一下,我也是没想到我居然被一篇错误的文章折腾了好几个小时

回想一下,可能漏洞作者挖这个洞根本就没看代码,只是偶然发现它是一个反向代理的架构,然后用了这么一个绕过手法测试了一下,结果没想到,真就捡到了一个漏洞

 

参考文章

这篇文章分析的很好,本文关于c语言反编译那一块的图片都是来自它,遗憾的是它是我在写这篇文章的前一天才发出来的,要是早看到这篇文章,就不会走那么多弯路了,呜呜呜

https://research.nccgroup.com/2020/07/12/understanding-the-root-cause-of-f5-networks-k52145254-tmui-rce-vulnerability-cve-2020-5902/

(完)