Overview
The HashCracker module provides fast MD5 password hash cracking using two techniques: known hash lookup and dictionary-based brute-forcing with common passwords. It integrates seamlessly with credential extraction from other modules like SQLMapInjector.
Ethical Use Only: Hash cracking should only be performed on credentials extracted from authorized penetration tests. Unauthorized password cracking is illegal.
Module Location
Class Definition
class HashCracker:
"""Crackeo rápido de hashes MD5 con diccionario"""
HASH_MD5_KNOWN = {
'5f4dcc3b5aa765d61d8327deb882cf99': 'password',
'21232f297a57a5a743894a0e4a801fc3': 'admin',
'0192023a7bbd73250516f069df18b500': 'admin123',
'e10adc3949ba59abbe56e057f20f883e': '123456',
'63a9f0ea7bb98050796b649e85481845': 'root',
'7813d1590d28a7dd372ad54b5d29571b': 'toor',
'0d107d09f5bbe40cade3de5c71e9e9b7': 'letmein',
'40be4e59b9a2a2b5dffb918c0e86b3d7': 'monkey',
'8621ffdbc5698829397d97767ac13db3': 'dragon',
'eb0a191797624dd3a48fa681d3061212': 'qwerty',
'd8578edf8458ce06fbc5bb76a58c5ca4': 'qwerty',
}
The class uses a static dictionary of pre-computed MD5 hashes for instant lookup of common passwords.
Core Methods
crack_md5()
Attempts to crack a single MD5 hash using two-stage approach.
@classmethod
def crack_md5(cls, hash_value):
"""Intentar crackear un hash MD5"""
hash_lower = hash_value.strip().lower()
# 1. Buscar en hashes conocidos
if hash_lower in cls.HASH_MD5_KNOWN:
return cls.HASH_MD5_KNOWN[hash_lower]
# 2. Probar contraseñas comunes
for pwd in Config.COMMON_PASSWORDS:
if hashlib.md5(pwd.encode()).hexdigest() == hash_lower:
return pwd
return None
Stage 1: Known Hash Lookup
O(1) dictionary lookup against pre-computed hashes:
if hash_lower in cls.HASH_MD5_KNOWN:
return cls.HASH_MD5_KNOWN[hash_lower]
Pre-computed Hashes:
| Hash | Password |
|---|
| 5f4dcc3b5aa765d61d8327deb882cf99 | password |
| 21232f297a57a5a743894a0e4a801fc3 | admin |
| e10adc3949ba59abbe56e057f20f883e | 123456 |
| 63a9f0ea7bb98050796b649e85481845 | root |
| 0d107d09f5bbe40cade3de5c71e9e9b7 | letmein |
Stage 2: Dictionary Brute-Force
Computes MD5 hash for each password in Config.COMMON_PASSWORDS:
for pwd in Config.COMMON_PASSWORDS:
if hashlib.md5(pwd.encode()).hexdigest() == hash_lower:
return pwd
Return Value:
- String: Cracked password
- None: Hash not cracked
crack_credentials()
Batch processes a list of credential dictionaries and updates them with cracked passwords.
@classmethod
def crack_credentials(cls, credentials):
"""Crackear todos los hashes en una lista de credenciales"""
cracked = 0
for cred in credentials:
hash_val = cred.get('hash', '')
# Si parece un hash MD5 (32 hex chars)
if hash_val and len(hash_val) == 32 and all(c in '0123456789abcdefABCDEF' for c in hash_val):
result = cls.crack_md5(hash_val)
if result:
cred['password'] = f"{result}"
cred['cracked'] = True
cracked += 1
rprint(f" [bold green]✅ CRACKED: {cred['user']} → {hash_val[:16]}... = '{result}'[/bold green]")
else:
cred['cracked'] = False
rprint(f" [yellow]❌ No crackeado: {cred['user']} → {hash_val[:16]}...[/yellow]")
return cracked
MD5 Hash Validation:
if hash_val and len(hash_val) == 32 and all(c in '0123456789abcdefABCDEF' for c in hash_val):
Verifies:
- Hash exists and is not empty
- Exactly 32 characters (MD5 length)
- All characters are hexadecimal (0-9, a-f, A-F)
Credential Update:
if result:
cred['password'] = f"{result}"
cred['cracked'] = True
cracked += 1
else:
cred['cracked'] = False
Modifies credentials in-place, adding cracked boolean flag.
Output Examples:
Success:
✅ CRACKED: admin → 5f4dcc3b5aa765d6... = 'password'
✅ CRACKED: user1 → e10adc3949ba59ab... = '123456'
Failure:
❌ No crackeado: user2 → 8f3a9bc4e2d1f5c7...
Return Value: Integer count of successfully cracked hashes.
Configuration Requirements
Config.COMMON_PASSWORDS = [
'password', 'admin', 'root', '123456', 'qwerty',
'letmein', 'welcome', 'monkey', 'dragon', '1234',
'password1', 'admin123', 'root123', 'test', 'guest'
]
Expand this list for higher crack rates. Common sources:
- Top 100 passwords from breach databases
- Rockyou.txt top 1000
- Context-specific passwords (company name, year, etc.)
Integration with SQLMapInjector
Typical workflow from SQL injection to hash cracking:
from services.sqlmap_inject import SQLMapInjector
from services.hash_cracker import HashCracker
# 1. Extract credentials via SQLMap
injector = SQLMapInjector(host)
vulns = injector.attack()
# host.credentials now contains:
# [
# {'user': 'admin', 'hash': '5f4dcc3b5aa765d61d8327deb882cf99', 'password': '5f4dcc...', 'cracked': False},
# {'user': 'user1', 'hash': 'e10adc3949ba59abbe56e057f20f883e', 'password': 'e10adc...', 'cracked': False}
# ]
# 2. Crack extracted hashes
cracked_count = HashCracker.crack_credentials(host.credentials)
# host.credentials now contains:
# [
# {'user': 'admin', 'hash': '5f4dcc...', 'password': 'password', 'cracked': True},
# {'user': 'user1', 'hash': 'e10adc...', 'password': '123456', 'cracked': True}
# ]
print(f"Cracked {cracked_count} passwords")
Input dictionary structure:
{
'source': 'SQLMap (DVWA)', # Where hash was found
'user': 'admin', # Username
'password': '5f4dcc3b5aa765...', # Initially contains hash
'hash': '5f4dcc3b5aa765...', # MD5 hash value
'cracked': False # Status flag
}
After cracking:
{
'source': 'SQLMap (DVWA)',
'user': 'admin',
'password': 'password', # Replaced with plaintext
'hash': '5f4dcc3b5aa765...',
'cracked': True # Updated to True
}
Known Hash Lookup:
- Time Complexity: O(1)
- Space Complexity: O(n) where n = size of HASH_MD5_KNOWN
- Speed: Instant (dictionary lookup)
Dictionary Brute-Force:
- Time Complexity: O(m) where m = len(Config.COMMON_PASSWORDS)
- Space Complexity: O(1)
- Speed: ~1000 hashes/second on modern CPU
Total per hash: Typically < 10ms
Limitations
- MD5 Only: Does not support bcrypt, SHA-256, SHA-512, or other algorithms
- Small Dictionary:
Config.COMMON_PASSWORDS is limited; won’t crack strong passwords
- No GPU Acceleration: Uses CPU-based hashlib, not optimized for cracking
- No Rainbow Tables: Doesn’t use precomputed tables beyond
HASH_MD5_KNOWN
- No Salts: Assumes unsalted MD5 hashes (common in old applications)
Extending the Module
Adding More Known Hashes
HASH_MD5_KNOWN = {
# Existing hashes...
'a94a8fe5ccb19ba61c4c0873d391e987': '12345',
'81dc9bdb52d04dc20036dbd8313ed055': '1234',
'c4ca4238a0b923820dcc509a6f75849b': '1',
}
Using External Wordlists
@classmethod
def crack_md5_with_wordlist(cls, hash_value, wordlist_path):
hash_lower = hash_value.strip().lower()
with open(wordlist_path, 'r', encoding='latin-1') as f:
for line in f:
pwd = line.strip()
if hashlib.md5(pwd.encode()).hexdigest() == hash_lower:
return pwd
return None
Usage:
result = HashCracker.crack_md5_with_wordlist(
'5f4dcc3b5aa765d61d8327deb882cf99',
'/usr/share/wordlists/rockyou.txt'
)
For production hash cracking, consider:
Hashcat (GPU-accelerated):
hashcat -m 0 -a 0 hashes.txt rockyou.txt
-m 0: MD5 mode
-a 0: Dictionary attack
- Speeds: 10-50 billion hashes/second on modern GPU
John the Ripper:
john --format=raw-md5 --wordlist=rockyou.txt hashes.txt
Dependencies
- hashlib: Python standard library (MD5 hashing)
- rich: Terminal formatting for output
Security Considerations
- Weak Hashes: MD5 is cryptographically broken; modern applications use bcrypt/Argon2
- Storage: Never store cracked passwords in plaintext logs
- Reporting: Redact passwords in reports; demonstrate vulnerability without exposing credentials
- Legal: Possession of cracked credentials may have legal implications in some jurisdictions
- SQLMapInjector: Extracts MD5 hashes from databases
- WPForceBrute: Obtains plaintext passwords (no cracking needed)
- NmapScanner: No direct integration (doesn’t extract credentials)