Lucene search
+L

Command History Analyzer

🗓️ 28 Apr 2026 08:32:20Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 3 Views

The Command History Analyzer analyzes shell history for suspicious command patterns.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kittysploit import *
import re


class Module(Post):
    __info__ = {
        "name": "Command History Analyzer",
        "description": "Analyze shell history for suspicious command patterns and incident-response indicators",
        "platform": Platform.MULTI,
        "author": "KittySploit Team",
        "session_type": [
            SessionType.SHELL,
            SessionType.METERPRETER,
            SessionType.SSH],
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [],
         'consumes_capabilities': ['shell'],
         'option_bindings': {},
         'suggested_followups': []},
    },
    }

    target = OptChoice(
        "auto",
        "Target platform: auto, linux, windows",
        False,
        choices=["auto", "linux", "windows"],
    )
    max_history_files = OptInteger(25, "Maximum history files to inspect", False)
    max_hits_per_category = OptInteger(20, "Maximum suspicious commands to print per category", False)

    _PATTERNS = {
        "defense_evasion": [
            r"\b(history\s+-c|unset\s+HISTFILE|Set-PSReadLineOption\s+-HistorySaveStyle\s+SaveNothing)\b",
            r"\b(clear-eventlog|wevtutil\s+cl|auditpol\s+/clear)\b",
            r"\b(systemctl\s+stop\s+(auditd|rsyslog)|service\s+(auditd|rsyslog)\s+stop)\b",
            r"\b(iptables\s+-F|ufw\s+disable|netsh\s+advfirewall\s+set\s+allprofiles\s+state\s+off)\b"],
        "download_execution": [
            r"\b(curl|wget)\b.*(\|\s*(sh|bash)|-o\s+/tmp|Invoke-WebRequest|iwr)",
            r"\b(powershell(\.exe)?\s+.*(DownloadString|IEX|Invoke-Expression))\b",
            r"\b(certutil\s+-urlcache\s+-f|bitsadmin\s+/transfer|mshta\s+http)\b",
            r"\b(chmod\s+\+x\s+/tmp/|/tmp/[^ ]+\s*$)\b"],
        "credential_access": [
            r"\b(cat|grep|awk)\b.*(/etc/shadow|/etc/passwd|id_rsa|\.aws/credentials|\.kube/config)\b",
            r"\b(secretsdump|mimikatz|lsass|sam\s+save|reg\s+save\s+hklm\\sam)\b",
            r"\b(findstr|grep)\b.*(password|passwd|token|secret|api[_-]?key)\b"],
        "privilege_escalation": [
            r"\b(sudo\s+-l|sudo\s+su|su\s+-|pkexec|doas)\b",
            r"\b(chmod\s+u\+s|setcap\s+|getcap\s+)\b",
            r"\b(useradd|usermod|net\s+user\s+.*\s+/add|net\s+localgroup\s+administrators)\b"],
        "persistence": [
            r"\b(crontab\s+-e|echo\s+.*\|\s*crontab|/etc/cron\.|schtasks\s+/create)\b",
            r"\b(systemctl\s+enable|ln\s+-s\s+.*systemd|rc\.local|update-rc\.d)\b",
            r"\b(reg\s+add\s+HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run)\b",
            r"\b(authorized_keys|Startup\\|\.bashrc|\.profile|\.zshrc)\b"],
        "reconnaissance": [
            r"\b(whoami|id|uname\s+-a|hostnamectl|systeminfo|ipconfig\s+/all)\b",
            r"\b(ifconfig|ip\s+a|route\s+-n|netstat\s+-an|ss\s+-antup|arp\s+-a)\b",
            r"\b(ps\s+aux|tasklist|wmic\s+process|Get-Process)\b"],
    }

    def _run_cmd(self, command: str) -> str:
        try:
            output = self.cmd_execute(command)
            return output.strip() if output else ""
        except Exception:
            try:
                output = self.cmd_exec(command)
                return output.strip() if output else ""
            except Exception:
                return ""

    def _detect_platform(self) -> str:
        selected = str(self.target or "auto").strip().lower()
        if selected in ("linux", "windows"):
            return selected

        probe = self._run_cmd("uname -s 2>/dev/null")
        if probe and any(v in probe.lower() for v in ("linux", "darwin", "bsd")):
            return "linux"

        probe = self._run_cmd("cmd /c ver")
        if probe and "windows" in probe.lower():
            return "windows"
        return "linux"

    def _print_section(self, title: str):
        print_status("=" * 60)
        print_status(title)
        print_status("=" * 60)

    def _collect_history_linux(self) -> list:
        max_files = int(self.max_history_files)
        output = self._run_cmd(
            "find /home /root -maxdepth 4 -type f "
            "\\( -name '.bash_history' -o -name '.zsh_history' -o -name '.history' -o -name '.ash_history' \\) "
            "2>/dev/null | head -n {count}".format(count=max_files)
        )
        files = [line.strip() for line in output.splitlines() if line.strip()]
        commands = []
        for path in files:
            lines = self._run_cmd("tail -n 500 '{path}' 2>/dev/null".format(path=path.replace("'", "'\\''")))
            for idx, line in enumerate(lines.splitlines(), 1):
                cmd = line.strip()
                if cmd and not cmd.startswith("#"):
                    commands.append((path, idx, cmd))
        return commands

    def _collect_history_windows(self) -> list:
        max_files = int(self.max_history_files)
        file_list = self._run_cmd(
            'powershell -NoP -Command "Get-ChildItem -Path C:\\Users -Recurse '
            '-Filter ConsoleHost_history.txt -ErrorAction SilentlyContinue | '
            'Select-Object -First {count} -ExpandProperty FullName"'.format(count=max_files)
        )
        files = [line.strip() for line in file_list.splitlines() if line.strip()]
        commands = []
        for path in files:
            ps = (
                'powershell -NoP -Command "$i=0; Get-Content -Path \\"{path}\\" -ErrorAction SilentlyContinue | '
                'Select-Object -Last 500 | ForEach-Object {{ $i++; \\"$i`t$_\\" }}"'
            ).format(path=path.replace('"', '`"'))
            lines = self._run_cmd(ps)
            for line in lines.splitlines():
                if "\t" not in line:
                    continue
                idx_s, cmd = line.split("\t", 1)
                cmd = cmd.strip()
                if cmd and not cmd.startswith("#"):
                    try:
                        idx = int(idx_s.strip())
                    except Exception:
                        idx = 0
                    commands.append((path, idx, cmd))
        return commands

    def _analyze(self, commands: list) -> dict:
        findings = {category: [] for category in self._PATTERNS}
        for path, line_no, cmd in commands:
            for category, patterns in self._PATTERNS.items():
                matched = False
                for pattern in patterns:
                    if re.search(pattern, cmd, re.IGNORECASE):
                        findings[category].append((path, line_no, cmd))
                        matched = True
                        break
                if matched:
                    # A single command can match multiple categories if needed,
                    # but we avoid duplicate entries in same category.
                    continue
        return findings

    def _severity_score(self, findings: dict) -> int:
        weights = {
            "defense_evasion": 5,
            "credential_access": 5,
            "persistence": 4,
            "privilege_escalation": 4,
            "download_execution": 3,
            "reconnaissance": 1,
        }
        score = 0
        for category, entries in findings.items():
            score += len(entries) * weights.get(category, 1)
        return score

    def _severity_label(self, score: int) -> str:
        if score >= 80:
            return "high"
        if score >= 35:
            return "medium"
        if score > 0:
            return "low"
        return "none"

    def _print_findings(self, findings: dict):
        limit = int(self.max_hits_per_category)
        for category in [
            "defense_evasion",
            "credential_access",
            "persistence",
            "privilege_escalation",
            "download_execution",
            "reconnaissance"]:
            entries = findings.get(category, [])
            self._print_section(f"Category: {category} ({len(entries)} hit(s))")
            if not entries:
                print_info("No suspicious command found in this category")
                continue
            for path, line_no, cmd in entries[:limit]:
                print_warning(f"{path}:{line_no} -> {cmd}")

    def run(self):
        platform = self._detect_platform()
        print_status(f"Running command history forensic analysis on: {platform}")

        if platform == "windows":
            commands = self._collect_history_windows()
        else:
            commands = self._collect_history_linux()

        self._print_section("Collection Summary")
        print_info(f"Collected command lines: {len(commands)}")
        if not commands:
            print_warning("No history data collected from the inspected paths")
            return True

        findings = self._analyze(commands)
        score = self._severity_score(findings)
        severity = self._severity_label(score)

        self._print_findings(findings)
        self._print_section("Risk Summary")
        print_info(f"Forensic score: {score}")
        if severity == "high":
            print_error("Severity: high (multiple strong indicators of malicious activity)")
        elif severity == "medium":
            print_warning("Severity: medium (several suspicious commands detected)")
        elif severity == "low":
            print_warning("Severity: low (limited suspicious activity detected)")
        else:
            print_success("Severity: none (no suspicious command pattern detected)")

        print_success("Command history analysis completed")
        return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Aug 2026 20:51Current
6Medium risk
Vulners AI Score6
3