Windows Credential Dumping Tool


Description

Detects execution of tools commonly used for credential dumping on Windows systems. These tools can extract OAuth refresh tokens (GAIA), passwords, and authentication secrets from Windows memory (LSASS) and registry.

Query · python

import re

CREDENTIAL_DUMPING_TOOLS = {
    "mimikatz.exe",
    "secretsdump.py",
    "pwdump.exe",
    "fgdump.exe",
    "gsecdump.exe",
    "samdump2.exe",
    "quarks-pwdump.exe",
    "cachedump.exe",
    "lsadump.exe",
    "procdump.exe",
    "procdump64.exe",
    "mimipenguin.sh",
    "mimidogz.ps1",
    "logonpasswords.exe",
    "pypykatz.exe",
    "dsusers.py",
    "ntdsgrab.py",
    "lazagne.exe",
    "creddump7.exe",
    "keethief.ps1",
    "inveigh.exe",
    "sharpkatz.exe",
    "dumpert.exe",
    "hivedump.exe",
    "kerbrute.exe",
    "sessiongopher.ps1",
    "GoTokenTheft.exe",
}


def normalize_username(username):
    """
    Normalize username for correlation matching by removing special characters
    and converting to lowercase.
    Examples: Jane.Doe -> janedoe, john_smith -> johnsmith
    """
    if not username:
        return None
    # Remove all non-alphanumeric characters and convert to lowercase
    return re.sub(r"[^a-z0-9]", "", username.lower())


def rule(event):
    # Event ID 4688: Windows Security Audit - new process created
    # Event ID 1: Sysmon - process creation
    event_id = event.get("EventID", "")

    if event_id not in ["4688", "1"]:
        return False

    extra_data = event.get("ExtraEventData", {})

    # Event 4688 uses NewProcessName, Sysmon uses Image
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")

    if not process_name:
        return False

    # Extract just the filename from the full path
    # Handle both backslash and forward slash separators, and UNC paths
    process_filename = process_name.lower().replace("/", "\\").split("\\")[-1]

    return process_filename in CREDENTIAL_DUMPING_TOOLS


def title(event):
    extra_data = event.get("ExtraEventData", {})
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")
    if process_name:
        process_filename = process_name.lower().replace("/", "\\").split("\\")[-1]
    else:
        process_filename = "<UNKNOWN>"

    computer = event.get("Computer", "<UNKNOWN_HOST>")

    # Try to extract username from process path (e.g., C:\Users\jdoe\...)
    username = "<UNKNOWN_USER>"
    if process_name:
        # Normalize path separators for consistent parsing
        normalized_path = process_name.replace("/", "\\")
        parts = normalized_path.split("\\")
        parts_lower = [p.lower() for p in parts]

        # Check for standard Windows user profile path
        if "users" in parts_lower:
            try:
                users_index = parts_lower.index("users")
                if users_index + 1 < len(parts) and parts[users_index + 1]:
                    username = parts[users_index + 1]
            except (ValueError, IndexError):
                pass

        # Fall back to SID if username not extracted from path
        if username == "<UNKNOWN_USER>":
            username = event.get("UserID", "<UNKNOWN_USER>")

    return (
        f"Windows: Credential dumping tool [{process_filename}] "
        f"executed on [{computer}] by [{username}]"
    )


def alert_context(event):
    extra_data = event.get("ExtraEventData", {})
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")

    # Extract username from process path or fall back to SID
    username = None
    if process_name:
        # Normalize path separators for consistent parsing
        normalized_path = process_name.replace("/", "\\")
        parts = normalized_path.split("\\")
        parts_lower = [p.lower() for p in parts]

        # Check for standard Windows user profile path
        if "users" in parts_lower:
            try:
                users_index = parts_lower.index("users")
                if users_index + 1 < len(parts) and parts[users_index + 1]:
                    username = parts[users_index + 1]
            except (ValueError, IndexError):
                pass

    return {
        "computer": event.get("Computer"),
        "user": username,
        "username_normalized": normalize_username(username),
        "user_sid": event.get("UserID"),
        "process_name": process_name,
        "command_line": (extra_data.get("CommandLine") or extra_data.get("ProcessCommandLine")),
        "parent_process": (extra_data.get("ParentProcessName") or extra_data.get("ParentImage")),
        "process_id": (extra_data.get("NewProcessId") or extra_data.get("ProcessId")),
        "event_id": event.get("EventID"),
        "description": (
            "Detected execution of credential dumping tool commonly used to "
            "extract OAuth tokens, passwords, and authentication secrets from "
            "Windows memory and registry"
        ),
    }

