OverTheWire Natas Level 31 Walkthrough

This next level of Natas is another Perl-focused one. The attack vector used is from a 2016 presentation called “Perl Jam 2“, describing a 20 year old bug. Despite (or maybe because of?) how old the issue is, original documentation for the functions seems hard to find, and getting the syntax right is pretty tricky.

This walkthrough breaks down the logic behind the attack, and how to use Burp Suite to craft your own attack.

What is Natas?

Natas is an online hacking game meant to help you learn and practice security concepts.

OverTheWire is a website with a number of “war games”, which are online hacking games that allow you to practice security concepts. If you are looking for a beginner introduction to web security (albeit an older tech stack), then Natas is a great place to start.

Natas is hosted on different subdomains following the pattern of http://natas<level#>.natas.labs.overthewire.org. As you progress through the levels, you’ll need to increment the level number in the URL in order to view the correct level.

Each level requires the levels below it to be solved, so you will need the level 31 flag found in level 30 to begin this walkthrough. As before, make sure you keep notes and write down the passwords as you find them!

Level 31 ➔ 32

First things first, head over to http://natas31.natas.labs.overthewire.org and login with username natas31 and password hay7aecuungiuKaezuathuk9biin0pu1 from the last level.

The website offers us the chance to upload CSV files:

We’re also shown the source code. Here’s the relevant part:

In short, we can upload CSV (and other) files, and then the contents are displayed on the webpage in a nicely formatted table.

What didn’t work

My original thought was some kind of CSV injection attack, wherein you use values that start with = and other operators, which are then evaluated when the file is opened up. I read about these attacks and tried some payloads but no success. This is probably because the CSV form isn’t being opened in the way we need it to (nothing is being evaluated, it’s just being opened as a text file).

I also tried uploading other types of files, since do have file upload capabilities, but no luck. Similarly, our uploaded file isn’t being processed as anything executable.

The Pinnacle (Perl Jam 2)

The vulnerable part is actually in the $cgi-> function calls.

As alluded to in the intro of this blog post, documentation on these older Perl functions was pretty difficult to find. The vulnerability itself is described in a “Perl Jam 2” talk, given by Netanel Rubin at CCC in 2016 among other conferences. The linked video discussed the attack at ~21:45, but I’ll describe it here as well.

Perl Jam 2 is a continuation of a previous talk (“Perl Jam“) in which he describes a number of other Perl language quirks, including the DBI issue from the last Natas level.

How it works

First, let’s look at the vulnerable source code provided in his talk:

You’ll notice that this is almost identical to the source code we’re given, up until the contents of the while() loop.

Line by line:

$cgi->upload('file')

This line should look at the “file” parameter to see if it’s an uploaded file. But apparently we can do some kind of array trick like last time, where we upload a file (that’s our one valid, real file) and then send over a completely unrelated “file” parameter that ends up getting used.

Why is that an option? From documentation:

In a list context, upload() will return an array of filehandles. This makes it possible to process forms that use the same name for multiple upload fields.

So upload() can be an array of values and not just a singular file handles.

Next,

$file = $cgi->param('file');

In the talk, param() is described as returning a list of all parameter values. But only the first value is inserted into the $file variable. So if another value was assigned first, that value (and not the “real” file) will be assigned to $file.

Essentially, $file is a file descriptor that we’ll use later, and we can upload an actual CSV, but add in our own additional value ahead of the real CSV data. The file info for the CSV will be ignored, and the file descriptor for our fake string value will be used instead.

Normally this wouldn’t do us any good, a random string as a file handle isn’t going to get us anywhere.

while (<$file>) {

...unless it’s ARGV.

In that case, “<>” loops through the ARG values… inserting each one to an open() call!

This gives us read access wherever we want.

Recap

To recap, we were supposed to pass in a CSV file with only legitimate CSV contents and file information.

But we decided to include a second fake file object so that the name would be interpreted as the filename handle.

We’ll make that ARGV, <> will loop through, and we can set the ARGV value to whatever filename we want, in order to read whatever file we want.

How to use the Perl Jam 2 Pinnacle Attack

Now that we’ve covered how it works in theory, let’s test it out. I used the free edition of Burp Suite for this. If you have not used Burp before, I have a blog post describing how to get it set up.

With Burp open and your browser traffic proxying through Burp, visit http://natas31.natas.labs.overthewire.org/ and upload a CSV file. The contents don’t matter. I opened up a text editor and saved the following content as test.csv:

1,2,
3,4,

In the Proxy > History tab, right-click the request and Send to Repeater.

In the Repeater tab, your request should look something like this:

It doesn’t matter if your Content-Type boundaries differ in value, as long as they are consistent throughout the request.

Read-Only Solution

There are two modifications you’ll need to make. First, copy/paste one of the form-data blocks above the CSV data, with Content-Disposition: form-data; name="file" followed by ARGV. Make sure the boundary data # matches the other blocks.

This is us putting other data ahead of the real file such that Perl grabs the first file descriptor and its value of ARGV.

Second, we need to provide a value to ARGV (the file we want opened). We do this by appending ?filename at the end of the URL. Since we want to read /etc/natas_webpass/natas32, we’ll use a ? and then that value.

And there’s our flag!

Upgrading to RCE

We already have the flag at this point, but it’s easy to upgrade to RCE. All we need to do is add a | character, and URL-encode our spaces. From the presentation:

For example, if we want to execute the command:

cat /etc/natas_webpass/natas32

We need to:

  • Prepend a ?: /index.pl?cat /etc/natas_webpass/natas32
  • Add a | at the end: /index.pl?cat /etc/natas_webpass/natas32 |
  • Change all of the spaces to %20 for URL encoding: /index.pl?cat%20/etc/natas_webpass/natas32%20|

That looks like this in our request:

The rest of the request stays the same:

Here’s another view of the flag using the Render tab:

Hurt Feelings

Perl Jam and Perl Jam 2 seemed to upset a lot of people in the Perl community:

You can understand why, the code is pretty old and the presenter wasn’t exactly nice to the language:

But as much as people’s feelings are hurt and that new version of Perl fix these issues, there’s still a practicality issue here.

Legacy systems don’t go away, and security experts aren’t (usually) looking to fully appreciate a language (one of the complaints of the Perl rebuttals). Instead, they’re concerned about impacts, and, if we’re being honest, probably some street cred as well.


Takeaway: look for documentation of how a language works. If that’s hard to find, look for past presentations in the infosec community.

Building up a knowledge base from talks, CTFs, and other inputs will help you in unexpected ways.