从HTTPD-SSRF CVE-2021-40438起手的RCE漏洞挖掘
Apache官方在 2021-09-16更新发布Apache Httpd 2.4.49版本,在这个版本中修复了CVE-2021-40438、CVE-2021-33193、CVE-2021-34798、CVE-2021-36160、 CVE-2021-39275等5个漏洞。其中CVE-2021-40438 为mod_proxy模块下的SSRF漏洞,影响版本为2.4.48及以下。根据官方描述来看此漏洞是Apache内部安全团队在分析CVE-2021-36160 mod_proxy_uwsg DoS 时无意发现的,由于mod_proxy模块为Apache HTTP Server实现了基础代理/网关功能,支持很多常见协议如http、ajp、fastcgi、ftp等,所以该模块在很多通用系统中都会使用到,漏洞的影响范围是相当广的。
另外笔者在后续对多个使用mod_proxy模块的大型应用系统分析时,大部分都存在此安全问题,且如果可以找到存在敏感功能的后端Unix Domain Socket/本地服务,那么就可以利用代理漏洞请求后端敏感服务 组合串联最终达成RCE的效果。本文只做思路分享、技术交流,不提供任何形式的EXP!
文章前半部分为Apache Httpd的调试环境搭建与漏洞分析,后半部分是对该漏洞在实际挖掘场景中的一些思考与总结。 如果对文章有疑问/建议或者想一起研究交流的师傅,欢迎私信😜
PS:本文仅用于技术讨论。严禁用于任何非法用途,违者后果自负。
1、安装部署
本次演示部署了2.4.43版本的Apache Httpd,并且使用vscode远程调试linux下的Apache Httpd服务。这部分内容参考了P师傅星球的文章:https://t.zsxq.com/RvfmEu3 、https://t.zsxq.com/7qNfeie 致敬感谢分享!
1.1 linux部署环境
下载2.4.43版本的源码自行编译,另外此次漏洞涉及到了apr、apr-util的函数,所以apache在指定编译时需要指定apr和aprutil
1 |
|
编译
1 |
|
编译完成后开始配置httpd-bin/conf/httpd.conf
,将proxy_module.so和mod_proxy_http.so注释去掉,且在文件末尾增加如下配置
1 |
|
1.2 远程调试Httpd
vscode插件下载地址:
https://marketplace.visualstudio.com/search?term=remote&target=VSCode
然后安装一下调试C时所需要的VSCode扩展:
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
离线安装remote-ssh插件
https://www.cnblogs.com/litaozijin/p/13202992.html
在vscode调试界面创建配置文件 launch.json
1 |
|
关闭远程服务器的httpd服务,使用本地vscode的gdb重新启动即可
断点下在proxy_util.c#fix_uds_filename(),访问地址 http://192.168.232.134/link 成功断到
2、漏洞分析
2.1 安全公告
CVE-2021-40438 为mod_proxy模块的SSRF漏洞,官方安全公告影响版本为2.4.48及以下
https://httpd.apache.org/security/vulnerabilities_24.html
2.2 补丁对比
CVE-2021-40438 修复的committed: https://github.com/apache/httpd/commit/520dcd80a45ce237e9a46ee28697e1b8af3fcd7e?diff=split
我们查下这两个函数的用法
ap_strcasestr(s1,s2):从s1中搜索s2, 返回s2开始的指针,不考虑大小写,相同则返回0
ap_cstr_casecmpn(s1,s2,n): 对比s2与s1的前n位,不考虑大小写,相同则返回0
函数定义在
简单解释下此次补丁变化:原来是从s1整个字符串搜索”unix:”,现在限定只能是前5位,也就是s1需要以”unix:”开头。我们查看下漏洞函数proxy_util.c#fix_uds_filename()
方法代码
1 |
|
代码从r->filename中根据unix:
及|
的位置解析出uds路径及url,并且判断r->filename是否以proxy:
开头
2.3 原理分析
请求代理路由/link
,看到了这样一条日志URI path '/link' matches proxy handler 'proxy:http://127.0.0.1:8000/'
根据该日志信息找到了mod_proxy.c#ap_proxy_trans_match(),再次发送后,看到r->filename的值为proxy:http://127.0.0.1:8000/
,代理请求r->proxyreq
为反向代理PROXYREQ_REVERSE
这样就解释了为什么漏洞函数proxy_util.c#fix_uds_filename()
需要先检测是否以proxy:
开头。我们查看apache mod_proxy uds的正常用法
https://www.docs4dev.com/docs/zh/apache/2.4/reference/mod-mod_proxy.html
apache从 2.4.7 开始支持了uds(但经过实际测试,我在2.4.9才找到了支持uds路径的写法),具体用法是ProxyPass /links "unix:/home/www.socket|http://localhost/whatever/"
,当访问/links 代理配置经过proxy_util.c#fix_uds_filename()
解析后,uds路径为/home/www.socket
及转发url为http://localhost/whatever/
,结合补丁代码修改部分,我们将uds代理字符串添加到参数位置进行测试:/link?unix:/home/www.socket|http://localhost/whatever/
可以看到经过proxy_util.c#fix_uds_filename()解析处理,成功赋值给uds_path及转发url。到这里漏洞触发点就明了了:当目标apache使用代理模块时,攻击者通过在代理路由的参数注入uds路径及转发url实现任意uds请求/url请求。现在可以请求任意uds地址。但是距离任意SSRF,还有一个问题:在经过fix_uds_filename()解析出uds、url后,会在mod_proxy_http.c#ap_proxy_determine_connection()中去请求uds/url,当uds不存在时,才会去请求url
所以问题就变成了如果使解析出来的uds值为null,老外使用了一个巧妙的方法,在fix_uds_filename()解析uds的代码:
1 |
|
config.c#ap_runtime_dir_relative()
在filepath.c#apr_filepath_merge()中,当rootpath
(/root/apache/httpd-bin/logs)与传入的路径长度之和+4大于4096时,返回名称过长错误。就会导致代码走到1625行,返回uds_path的值为null
所以在unix:与|之间传入大于(4097-4-len(rootpath))=4066个字符时就会产生SSRF漏洞,可以看到服务端成功请求了地址: http://localhost:8001/whatever/
这样,漏洞就可以达到SSRF、任意uds请求的效果,接着看看这个漏洞在实际漏洞挖掘场景中的情况
3、实际场景
该漏洞触发的条件是Apache Httpd开启mod_proxy模块,使用到反向代理且路由可以从前台访问。如果目标系统满足条件,就可以从前台请求到服务端存在敏感功能的uds/服务,完成一个前台漏洞的触发串联。本章节演示这个漏洞在实际场景中的一些情况
3.1 特殊配置
在测试过程中发现存在一种特殊配置,当路径以/结尾,但是代理地址不以/进行结尾时,可以通过@绕过代理触发SSRF漏洞,这里记录一下。如果平时测试碰到/admin/module//login
类似路径,可以使用/admin/module/@127.0.0.1:8833
测试,如果恰好httpd使用的路径以/结尾,但是代理地址不以/进行结尾的特殊配置时,目标机器会触发ssrf漏洞请求127.0.0.1:8833
1 |
|
3.2 漏洞思考?
在分析完CVE-2021-40438后感觉这个漏洞的影响是被低估的,该漏洞能覆盖影响Httpd 2.4.9-2.4.48 近40个小版本、mod_proxy ProxyPass配置很常见、代理也多为前台触发访问,非常适合做为漏洞挖掘绕过权限的突破口 。当时正在研究Vmware家的Vmware Vrealize operations产品,也跟进测试了最新版,发现部署的是httpd 2.4.46版本、配置使用了代理模块、路由也可以从前台触发。所以最新版是受CVE-2021-40438影响的,但是当时并没有找到后端可用的uds完成rce,比较遗憾。只给vmware发了ssrf的报告,后面官方出了安全更新升级了httpd的版本
官方出的公告:https://kb.vmware.com/s/article/87227
复现截图
3.3 向RCE进发
由于在vmware产品中没有找到可利用的uds完成RCE,比较遗憾但是并没有放弃这个攻击面。后续也审计了几款应用,最终成功在XXX与XXX产品中串联起来完成了RCE。如果想要完成RCE的效果需要串联后端的敏感服务,其中WEB服务的审计与平时审计思路差别不大,而自己对于uds部分的知识还是欠缺的,所以利用这次机会做一波知识储备。扩大知识面->知识面决定攻击面!
在查找资料过程中看到百度安全团队在blackhat 2022大会上对安卓上得Unix Domain Socket 攻击面做了梳理, 可以作为知识补充学习一波。议题名称:Unix Domain Socket: A Hidden Door Leading to Privilege Escalation in the Android Ecosystem 议题paper:https://developer.baidu.com/article/detail.html?id=295123 议题slides:https://i.blackhat.com/Asia-22/Thursday-Materials/AS-22-Ke-Unix-Domain-Socket-A-Hidden-Door.pdf
unix域套接字uds(Unix Domain Socket)与传统网络套接字得主要区别是:网络套接字使用ip与端口标识客户端与服务端,而域套接字uds使用系统文件名,这个文件被称为套接字文件。套接字文件有两种形式:一种是绝对路径 类似/var/rpc/rpc.sock;另外一种是抽象路径 类似@jd-control,即一个不存在得路径。这里整理了几个与uds相关的命令
1 |
|
在审计XXX系统时,发现其使用了Apache httpd 2.4.41版本,在/apache/conf/httpd.conf中看到配置启动了代理模块mod_proxy(使用指令ProxyPass、ProxyPassReverse),且代理路由可以从前台访问,满足该漏洞的条件要求
接下来就是寻找能够串联起来完成利用的后端服务,本次审计主要聚焦于uds。lsof -U
命令用于显示Unix Domain Socket (UDS)的相关信息,包括哪些进程正在使用指定的UDS文件以及它们的详细信息
看到有个xmlrpc.sock、xmlrpc0.sock、xmlrpc1.sock,检索下相关进程
这个是python启动的后端进程,而该系统的WEB前台XXX敏感功能处与后端的xmlrpc uds有交互请求
那只要能拿到最终请求到uds的数据包,我们就可以绕过WEB侧鉴权转而利用httpd侧代理漏洞请求uds完成漏洞串联利用。尝试了很多方法,最终通过创建中转流量的8089端口成功抓到了uds的数据包。在过程中使用tcpdump抓取xmlrpc.sock的流量发现为空,在查看了cfg配置文件结合上面的进程信息发现还有xmlrpc0.sock与xmlrpc1.sock,随即对这两个sock也进行了监听,最后成功在xmlrpc1.sock中发现了请求uds的流量
1 |
|
在监听uds后返回到web界面,手动点击操作XX功能后,lo-test.pcap就有了内容,使用wireshark打开pcap包筛选host就能找到具体的数据包
返回数据:
那么将该数据包转为使用httpd代理漏洞的格式发送给uds即可完成漏洞利用,第一次在这里测试时多次碰到返回502、503、408等错误状态码的情况。为了定位在httpd c代码中的具体报错信息,我这里手动修改配置文件httpd-ssl.conf与httpd.conf,开启日志记录并重启httpd,配置信息如下
1 |
|
最终成功构造出从前台直接请求后端uds的数据包,完成漏洞利用
Just For Fun!
4、参考链接
https://firzen.de/building-a-poc-for-cve-2021-40438
https://www.leavesongs.com/PENETRATION/apache-mod-proxy-ssrf-cve-2021-40438.html
https://mivehind.net/2018/04/20/sniffing-unix-domain-sockets/