
Документация по настройке лаборатории и POC для CVE 2021 41773
Документация по настройке лаборатории и PoC для CVE-2021-41773
CVE-2021-41773 — это уязвимость обхода пути в сервере Apache HTTP версий 2.4.49 и 2.4.50. Данная уязвимость использует изменение в коде нормализации пути, введённое в этой версии.
Функция нормализации пути:
Обычно нормализация пути фильтрует URL-путь до стандартного формата,
чтобы предотвратить злонамеренные действия противников.
Apache HTTP Server 2.4.49 представил изменение в функции ap_normalize_path, которое стало коренной причиной этой уязвимости.
Если мы посмотрим на исходный код, то заметим, что функция перебирает каждый символ в переданном URL и применяет санитизацию.
Но уязвимость присутствует в том участке кода, где выполняется декодирование URL. Функция проста: она просто декодирует URL-закодированный символ
if ((flags & AP_NORMALIZE_DECODE_UNRESERVED) &&
path[l] == '%' &&
apr_isxdigit(path[l + 1]) &&
apr_isxdigit(path[l + 2]))
{
// Decode the percent-encoded character
const char c = x2c(&path[l + 1]);
// Check if the decoded character is alphanumeric or one of the allowed symbols
if (apr_isalnum(c) || (c && strchr("-._~", c)))
{
// Replace the last character with the decoded one and update position
l += 2;
path[l] = c;
}
}
Проблема здесь в том, что он обрабатывает только первую точку . в URL, то есть если вместо ../ мы передадим .%2e/, сервер декодирует %2e в точку, что преобразуется в ../
Normal case:
URL Input: http://target/cgi-bin/../../etc/passwd
Path Normalization Steps:
1. Detect ../ -> Attempt to traverse up a directory.
2. Normalize function -> will remove or block ../
Vulnerable case:
URL Input: http://target/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd
Path Normalization Steps:
1. Decodes %2e to . -> The Result is ./.././../etc/passwd
2. Partial Normalization -> Does not recognize .%2e/ as equivalent to ../
3. Path Traversal is not fully blocked.
Resulting Path: /etc/passwd (Access Granted)
Эта проблема становится опасной и эксплуатируемой в сочетании с серверными директивами. Они действуют как правила поведения сервера Apache.
Конфигурация Require all granted разрешает все запросы на доступ к ресурсам внутри DocumentRoot явно.
<Directory />
AllowOverride None
Require all granted # Deliberately vulnerable setting, here it was denied usually
</Directory>
Если сервер настроен с директивой Require all granted на корневом уровне, это делает всю файловую систему общедоступной.
Директория cgi-bin в Apache по умолчанию является псевдонимом с директивой Require all granted, что разрешает публичный доступ, то есть любой может обращаться к директории /usr/local/apache2/cgi-bin/.
Объединив логическую ошибку в функции ap_normalize_path, позволяющую обход пути, и неправильно настроенную директиву Require all granted на сервере, злоумышленники могут получить доступ к файлам файловой системы сервера за пределами предназначенных директорий.
Эта уязвимость может быть дополнительно использована для удалённого выполнения кода, если на сервере включён mod_cgi.
По умолчанию этот модуль не включён в Apache HTTPD, что означает, что версия по умолчанию не уязвима для RCE.
mod_cgi позволяет выполнять CGI-скрипты (Common Gateway Interface) на сервере и возвращать результат клиенту; он используется в основном для обеспечения динамических функций веб-сайтов.
Скачайте уязвимую версию Apache (скачиваем из архива, так как уязвимая версия недоступна для прямой установки):
wget https://archive.apache.org/dist/httpd/httpd-2.4.49.tar.gz
установите зависимости:
sudo apt-get install libapr1 libapr1-dev libaprutil1 libaprutil1-dev
sudo apt-get install build-essential
Распакуйте уязвимый файл Apache и настройте его:
tar -xvf httpd-2.4.50.tar.gz
cd httpd-2.4.50
./configure
make
sudo make install
После успешного завершения перейдите к файлу конфигурации Apache:
sudo nano /usr/local/apache2/conf/httpd.conf
Добавьте следующее в файл конфигурации:
ServerName 127.0.1.1
Запустите службу Apache:
sudo /usr/local/apache2/bin/apachectl start
Перейдите в каталог веб-сервера по умолчанию:
cd /usr/local/apache2/htdocs
Примечание: обычно корневой каталог Apache — /var/www/html, здесь это /usr/local/apache2/htdocs, потому что мы не устанавливали сервер из исходников. Мы можем сделать его /var/www/html, если хотим. Но пока я оставляю как есть.
Создайте простой статический сайт:
HTML:
echo "GNU nano 6.2 index.html *
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CVE-2021-41773</title>
<!-- Link to external CSS file -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="noise"></div>
<div class="overlay"></div>
<div class="terminal">
<h1>Error <span class="errorcode">404</span></h1>
<p class="output">This is a replication of CVE-2021-41773</p>
<p class="output">Exploit <a href="https://nvd.nist.gov/vuln/detail/cve-2021-41773"> the vulnerability</a> or <a href="https://www.hackthebox.com/blog/cve-2021-41773-explained">Learn more about it </a> </p>
<p class="output">Good luck.</p>
</div>
</body>
</html>" | sudo tee index.html
CSS:
echo "@import 'https://fonts.googleapis.com/css?family=Inconsolata';
html {
min-height: 100%;
}
body {
box-sizing: border-box;
height: 100%;
background-color: #000000;
background-image: radial-gradient(#11581E, #041607), url("https://media.giphy.com/media/oEI9uBYSzLpBK/giphy.gif");
background-repeat: no-repeat;
background-size: cover;
font-family: 'Inconsolata', Helvetica, sans-serif;
font-size: 1.5rem;
color: rgba(128, 255, 128, 0.8);
text-shadow:
0 0 1ex rgba(51, 255, 51, 1),
0 0 2px rgba(255, 255, 255, 0.8);
}
.noise {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
background-image: url("https://media.giphy.com/media/oEI9uBYSzLpBK/giphy.gif");
background-repeat: no-repeat;
background-size: cover;
z-index: -1;
opacity: .02;
}
.overlay {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
background:
repeating-linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.3) 50%,
rgba(0, 0, 0, 0) 100%);
background-size: auto 4px;
z-index: 1;
}
.overlay::before {
content: "";
pointer-events: none;
position: absolute;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(
0deg,
transparent 0%,
rgba(32, 128, 32, 0.2) 2%,
rgba(32, 128, 32, 0.8) 3%,
rgba(32, 128, 32, 0.2) 3%,
transparent 100%);
background-repeat: no-repeat;
animation: scan 7.5s linear 0s infinite;
}
@keyframes scan {
0% { background-position: 0 -100vh; }
35%, 100% { background-position: 0 100vh; }
}
.terminal {
box-sizing: inherit;
position: absolute;
height: 100%;
width: 1000px;
max-width: 100%;
padding: 4rem;
text-transform: uppercase;
}
.output {
color: rgba(128, 255, 128, 0.8);
text-shadow:
0 0 1px rgba(51, 255, 51, 0.4),
0 0 2px rgba(255, 255, 255, 0.8);
}
.output::before {
content: "> ";
}