Lab Guide — Results Analysis & Post-Processing
Four exercises covering output formats, session management, data cleaning, and password analysis with Python.
pandas and matplotlib packages. Install them with: pip install pandas matplotlib
Setup — Create Test Hashes
Before starting, generate a set of test hashes to work with throughout the lab.
Prepare test data
Create a file called lab_hashes.txt with MD5 hashes. You can generate these from known passwords:
# Generate MD5 hashes from a list of known passwords
echo -n "password" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "123456" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "qwerty" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "letmein" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "admin" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "welcome1" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "monkey" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "dragon" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "master" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
echo -n "P@ssw0rd!" | md5sum | cut -d' ' -f1 >> lab_hashes.txt
# Also create a version with usernames
echo "alice:$(echo -n 'password' | md5sum | cut -d' ' -f1)" >> lab_users.txt
echo "bob:$(echo -n '123456' | md5sum | cut -d' ' -f1)" >> lab_users.txt
echo "charlie:$(echo -n 'qwerty' | md5sum | cut -d' ' -f1)" >> lab_users.txt
echo "diana:$(echo -n 'letmein' | md5sum | cut -d' ' -f1)" >> lab_users.txt
echo "admin:$(echo -n 'admin' | md5sum | cut -d' ' -f1)" >> lab_users.txt
Exercise 1 — Output Formats
Explore --outfile-format options
Run the same attack with different output formats and compare the results.
- Run a dictionary attack saving results in format 3 (default):
hashcat -m 0 lab_hashes.txt /usr/share/wordlists/rockyou.txt \ -o results_fmt3.txt --outfile-format 3 - Run again with format 2 (plain passwords only):
hashcat -m 0 lab_hashes.txt /usr/share/wordlists/rockyou.txt \ -o results_fmt2.txt --outfile-format 2 --potfile-disable - Run with format 5 using the username file:
hashcat -m 0 lab_users.txt /usr/share/wordlists/rockyou.txt \ -o results_fmt5.txt --outfile-format 5 --username - Compare the three output files:
echo "=== Format 3 (hash:password) ===" head -5 results_fmt3.txt echo "=== Format 2 (plain only) ===" head -5 results_fmt2.txt echo "=== Format 5 (login:password:hash) ===" head -5 results_fmt5.txt
Checkpoint: You have three output files, each showing the same cracked passwords in different formats.
Exercise 2 — Session Management
Practice --session and --restore
- Start a named session with a large wordlist:
hashcat -m 0 lab_hashes.txt /usr/share/wordlists/rockyou.txt \ --session lab09_session -o session_results.txt - While running, press
sto view status, then presspto pause - Press
qorCtrl+Cto quit gracefully (saves checkpoint) - Resume the session:
hashcat --session lab09_session --restore - After completion, inspect what was cracked and what remains:
# Show cracked hashes hashcat -m 0 --show lab_hashes.txt # Show uncracked hashes hashcat -m 0 --left lab_hashes.txt # Save uncracked to a separate file hashcat -m 0 --left lab_hashes.txt > remaining.txt echo "Remaining hashes:" wc -l remaining.txt
Checkpoint: You can pause, resume, and inspect results from a named session.
Exercise 3 — Python Data Cleaning (clean_results.py)
Build a script to clean and normalize hashcat output
Create a file called clean_results.py that processes raw hashcat output.
#!/usr/bin/env python3
"""clean_results.py - Clean and normalize hashcat output."""
import pandas as pd
import sys
def clean_results(input_file, output_file='clean_results.csv'):
# Read hash:password format
data = []
with open(input_file, 'r') as f:
for line in f:
line = line.strip()
if ':' in line:
# Split on first colon only (passwords may contain colons)
parts = line.split(':', 1)
if len(parts) == 2:
data.append({'hash': parts[0], 'password': parts[1]})
df = pd.DataFrame(data)
print(f"Total entries read: {len(df)}")
# Remove duplicate hashes (same hash cracked multiple times)
df.drop_duplicates(subset='hash', inplace=True)
print(f"After dedup by hash: {len(df)}")
# Remove empty passwords
df = df[df['password'].str.len() > 0]
print(f"After removing empty: {len(df)}")
# Add metadata columns
df['length'] = df['password'].str.len()
df['has_upper'] = df['password'].str.contains(r'[A-Z]', regex=True)
df['has_digit'] = df['password'].str.contains(r'[0-9]', regex=True)
df['has_special'] = df['password'].str.contains(r'[^A-Za-z0-9]', regex=True)
# Save cleaned data
df.to_csv(output_file, index=False)
print(f"\nCleaned data saved to: {output_file}")
return df
if __name__ == '__main__':
input_file = sys.argv[1] if len(sys.argv) > 1 else 'results_fmt3.txt'
df = clean_results(input_file)
print(f"\nSample output:")
print(df.head().to_string())
Run the script:
python3 clean_results.py results_fmt3.txt
Checkpoint: The script reads raw hashcat output, removes duplicates, and produces a clean CSV with metadata columns.
Exercise 4 — Password Analysis (analyze_passwords.py)
Build an analysis script with frequency charts and complexity breakdown
Create analyze_passwords.py that reads the cleaned CSV and produces visualizations.
#!/usr/bin/env python3
"""analyze_passwords.py - Password frequency analysis with charts."""
import pandas as pd
import matplotlib.pyplot as plt
import re
import sys
def classify_complexity(pw):
"""Classify password into Weak/Fair/Good/Strong."""
checks = [
bool(re.search(r'[a-z]', str(pw))),
bool(re.search(r'[A-Z]', str(pw))),
bool(re.search(r'[0-9]', str(pw))),
bool(re.search(r'[^A-Za-z0-9]', str(pw))),
]
score = sum(checks)
if score <= 1: return 'Weak'
if score == 2: return 'Fair'
if score == 3: return 'Good'
return 'Strong'
def analyze(csv_file):
df = pd.read_csv(csv_file)
print(f"Analyzing {len(df)} passwords...\n")
# --- Top 10 passwords ---
print("=" * 40)
print("TOP 10 MOST COMMON PASSWORDS")
print("=" * 40)
top10 = df['password'].value_counts().head(10)
for i, (pw, count) in enumerate(top10.items(), 1):
print(f" {i:2d}. {pw:20s} ({count})")
# --- Length distribution ---
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
df['length'] = df['password'].str.len()
df['length'].hist(bins=range(1, int(df['length'].max()) + 2),
color='#1f6feb', edgecolor='#0d1117', ax=axes[0])
axes[0].set_xlabel('Password Length')
axes[0].set_ylabel('Count')
axes[0].set_title('Password Length Distribution')
axes[0].axvline(x=8, color='#f85149', linestyle='--', label='8-char minimum')
axes[0].legend()
# --- Complexity distribution ---
df['complexity'] = df['password'].apply(classify_complexity)
complexity_order = ['Weak', 'Fair', 'Good', 'Strong']
colors = {'Weak': '#f85149', 'Fair': '#d29922', 'Good': '#1f6feb', 'Strong': '#2ea043'}
counts = df['complexity'].value_counts().reindex(complexity_order).fillna(0)
counts.plot(kind='bar', color=[colors[x] for x in counts.index], ax=axes[1])
axes[1].set_ylabel('Count')
axes[1].set_title('Password Complexity Distribution')
axes[1].tick_params(axis='x', rotation=0)
plt.tight_layout()
plt.savefig('password_analysis.png', dpi=150)
plt.show()
print(f"\nChart saved to: password_analysis.png")
# --- Summary statistics ---
print(f"\n{'=' * 40}")
print("SUMMARY STATISTICS")
print(f"{'=' * 40}")
print(f" Total passwords: {len(df)}")
print(f" Average length: {df['length'].mean():.1f}")
print(f" Under 8 characters: {(df['length'] < 8).sum()} "
f"({(df['length'] < 8).mean()*100:.1f}%)")
print(f" Weak complexity: {(df['complexity'] == 'Weak').sum()} "
f"({(df['complexity'] == 'Weak').mean()*100:.1f}%)")
print(f" Strong complexity: {(df['complexity'] == 'Strong').sum()} "
f"({(df['complexity'] == 'Strong').mean()*100:.1f}%)")
if __name__ == '__main__':
csv_file = sys.argv[1] if len(sys.argv) > 1 else 'clean_results.csv'
analyze(csv_file)
Run the analysis:
python3 analyze_passwords.py clean_results.csv
Checkpoint: You have a bar chart of the top passwords, a length distribution histogram, a complexity pie/bar chart, and printed summary statistics.
plt.switch_backend('Agg') at the top of the script so matplotlib saves files without trying to open a window.