Analyst notes

  1. Query Windows.EventLogs for all process creation events (EventID 4688 or Sysmon EventID 1) on the Computer hostname in the 1 hour before and after the alert to identify the full scope of malicious activity and parent processes
  2. Check if the process was executed by a privileged account by reviewing the ExtraEventData SubjectUserName field, and search for other suspicious processes spawned by the same ParentProcessName to identify potential lateral movement
Raw source Windows Credential Dumping Tool · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
DisplayName: "Windows Credential Dumping Tool"
RuleID: "Windows.Credential.Dumping.Tool"
Description: >
  Detects execution of tools commonly used for credential dumping on Windows systems.
  These tools can extract OAuth refresh tokens (GAIA), passwords, and authentication secrets
  from Windows memory (LSASS) and registry.
Enabled: true
CreateAlert: false
Filename: windows_credential_dumping_tool.py
Reference: https://businessinsights.bitdefender.com/the-chain-reaction-new-methods-for-extending-local-breaches-in-google-workspace
Runbook: |
  1. Query Windows.EventLogs for all process creation events (EventID 4688 or Sysmon EventID 1) on the Computer hostname in the 1 hour before and after the alert to identify the full scope of malicious activity and parent processes
  2. Check if the process was executed by a privileged account by reviewing the ExtraEventData SubjectUserName field, and search for other suspicious processes spawned by the same ParentProcessName to identify potential lateral movement
Severity: High
LogTypes:
  - Windows.EventLogs
DedupPeriodMinutes: 60
Tags:
  - Windows
  - Credential Access
  - GAIA
  - Mimikatz
  - OAuth
  - T1003
Reports:
  MITRE ATT&CK:
    - TA0006:T1003
    - TA0006:T1003.001
SummaryAttributes:
  - computer
  - p_any_usernames
Tests:
  - Name: Mimikatz Execution Event ID 4688
    ExpectedResult: true
    Log:
      EventID: "4688"
      ProviderName: "Microsoft-Windows-Security-Auditing"
      TimeCreated: "2024-01-15 10:30:45 +0000"
      Computer: "WIN-WORKSTATION-01"
      Channel: "Security"
      EventRecordID: "12345678"
      Level: "0"
      UserID: "S-1-5-21-123456789-123456789-123456789-1001"
      Message: "A new process has been created..."
      MessageTitle: "A new process has been created"
      ExtraEventData:
        SubjectUserSid: "S-1-5-21-123456789-123456789-123456789-1001"
        SubjectUserName: "jdoe"
        SubjectDomainName: "CORP"
        SubjectLogonId: "0x3e7"
        NewProcessId: "0x1234"
        NewProcessName: "C:\\Users\\jdoe\\Downloads\\mimikatz.exe"
        TokenElevationType: "%%1936"
        ProcessId: "0x5678"
        CommandLine: "mimikatz.exe privilege::debug sekurlsa::logonpasswords"
        ParentProcessName: "C:\\Windows\\System32\\cmd.exe"
      p_log_type: "Windows.EventLogs"
      p_event_time: "2024-01-15 10:30:45.000000000"
  - Name: Benign Process Execution
    ExpectedResult: false
    Log:
      EventID: "4688"
      ProviderName: "Microsoft-Windows-Security-Auditing"
      TimeCreated: "2024-01-15 09:00:00 +0000"
      Computer: "WIN-WORKSTATION-01"
      Channel: "Security"
      EventRecordID: "11111111"
      Level: "0"
      UserID: "S-1-5-21-123456789-123456789-123456789-1001"
      Message: "A new process has been created..."
      MessageTitle: "A new process has been created"
      ExtraEventData:
        SubjectUserSid: "S-1-5-21-123456789-123456789-123456789-1001"
        SubjectUserName: "jdoe"
        SubjectDomainName: "CORP"
        SubjectLogonId: "0x3e7"
        NewProcessId: "0x9999"
        NewProcessName: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
        TokenElevationType: "%%1936"
        ProcessId: "0x8888"
        CommandLine: '"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"'
        ParentProcessName: "C:\\Windows\\explorer.exe"
      p_log_type: "Windows.EventLogs"
      p_event_time: "2024-01-15 09:00:00.000000000"

# ------ paired body: windows_credential_dumping_tool.py ------

