Apache HTTP Server 请求走私漏洞 CVE-2023-25690 调试分析
[toc]
Apache HTTP Server 请求走私漏洞 CVE-2023-25690
漏洞描述
Apache HTTP Server 版本 2.4.0 到 2.4.55 上的某些 mod_proxy 配置允许 HTTP 请求走私攻击。
启用 mod_proxy 以及特定配置的 RewriteRule 或 ProxyPassMatch 模块时,当规则与用户提供的URL的某些部分匹配时,会因为变量替换从而造成代理请求目标错误
例如以下配置
1 | RewriteEngine on |
此漏洞会造成请求拆分和走私,引起权限绕过,缓存投毒等攻击
影响版本
2.4.0 <= Apache HTTP Server <= 2.4.55
环境搭建
操作系统使用 Ubuntu 20.04
安装依赖
安装依赖,包括我们编译软件所需要的build-essential,以及调试C程序所需要的gdb,以及Apache所依赖的几个第三方库:
1 | sudo apt-get install build-essential gdb |
下载源码
去apache官网下载2.4.55版本源码,Index of /dist/httpd (apache.org)
还要下载apr-1.7.2和apr-util-1.6.3的源码, Index of /apr (apache.org)
解压
1 | tar -xvzf httpd-2.4.55.tar.gz |
编译安装
编译安装apr,要开启调试,我们在编译时需要制定环境变量CFLAGS=”-g”
1 | CLFAGS="-g" ./configure --prefix=/root/workspace/apache-bin/apr |
编译安装apr-util,指定apr路径
1 | CLFAGS="-g" ./configure --prefix=/root/workspace/apache-bin/apr-util --with-apr=/root/workspace/apache-bin/apr |
编译安装httpd,指定apr路径和apr-util路径
1 | CFLAGS="-g" ./configure --prefix=/root/workspace/apache-bin/httpd --with-apr=/root/workspace/apache-bin/apr --with-apr-util=/root/workspace/apache-bin/apr-util |
远程调试配置
进入/root/workspace/apache-bin/httpd目录,安装vscode的cpp扩展
然后添加launch.json配置文件
1 | { |
其中,program是我们需要调试的二进制文件,我这里就是bin/httpd
args是运行时传递的参数,我传了两个参数,-DFOREGROUND
的作用是让Apache运行在前台。-X
的作用是只启动一个进程,Apache本身是一个多进程的Web服务器,调试的时候会产生干扰,所以指定-X非常重要。
cwd是指定运行时的目录
漏洞分析
httpd.conf配置
进入/home/oyzy/workspace/apache-bin/httpd
修改conf/httpd.conf,开启添加以下模块
1 | LoadModule proxy_module modules/mod_proxy.so |
在8000端口开启一个反代服务
1 | <VirtualHost *:8000> |
设置日志等级,记录mod_rewrite和mod_proxy日志 LogLevel alert rewrite:trace3 proxy:trace8
,可以在error.log中查看
RewriteRule可以参考mod_rewrite模块的文档
https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
末尾的 [P] 会将请求发送给mod_proxy模块,让apache生成一个request去请求目标后端服务器,也就是这里的10.122.255.252
而漏洞就发生在mod_rewrite.c的hook_uri2file函数中,当我们的uri匹配到正则时,就会进行RewriteRule规则替换,然后准备一个新的请求交给mod_proxy
正常功能
我们可以打断点在modules/mappers/mod_rewrite.c的4693行,然后发送如下请求包
1 | GET /hello/abc HTTP/1.1 |
这里正在解析我们的请求,取出thisserver,port,thisurl
继续来到4717行,这里是应用rewrite规则的地方,此时左边的r->filename的值和r->uri保持一致
当经过apply_rewrite_list函数后,r->filename会变成"proxy:http://10.122.255.252/index.php"
,这里的写法可以参考mod_proxy模块,在上一篇的文章也有提到,然后我们的请求参数r->args也会被替换成"name=abc"
,也就是配置文件中写的get传参
之后会判断r->filename是不是以proxy:
开头
再设置r->handler为"proxy-server"
,之后就会交给mod_proxy处理
最后收到的响应如下
1 | HTTP/1.1 200 OK |
漏洞点
以上整个流程中,我们的可控点只有r->args,那么可以考虑从这里入手,如果我们访问的uri中带有控制字符,就有可能控制发送给mod_proxy的请求体,从而造成请求走私,类似于CRLF注入
尝试发送如下请求,uri中携带控制字符
1 | GET /hello/abc%20qqq%0d%0aABC:%20ccc HTTP/1.1 |
经过apply_rewrite_list函数后,我们的r->args被设置成了name=$1
的形式,而$1
也就是r->uri中匹配"^/hello/(.*)"
的部分,值得注意的是,这里的r->uri是经过了url解码的,我们的控制字符都被解析了,这里就让我们有机会进行CRLF注入
我们可以nc来接受以下最终发给后端服务器的请求包
这里的r->args,也就是阴影部分,会被直接拼接到请求报文中发给后端服务器,造成了请求走私
利用方式(无回显走私)
最终可控的部分在整个请求头的中间,我们可以在pre.txt中准备一个要走私的请求
写个脚本处理一下合成我们最终的请求,并用socket发送
1 | import urllib |
记录请求包和响应包
在实际利用过程中,会发现这里的走私,没有产生响应队列中毒的效果,收到的请求始终是我们请求包中的第一个请求的响应
可以用wireshark来分析一下具体的过程,这一次请求一共有四个http包
- 客户端发送给代理服务器Apache的请求
- Apache向后端服务器发起请求(这里包含三个)
- 后端服务器给Apache的响应包(同样是三个)
- Apache给客户端的响应(只有第一个响应)
追踪TCP流
我们的三个请求都被后端服务器处理,并且得到了三个响应,但最终只有第一个响应发送给了客户端,成功走私,但是无法看到回显
这种现象和后端服务器有关,以上使用的是Apache 2.4.39
同样测试了nginx和tomcat,虽然服务端都处理了三个请求,但最后发给客户端的都只有第一个
nginx 1.15.11
tomcat 7.0.79
本人最终只能达到无回显走私的效果,如果有师傅研究过这个问题可以和我探讨
修复分析
参考github上的修复
don’t forward invalid query strings · apache/httpd@d78a166 (github.com)
在mod_rewrite中对r->args进行判断,如果是控制字符则会报错
同时在mod_proxy的几个模块中也进行一模一样的判断
在请求发给mod_proxy后又对r->args进行了判断,无法进行CRLF注入
Reference
https://httpd.apache.org/security/vulnerabilities_24.html
https://github.com/apache/httpd/commit/d78a166fedd9d02c23e4b71d5f53bd9b2c4b9a51