This is the second part of the DVWA write-up. I will more than likely get around to the other parts very soon, but have been sick, so have not been writing or researching anywhere near as I wanted to recently.
This write-up will start on the Brute Force section of the DVWA which simulates one of the problems with web applications with poor security controls. After perhaps 10 failed login attempts, the application should really enforce some way of stopping the user continuing to guessing, either through rate limiting incoming requests from potentially malicious IP’s or by locking out accounts who have guessed incorrectly a certain amount of times. This section of the DVWA looks just like this.
And this is what it looks like the second you fail at guessing the credentials.
I tried to manually guess the password and username a dozen or so times, and there was no limiting or locking out that I could discern. I imagine stuff like that would occur the higher the security level, but we are starting out at low.
So obviously for this we are going to need to replay sending the application a username and a password. Hydra would normally be an excellent choice of tool for network login attempts, but as this is a web application, it would not be so useful here.
What we need is some sort of program that will repeatedly send the same HTTP request to this application, but change the username and password fields each iteration. Burpsuite is excellent for this, and we can start off using the intercepting proxy to find out which bits to simulate.
Once we have captured one request, we can right click it and “Send to Intruder”. We set the target to the IP address the webapp is using, and the port number it uses (generally this is going to be 80). Now it’s time to tell Burp which parts of the request we want it to change with each request.
Burp allows you to highlight these areas using the ‘§’ character. As you can see above, I have added in one field for the username and one for the password. Burp will iterate through these fields with lists that we supply it with, as you can see here.
The payload set defines which of the fields it is that we associate our list with, so first up is just a test set of 5 usernames that seem like likely candidates for the administrator access. The passwords I used were a list of the 10 thousand most common ones that I have found to be pretty accurate. I would like to get the list down a little to around 300 at some point, and perhaps that will form the basis of another blog post. It is far better to have a list of a few hundred highly likely passwords than a list of every single possible character combination.
The rate limiting is not too bad if you have only a few hundred possibilities, as after a few minutes, it had done a couple of hundred requests.
Right away we can see that there is one request, right at the top that has a different length than the others, indicating this is likely to be the right one. The pretty standard default credentials of admin/password was ironically not one of the ones I tried at the beginning of this session with the DVWA when attempting to determine if there was any rate limiting in place.
Sure enough, logging in with admin/password gives us a welcome screen! Going through this brute forcing was quite fun, and a good way for me to get more accustomed to Burp Suite after not using it for some time. It is really, really good at what it does, and if you are testing webapps of any kind, it is an absolute must.
What frustrated me was the relatively slow speed of the brute forcer. In fact it is quite quick, the first 10 requests occur in only a second or two. The next ten to twenty take about twice as long, and after a while it gets progressively slower and slower. Surely there must be a way we can make our own one?
So I did, and as with most of my hacky scripts, I used Python due to the very rapid prototyping that you can do with it. Before I show you the code, let’s just walk through exactly what we want it to do.
First we know we are going to be making a GET request to a url, which makes importing the Requests library pretty much a given. We also know we are going to be iterating over a couple of lists (usernames and passwords), so we are going to want to open a couple of files, and probably take them as command line arguments, so the sys library is also required.
Also we are going to need a success condition, and a failure condition to evaluate which of our requests are working and which are not. Rifling through the failed requests in our initial Burp attack, we can see that every failed login attempt has the phrase “Username and/or password incorrect” somewhere in it. Obviously the successful attempt does not have this, and other than that the requests are pretty similar, so this can be our differentiator.
Our pseudo code is going to be something like :
- Take a username and password combo from this list of the two.
- Make a GET request to this url with those credentials embedded.
- Check for the presence of the word “incorrect” in the reply to indicate failure.
And that is pretty accurate. In fact, the code is only around 15 lines.
exception = "incorrect"
users = open(sys.argv, 'r')
passwords = open(sys.argv, 'r')
user_list = users.readlines()
password_list = passwords.readlines()
for password in password_list:
for user in user_list:
user = user.rstrip('\n')
password = password.rstrip('\n')
cookies = dict(PHPSESSID='toika93tdo9pes4svmu5mp07u4',security='low')
url = "http://192.168.1.105/vulnerabilities/brute/?username=" + user + "&password=" + password + "&Login=Login#"
r = requests.get(url, cookies=cookies)
if exception not in r.text:
print "Success with username: " + user + " and password: " + password
The more I look at the code too, the more I can see that I could probably remove, trim and refactor. But that is not what a hacky script should have done to it. It should be used and thrown away. So we will just ignore it 😉
One thing I will say was completely necessary through was the stripping of the line endings (user = user.rstrip(‘\n’)). I was tearing my hair out for about an hour trying to figure out why the script would not work, and even tried replicating the script in its most basic form, performing only one request with hard coded credentials as below.
cookies = dict(PHPSESSID='toika93tdo9pes4svmu5mp07u4',security='low')
r = requests.get("http://192.168.1.105/vulnerabilities/brute/?username=admin&password=password&Login=Login#", cookies=cookies)
if "incorrect" not in r.text:
This version even includes 2 extra lines of debugging code, just to try to figure out where I was going wrong. Obviously when applying the age old debugging method of “print out everything” to my original script, and seeing the url spread over three lines did I realise exactly what my mistake was.
With a test file of 5 passwords and another of 5 usernames, the script worked! Finally!
Next up was to try it out with my list of 10,000 passwords and the same 5 usernames.
So there are a few things to take away here.
- The script actually works. Huge success!
- The time taken with 50,000 requests (5 users * 10,000 passwords) is really quite low, at around 5 minutes. Remember that Burp only had completed around 200 in 3 minutes. Secondary Huge Success!
- The script didn’t work with the larger list. Huge Failure!
I don’t exactly know why it didn’t work the second time. Perhaps the DVWA had invalidated the PHPSESSID that I was using, or associated it with an authenticated account? Perhaps the successful request got lost in the noise? We are making around 175 requests a second, after-all. Also it is still a bit slow for my liking.
The next thing for me to try will be to try to introduce the Threading library into this script in order to make it a bit faster. Obviously I will have to repeat the experiment without first succeeding with the test version, and thereby associating my PHPSESSID token with an authenticated user, just to eliminate that part of the equation.
Overall, this really re-enforced to me exactly how much I love turning these challenges that are meant to be solved with tools into programming challenges. I completely lose track of time, and can find myself looking up to a room sheathed in darkness, where once was daylight. I can only imagine this is what other programmers feel like, and I am not a little bit jealous.
That’s all for now, I will write more about the DVWA when I get another chance to take a look. Hopefully this weekend. Have yourselves a great week!