import re

CREDENTIAL_DUMPING_TOOLS = {
    "mimikatz.exe",
    "secretsdump.py",
    "pwdump.exe",
    "fgdump.exe",
    "gsecdump.exe",
    "samdump2.exe",
    "quarks-pwdump.exe",
    "cachedump.exe",
    "lsadump.exe",
    "procdump.exe",
    "procdump64.exe",
    "mimipenguin.sh",
    "mimidogz.ps1",
    "logonpasswords.exe",
    "pypykatz.exe",
    "dsusers.py",
    "ntdsgrab.py",
    "lazagne.exe",
    "creddump7.exe",
    "keethief.ps1",
    "inveigh.exe",
    "sharpkatz.exe",
    "dumpert.exe",
    "hivedump.exe",
    "kerbrute.exe",
    "sessiongopher.ps1",
    "GoTokenTheft.exe",
}


def normalize_username(username):
    """
    Normalize username for correlation matching by removing special characters
    and converting to lowercase.
    Examples: Jane.Doe -> janedoe, john_smith -> johnsmith
    """
    if not username:
        return None
    # Remove all non-alphanumeric characters and convert to lowercase
    return re.sub(r"[^a-z0-9]", "", username.lower())


def rule(event):
    # Event ID 4688: Windows Security Audit - new process created
    # Event ID 1: Sysmon - process creation
    event_id = event.get("EventID", "")

    if event_id not in ["4688", "1"]:
        return False

    extra_data = event.get("ExtraEventData", {})

    # Event 4688 uses NewProcessName, Sysmon uses Image
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")

    if not process_name:
        return False

    # Extract just the filename from the full path
    # Handle both backslash and forward slash separators, and UNC paths
    process_filename = process_name.lower().replace("/", "\\").split("\\")[-1]

    return process_filename in CREDENTIAL_DUMPING_TOOLS


def title(event):
    extra_data = event.get("ExtraEventData", {})
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")
    if process_name:
        process_filename = process_name.lower().replace("/", "\\").split("\\")[-1]
    else:
        process_filename = "<UNKNOWN>"

    computer = event.get("Computer", "<UNKNOWN_HOST>")

    # Try to extract username from process path (e.g., C:\Users\jdoe\...)
    username = "<UNKNOWN_USER>"
    if process_name:
        # Normalize path separators for consistent parsing
        normalized_path = process_name.replace("/", "\\")
        parts = normalized_path.split("\\")
        parts_lower = [p.lower() for p in parts]

        # Check for standard Windows user profile path
        if "users" in parts_lower:
            try:
                users_index = parts_lower.index("users")
                if users_index + 1 < len(parts) and parts[users_index + 1]:
                    username = parts[users_index + 1]
            except (ValueError, IndexError):
                pass

        # Fall back to SID if username not extracted from path
        if username == "<UNKNOWN_USER>":
            username = event.get("UserID", "<UNKNOWN_USER>")

    return (
        f"Windows: Credential dumping tool [{process_filename}] "
        f"executed on [{computer}] by [{username}]"
    )


def alert_context(event):
    extra_data = event.get("ExtraEventData", {})
    process_name = extra_data.get("NewProcessName", "") or extra_data.get("Image", "")

    # Extract username from process path or fall back to SID
    username = None
    if process_name:
        # Normalize path separators for consistent parsing
        normalized_path = process_name.replace("/", "\\")
        parts = normalized_path.split("\\")
        parts_lower = [p.lower() for p in parts]

        # Check for standard Windows user profile path
        if "users" in parts_lower:
            try:
                users_index = parts_lower.index("users")
                if users_index + 1 < len(parts) and parts[users_index + 1]:
                    username = parts[users_index + 1]
            except (ValueError, IndexError):
                pass

    return {
        "computer": event.get("Computer"),
        "user": username,
        "username_normalized": normalize_username(username),
        "user_sid": event.get("UserID"),
        "process_name": process_name,
        "command_line": (extra_data.get("CommandLine") or extra_data.get("ProcessCommandLine")),
        "parent_process": (extra_data.get("ParentProcessName") or extra_data.get("ParentImage")),
        "process_id": (extra_data.get("NewProcessId") or extra_data.get("ProcessId")),
        "event_id": event.get("EventID"),
        "description": (
            "Detected execution of credential dumping tool commonly used to "
            "extract OAuth tokens, passwords, and authentication secrets from "
            "Windows memory and registry"
        ),
    }

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.