
The POC and Lab setup documentation of CVE 2021 41773
The POC and Lab setup documentation of CVE 2021 41773
CVE-2021-41773 is a path traversal vulnerability in Apache HTTP server 2.4.49 and 2.4.50. This vulnerability exploits an update in the path normalisation code introduced in this version.
The Path Normalisation Function:
Normally Path normalisation will filter out the url path to a standard
format to prevent adversaries from malicious actions.
Apache HTTP Server 2.4.49 introduced a change to the ap_normalize_path function which became the root cause of this vulnerability.
If we take a look at the source code, we can spot that the function iterates through each character in the given url and apply sanitisation.
But the vulnerability is present in the section of code, where it does url decoding. The function is simple, it will just decode the url encoded character
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;
}
}
The issue here is that it only handles the first dot . in url, which means if instead of ../ , we give .%2e/, server will decode the %2e and as dot which will translate to ../
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/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)
This issue becomes dangerous and exploitable when it is combined with server directives. This act as a rule for the behaviour of Apache server.
The Require all granted configuration will all requests to access resources inside the DocumentRoot explicitly.
<Directory />
AllowOverride None
Require all granted # Deliberately vulnerable setting, here it was denied usually
</Directory>
If the server is configured with the Require all granted directive at the root level, this makes the whole file system publicly accessible.
The cgi-bin directory in Apache is by default an alias directory with the Require all granted directive, which allows public access, meaning everyone can request to the /usr/local/apache2/cgi-bin/ directory.
By combining the logic flaw in the ap_normalize_path function that allows the path traversal bypass and a misconfigured Require all granted directive on the server, attackers can access files on the server files system outside intended directories.
This vulnerability can be further exploited leading to Remote Code Execution if mod_cgi is enabled on the server.
By default this module is not enabled on Apache HTTPD, this means that the default version is not vulnerable to RCE.
mod_cgi allows the execution of CGI (Common Gateway Interface) scripts on the server and the output returned to the client; it's used mainly to provide dynamic features to websites.
Download vulnerable apache version(Downloading it from archive cause the vulnerable version is not available to install directly):
wget https://archive.apache.org/dist/httpd/httpd-2.4.49.tar.gz
install dependencies:
sudo apt-get install libapr1 libapr1-dev libaprutil1 libaprutil1-dev
sudo apt-get install build-essential
Decompress the apache vulnerable file and configure it:
tar -xvf httpd-2.4.50.tar.gz
cd httpd-2.4.50
./configure
make
sudo make install
After it finishes successfully go to the configuration file for apache:
sudo nano /usr/local/apache2/conf/httpd.conf
Add the following on the configuration file:
ServerName 127.0.1.1
Start the apache services:
sudo /usr/local/apache2/bin/apachectl start
Navigate to default web server directory:
cd /usr/local/apache2/htdocs
note: normally root directory for apache is /var/www/html here its /usr/local/apache2/htdocs cause we didn't install the server form source, we can make it /var/ww/html if we want. But I am keeping it as it is for now.
Create a basic static website:
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: "> ";
}