2021 最新 Apache 漏洞分析
0x01 漏洞简介
2021 年 9 月 16 日,Apache 官方发布了 Apache httpd mod_proxy SSRF 漏洞 CVE-2021-40438,影响 v2.4.48 及以下版本。该漏洞影响范围较广,危害较大,利用简单,不得不引起重视。
0x02 漏洞搭建
docker 部署详见https://github.com/BabyTeam1024/CVE-2021-40438开启 mod_proxy 模块
启用 vhost 配置文件
/etc/httpd/extra/httpd-vhosts.conf 配置文件内容如下
使用 gdb 挂在 httpd 进程即可进行源码调试,如下图所示
0x03 漏洞分析
【一>所有资源获取<一】1、200 份很多已经买不到的绝版电子书 2、30G 安全大厂内部的视频资料 3、100 份 src 文档 4、常见安全面试题 5、ctf 大赛经典题目解析 6、全套工具包
0x1 问题梳理
老方法,在漏洞分析之前首先问自己几个问题,带着这些问题去调试思考,最终通过努力将会解答自己的疑惑。面对这个漏洞以及利用 poc,笔者有如下几个问题
1.apache 代理关键逻辑是哪块函数处理的,从漏洞挖掘角度该怎么思考 2.为什么可以通过 HTTP 数据包覆盖配置文件里的代理路径 3.为什么需要这么多字符才能覆盖为 http 代理
0x2 如何分析 Apache 代理模块
Apache 配置的代理模式,其实是一个 Apache Proxy 组件。该组件也是通过挂钩函数注册在 Apache 程序中,其中处理函数为 proxy_handler,钩子的注册函数为 ap_hook_handler,钩子的执行函数为 ap_run_handler。可以在/modules/proxy/mod_proxy.c 函数中找到 apache proxy 注册的身影
那么何时调用呢?这个其实在分析 CVE-2021-41773 Apache 路径穿越漏洞的时候就已经分析了,简单总结为在/modules/http/http_request.c 中的 ap_process_async_request 函数处理解析请求数据包
在/server/config.c 代码中存在如下处理逻辑
然而通过之前总结的《Apache 安全—挂钩分析》文章得知,ap_run_handler 在源码中并没有实际实现,而是通过宏定义的方式去实现,有兴趣的小伙伴可以去学习下。笔者的分析如下利用 ida 打开 httpd 程序,我们可以看到 ap_run_handler 的实具体现逻辑。
如上图所示 v1 为一个类似于虚表的结构,每 40 字节一个结构类型,处理函数的地址就存放在该结构的第一个 4 字节地址空间中。把断点下在第 15 行,通过下面的 gdb 脚本进行遍历打印
脚本执行结果如下
梳理一下大概有这几个钩子处理函数,然而这次漏洞就出现在了 proxy_handler 函数中
笔者通过查看汇编指令的方式查看地址对应的函数名
找到 proxy_handler 的调用过程后,主要分析 Apache 代理模块是如何进行代理的
0x3 Apache 代理功能分析
在/modules/proxy/mod_proxy.c 中的 proxy_handler 函数入口 r->filename 的值为下图所示
笔者发送的数据包为
显而易见,r->filename 采用了 proxy 路径之后的内容与 proxy:http://127.0.0.1:8888/进行拼接,继续往下分析。接下来一大段代码是首部字段 Max-Forwards 的处理逻辑,这里就不再讲解了。再之后的代码如下
此处的 uri 为http://127.0.0.1:8888/xxxx ,跟进 ap_proxy_pre_request 函数,最终来到了本次漏洞的主要函数fix_uds_filename
fix_uds_filename 函数开头部分判断了几个条件,是否为 proxy:开头,其中是否包含了 unix 和| ,如果进入 if 判断这几个条件缺一不可。
笔者采用的配置方式如下所示,因此不会进入 fix_uds_filename 函数主逻辑进行处理。
同样的如下配置也是不能够进入到主处理逻辑的,因为不包含|
其实细心的同学们已经发现 fix_uds_filename 的第二个参数为引用参数,当函数执行完之后会将结果会传给实参。再往上追溯 ap_proxy_pre_request 函数也是采用引用参数的形式获取 url 的值,worker 和 balancer 参数同理
那么究竟如何给这个 url 赋值呢?他到底有什么作用?即使配置成这样也是不能进入处理逻辑
经过前面函数的处理只保留了 http 部分内容,接下来就是漏洞 payload 构造环节了
0x4 为什么可以替换代理内容
那么可以猜想 xxxx 部分内容是否可控,比如发送如下数据包
在后端查看 log 日志显示内容如下,attempt to connect to Unix domain socket /tmp/xxxx,这说明已经把之前配置文件里代理到http://127.0.0.1:8888/链接的配置修改为了访问 Unix domain socket 套接字。
接着上一小节继续分析,这中间到底发生了什么导致从 GET url 传递的 path 路径最后经过 apache 的解析覆盖了配置里的路径。具体细节在 fix_uds_filename 写的很清楚
最后通过 fix_uds_filename 的一波操作后 r->filename 变为了新的代理 url。调试分析如下
函数入口处的 r->filename 为带有|分割的两个代理路径,最后通过以下两条语句获取 uds_path 和 rurl
可以看到代码的最后使用 memmove 函数替换 r->filename 6 字节之后的内容,如下图所示
细心读者可能会注意到一个为 9999 端口一个为/tmp/xxxx uds 文件,为什么最后代理的是 uds 文件而不是 9999 端口呢?这要看 proxy_handler 之后的代码。
0x5 如何将代理替换为 http 协议
在 mod_proxy 模块中也有钩子的使用,在 proxy_handler 代码中有一处如下函数调用。
如果不了解 apache 函数调用机制的话,不知道是如何调用过来的,如果跟踪调用栈的话,只知道从 proxy_run_scheme_handler 到 proxy_http_handler,虽然中间发生了很多事,但对调试者来说是透明的。和之前的钩子分析类似,scheme_handler 也是一种挂钩,笔者找到了声明和注册的相关代码。
注册挂钩函数内容
p 神在分析文章中提到的关键代码
那么如何让 uds_path 为空就是这个漏洞将要解决的问题,把目光继续转向生成 uds_path 的关键代码。
影响返回值的关键函数是 apr_filepath_merge,代码将会根据该函数返回值,返回相应的内容。
但是 apr 函数不在 apache 源代码里,通过查看 apr-1.6.3 源码可以分析其中的逻辑,该函数在 apr-1.6.3/file_io/unix/filepath.c
通过调试分析,该部分绕过就显而易见了,只需控制 maxlen>APR_PATH_MAX 即可,addpath 为 uninx://和|之间的字符串,所以在构造 payload 的时候只需将长度控制在一定大小就能让 Apache 转发给指定的 http 端口。到此该漏洞的大部分内容已经分析完了,该漏洞涉及到了大量的 httpd 调试技术,笔者在之前的分析文章中也有介绍。
0x6 如何修复
在 2.4.49 版本代码中使用了 ap_cstr_casecmpn,该函数不区分大小写的比较两个字符串的前 5 个字符。
这也就意味着只能是 proxy:unix: 开头的字符串才能进入该分支,然而这并不属于用户控制的范围。但是如果在原用配置中使用了 uds 配置,则会进入该分支,至于会不会产生漏洞,笔者没有进一步测试。
0x04 总结
该 ssrf 代理访问漏洞适用于 apache 的绝大多是版本,相对来说漏洞原理比较简单,隐藏了多年也是很不容易了,至于危害的话,笔者认为危害还是挺大的,可以访问到服务器内部一些不对外开放的 http 端口及 uds 文件。整个分析流程已经完整呈现在大家面前,很多借鉴了 p 神和 Firzen 文章里的分析步骤,如有问题多多指正。











评论