Creating Custom Password Spray Scripts Using Python3

Overview

Love it or hate it, password spraying is a reliable weapon in an adversaries arsenal to recover credentials. Rather than traditionally brute force methods that try a large amount of passwords against a specific set of users, password spraying uses a very small number of common or “educated guess” passwords against a larger swath of user accounts.

There are a number of great scripts that exist [ See SpiderLabs Spray | Metasploit OWA Brute Force ] or you could use BurpSuite’s Intruder utility but knowing how to write your own scripts is an important skill to learn and ensures you can adapt to any scenario. This blog post will emulate an external attacker who wants to spray against a web property, and who has not yet breached the network perimeter.

This post will not discuss how to collect usernames (or suspected usernames) as that’s a different blog post all together. In this post I’ll develop a custom script for spraying passwords against a WordPress instance. In many cases WordPress may be viewed as a low-value target, however the idea here is to present the concepts that can be adapted for any single-factor authentication application you should encounter.

It should go without saying, but I’ll say it anyways, that this technique should not be used on any computer system that you do not have written permission to attempt to compromise. 

Prerequisites

  • Python3 environment
  • Python Requests library [ documentation ]
  • WireShark or BurpSuite
  • A user list / set of passwords you wish to try

A Note On Locking Accounts Out

Whenever you attempt to log into a service that’s facing the internet you should expect to run into some sort of brute force protection mechanism. Some of these mechanisms are nice enough to tell you how many more incorrect password attempts you have prior to locking the account / IP address out. Unless you enjoy angering your clients, it’s in your best interest not to create a DoS condition. If you don’t know how many incorrect attempts will lock out a user a user account be conservative with your guessing, and go low and slow. If you’re in the dark as to how many incorrect attempts will lock an account out, I’d advise guessing 2 passwords per user at a maximum.

Getting Started

The functionality of the script will be as follows :

  • Import a set of usernames and passwords from pre-defined text files
  • Send a POST request for each combination of username and password at random intervals
  • Log the output to a CSV file for later

With that said, the first thing we need to do, assuming your development environment is set up, is  capture a POST request to the targeted application. Fire up BurpSuite or WireShark and attempt to log in to the application to capture the POST request.

A sample POST request during an WordPress Login Attempt

After attempting to log into our target we have all the information we need to start writing our script.

  • Our headers (we’ll ignore a few of these but keep the important ones)
  • Cookies (we’ll ignore the Google Analytics cookie)
  • 5 POST parameters

Now that we know what a request looks like, we need to replicate it in our script.

We’ll start by importing the necessary libraries of requests, sys, time, random and Path. I’ll explain the need for each of these as we go along. We’ll also replicate the headers from a request.

The next couple of lines of code are to create two variables – one which will capture the “Start” time of the script, and the second will be a list of passwords to try for each user. The startTime variable is one of the reasons we needed to import the time library.

We’ll need a way to specify usernames to the application. You could do this in a number of ways, however my preferred method is to simply specify a .txt file with usernames on each line. In my opinion the easiest way to program this is to simply create a function which looks for a text file of a specific name. The following function will search for a text file named unames.txt – if it exists the script will carry on, and if it doesn’t exist it will exit. To check the file system to see whether the unames.txt file exists, we’ll utilize the Path library.

The next order of business is creating our function for the password spraying itself. The functionality of this function is as follows

  • Open the unames.txt file and import each line as a list
  • Create a csv file and export the results of each attempted password
  • Attempt a username and password combo
  • Wait a random interval before trying another password

Now all that’s left to do is define the main function and put it all together.

Final Script

Execution and Interpreting Results

With a functioning script we’ll need to determine how to identify successful logins versus unsuccessful logins. Each application will vary in it’s behaviour, however there are two main things to look for.

  • The Response code (Status) might change from a 200 to a 301 / 302 redirect.
  • The Length of the response will be different from the other requests.

Below is an example of the script running against a WordPress instance I created for the purpose of this blog entry. You’ll notice that a successful login still returns a response code of 200, however the length of the response is much larger than other failed attempts.

When all is said and done you should have a .csv file with all the requests and responses received after your attack that you can reference for your purposes.

In Conclusion

For any application that only enforces single factor authentication, password spraying will continue to be a very powerful tool for a red teamer or adversary. The script above can certainly be improved upon in many ways such as :

  • String formatting [ not used for sake of simplicity of the demonstration ]
  • Colour formatting on the terminal
  • Passing passwords as arguments rather than coding them into the script