CVE-2019-16278 - Unauthenticated Remote Code Execution in Nostromo web server

Hi,
Welcome to my blog!

In this post, I will analyze CVE-2019-16278, how to exploit and why it vulnerable. This CVE is about the Nostromo web server, aka nhttpd, an open-source web server that is very popular on Unix system like FreeBSD, OpenBSD,...

Nostromo fails to verify a URL that leads to path traversal to any file in the system. An unauthenticated attacker can force the server points to a shell file like /bin/sh and execute arbitrary commands. It's critical due to all Nostromo's versions, include the lasted release 1.9.6, are vulnerable. Even its developers' website, www.nazgul.ch, is exploitable.

In the past, Nostromo had also a path traversal which could lead to unauthenticated remote code execution named CVE-2011-0751. The web server checked for the string /../ before decoded escaped characters in the URI and the RedTeam Pentesting GmbH found it. They just encoded the character / to %2f and passed it to the server like this:
/..%2f..%2f..%2fbin/sh

Although Nostromo fixed CVE-2011-0751 in version 1.9.4 by decoding escaped characters before checking for the string /../, it's still bypassed by sp0re and we have CVE-2019-16278, the return of path traversal in Nostromo.

Exploit in the wild:

Searching on Shodan, you can find about 2000 Nostromo web servers exposed to the Internet by using the query:
"Server: nostromo"

Let's use my PoC written in Python to check some servers. In fact, all of them are vulnerable,.

If you look at my exploit, you can see I send a request with /.%0d./.%0d./.%0d./.%0d./bin/sh in the URI. Compare to CVE-2011-0751, I just change %2f to %0d, which is not checked by Nostromo. You may wonder how the system can traverse to the upper directory by a path contains the carriage return (CR) like this .%0d./.%0d./, instead of ../../. Actually, the system receives ../../ without any CR characters. So, where those CR characters have gone?
Let's take a look at the source code of Nostromo version 1.9.6.

Analysis:

First, when a request header comes, it is verified and then processed.

In the function http_verify(), the header is URL decoded and then check for the existing of /../.  The header now will be: /.\r./.\r./.\r./.\r./bin/sh.

Next, in the function http_proc(), the header is passed to the function http_header().

The root cause is here, in the function http_header(), it parses the header by the function strcutl().

Take a look inside the function strcutl(), you can see it take a string and return it with \r (carriage return) is cut off.

Now the path is  /../../../../bin/sh. So, the path traversal part is done, how it can be executed?
Because of the function execve() in the function http_proc(). It executes rh->rq_filef, which is indeed the path /../../../../bin/sh.

Now, we have a path traversal to RCE!

If you find it valuable, please share it with other people.
If you have any questions, please don't hesitate to ask me on Twitter or leave a comment.
Thank you for reading!

Comments