Skip to content

Instantly share code, notes, and snippets.

@Antonlovesdnb
Created April 4, 2026 15:50
Show Gist options
  • Select an option

  • Save Antonlovesdnb/01c22a6712df3eab1681b66121e44586 to your computer and use it in GitHub Desktop.

Select an option

Save Antonlovesdnb/01c22a6712df3eab1681b66121e44586 to your computer and use it in GitHub Desktop.
log-compliance
argument-hint [linux=user@host] [windows=host]
allowed-tools
Bash
Read
Write
Agent

Log Compliance Auditor — Multi-Agent CIS Benchmark Assessment

Run a cross-platform log compliance audit using parallel agents. A Linux agent SSHes into the target and checks auditd, journald, rsyslog, and log permissions. A Windows agent connects via pypsrp (WinRM) and checks audit policy, event log sizes, PowerShell logging, and Sysmon. Results are evaluated against CIS Benchmarks and cross-referenced to NIST 800-53, PCI-DSS 4.0, and MITRE ATT&CK.

Input

$ARGUMENTS optionally contains:

  • linux=user@host — Linux target (default: debian@10.2.10.15)
  • windows=host — Windows target (uses $WIN_USER and $WIN_PASS env vars for NTLM auth)

If neither is provided, default to Linux only with debian@10.2.10.15.

Configuration

LINUX_TARGET  -- from args or default debian@10.2.10.15
LINUX_PASS    -- from $LINUX_PASS env var (default: debian)
WIN_HOST      -- from args (optional)
WIN_USER      -- from $WIN_USER env var
WIN_PASS      -- from $WIN_PASS env var
VAULT=Obsidian Vault

Parse the linux= argument to extract user and host. If only a host is given (no user@), default user to debian. Use sshpass -p $LINUX_PASS for SSH authentication (password-based).

Embedded Windows Collection Script

If a Windows target is specified, write this script to /tmp/log-compliance/win_collect.py before spawning agents:

#!/usr/bin/env python3
"""Windows log compliance data collector via pypsrp (WinRM/NTLM)."""
import json
import sys
import argparse

from pypsrp.client import Client

def collect(host, username, password):
    findings = {}

    with Client(host, username=username, password=password,
                ssl=False, auth="ntlm", port=5985,
                connection_timeout=30, read_timeout=30) as client:

        # 1. Audit policy (CSV format for reliable parsing)
        out, streams, had_errors = client.execute_ps(
            'auditpol /get /category:* /r'
        )
        findings['auditpol'] = out.strip()

        # 2. Security event log config
        out, streams, had_errors = client.execute_ps(
            'wevtutil gl Security'
        )
        findings['security_log'] = out.strip()

        # 3. PowerShell Operational log config
        out, streams, had_errors = client.execute_ps(
            'wevtutil gl "Microsoft-Windows-PowerShell/Operational"'
        )
        findings['powershell_log'] = out.strip()

        # 4. PowerShell Script Block Logging registry
        out, streams, had_errors = client.execute_ps('''
            try {
                $val = Get-ItemProperty "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\PowerShell\\ScriptBlockLogging" -ErrorAction Stop
                $val | Select-Object EnableScriptBlockLogging, EnableScriptBlockInvocationLogging | ConvertTo-Json
            } catch {
                "NOT_CONFIGURED"
            }
        ''')
        findings['scriptblock_logging'] = out.strip()

        # 5. PowerShell Module Logging registry
        out, streams, had_errors = client.execute_ps('''
            try {
                $val = Get-ItemProperty "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\PowerShell\\ModuleLogging" -ErrorAction Stop
                $val | Select-Object EnableModuleLogging | ConvertTo-Json
            } catch {
                "NOT_CONFIGURED"
            }
        ''')
        findings['module_logging'] = out.strip()

        # 6. Sysmon service status
        out, streams, had_errors = client.execute_ps(
            'Get-Service sysmon* 2>$null | Select-Object Name,Status,StartType | ConvertTo-Json'
        )
        findings['sysmon'] = out.strip() if out.strip() else "NOT_INSTALLED"

        # 7. Process creation command-line logging registry
        out, streams, had_errors = client.execute_ps('''
            try {
                $val = Get-ItemProperty "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Audit" -ErrorAction Stop
                $val | Select-Object ProcessCreationIncludeCmdLine_Enabled | ConvertTo-Json
            } catch {
                "NOT_CONFIGURED"
            }
        ''')
        findings['cmdline_logging'] = out.strip()

        # 8. Event log sizes (Security, System, PowerShell)
        out, streams, had_errors = client.execute_ps('''
            @("Security","System","Microsoft-Windows-PowerShell/Operational") | ForEach-Object {
                $log = Get-WinEvent -ListLog $_ -ErrorAction SilentlyContinue
                if ($log) {
                    [PSCustomObject]@{
                        LogName = $log.LogName
                        MaxSizeBytes = $log.MaximumSizeInBytes
                        MaxSizeMB = [math]::Round($log.MaximumSizeInBytes / 1MB, 1)
                        RecordCount = $log.RecordCount
                        IsEnabled = $log.IsEnabled
                        LogMode = $log.LogMode
                    }
                }
            } | ConvertTo-Json
        ''')
        findings['event_log_sizes'] = out.strip()

    return findings


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--host', required=True)
    parser.add_argument('--user', required=True)
    parser.add_argument('--password', required=True)
    args = parser.parse_args()

    try:
        data = collect(args.host, args.user, args.password)
        print(json.dumps(data, indent=2))
    except Exception as e:
        print(json.dumps({"error": str(e)}), file=sys.stderr)
        sys.exit(1)

Workflow

This is a multi-agent workflow. The main agent orchestrates two parallel subagents for data collection, then evaluates and reports.

Step 0 — Prerequisites

  1. Create the working directory:
mkdir -p /tmp/log-compliance
  1. If a Linux target is specified, verify SSH connectivity:
sshpass -p "$LINUX_PASS" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 USER@HOST "echo ok" 2>&1

Where $LINUX_PASS defaults to debian if the env var is not set.

  1. If a Windows target is specified:
    • Verify pypsrp is installed: python3 -c "import pypsrp" 2>/dev/null
    • If not: pip install --break-system-packages pypsrp
    • Verify $WIN_USER and $WIN_PASS are set
    • Write the embedded Windows collection script to /tmp/log-compliance/win_collect.py

Step 1 — Parallel agent data collection

Spawn agents in parallel using the Agent tool. If both platforms are specified, launch both agents simultaneously in a single message.

Linux Agent

Spawn an Agent with this prompt (fill in USER, HOST from parsed arguments):

You are a Linux log compliance data collector. SSH into the target host and collect logging configuration data.

Connection: sshpass -p "LINUX_PASS" ssh -o StrictHostKeyChecking=no USER@HOST where LINUX_PASS defaults to debian if not set.

Run these 7 commands via SSH. Collect ALL output. If a command fails or returns empty, record that — it IS the finding.

  1. sudo auditctl -l 2>/dev/null || echo "AUDITCTL_UNAVAILABLE"
  2. systemctl is-active auditd 2>/dev/null; echo "---"; systemctl is-enabled auditd 2>/dev/null
  3. cat /etc/systemd/journald.conf 2>/dev/null || echo "FILE_NOT_FOUND"
  4. cat /etc/rsyslog.conf 2>/dev/null; echo "===RSYSLOG_D==="; ls /etc/rsyslog.d/ 2>/dev/null; for f in /etc/rsyslog.d/*.conf; do echo "===FILE:$f==="; cat "$f" 2>/dev/null; done
  5. stat -c '%a %U %G %n' /var/log/auth.log /var/log/syslog /var/log/kern.log /var/log/audit/audit.log /var/log/messages /var/log/secure 2>/dev/null || echo "STAT_PARTIAL"
  6. cat /etc/audit/auditd.conf 2>/dev/null || echo "FILE_NOT_FOUND"
  7. cat /etc/logrotate.conf 2>/dev/null; echo "===LOGROTATE_D==="; cat /etc/logrotate.d/rsyslog 2>/dev/null; cat /etc/logrotate.d/auditd 2>/dev/null

Write ALL raw output to /tmp/log-compliance/linux_raw.txt using the Write tool. Use section headers like === COMMAND N: description === between each command's output so the results are clearly separated.

Windows Agent

Spawn an Agent with this prompt (fill in HOST from parsed arguments):

You are a Windows log compliance data collector. Run the pypsrp collection script to gather Windows audit configuration.

Execute this command:

python3 /tmp/log-compliance/win_collect.py --host HOST --user "$WIN_USER" --password "$WIN_PASS"

Write the JSON output to /tmp/log-compliance/windows_raw.json using the Write tool.

If the script fails with a connection error, try with port 5986 and ssl=True by editing the script. If it still fails, write the error to the output file so the main agent can report it.

Step 2 — Read collection results

After both agents complete, read the output files:

  • /tmp/log-compliance/linux_raw.txt (if Linux was audited)
  • /tmp/log-compliance/windows_raw.json (if Windows was audited)

Step 3 — Compliance evaluation

Evaluate each check against the CIS Benchmark tables below. For each check, determine:

  • PASS: Configuration meets or exceeds CIS requirement
  • FAIL: Configuration does not meet CIS requirement
  • WARN: Insufficient privileges to check, or partial compliance

Record the expected value, actual value, and evidence (the raw output that proves it).

Linux CIS Checks

ID CIS Ref Check Name Pass Criteria
L1 4.1.1.1 auditd installed and enabled systemctl is-active auditd = "active" AND is-enabled = "enabled"
L2 4.1.1.3 Audit rules immutable auditctl -l output contains -e 2
L3 4.1.4 Time change events logged auditctl -l contains rules for adjtimex, settimeofday, clock_settime, AND /etc/localtime watch
L4 4.1.6 Identity file watches auditctl -l contains watches on /etc/passwd, /etc/shadow, /etc/group, /etc/gshadow
L5 4.1.11 Privilege escalation logged auditctl -l contains rules for execve with uid/euid checks OR watches on /usr/bin/sudo, /usr/bin/su
L6 4.1.17 Kernel module events auditctl -l contains rules for init_module, delete_module, finit_module OR watches on /sbin/insmod, /sbin/modprobe
L7 4.2.1.1 journald uses persistent storage /etc/systemd/journald.conf contains Storage=persistent (not commented out)
L8 4.2.2.1 rsyslog remote forwarding rsyslog configs contain @@ (TCP) or @ (UDP) remote target, OR omfwd action with target=
L9 4.2.3 Log file permissions <=640 stat output for log files shows permissions 640 or more restrictive (600, 400, etc.). Permissions like 644, 664, 755 are FAIL.
L10 4.1.2.3 Audit log retention adequate auditd.conf has max_log_file_action = keep_logs AND num_logs >= 5

Windows CIS Checks

ID CIS Ref Check Name Pass Criteria
W1 17.1.1 Credential Validation auditpol shows "Credential Validation" with "Success and Failure"
W2 17.2.1 User Account Management auditpol shows "User Account Management" with "Success and Failure"
W3 17.5.1 Logon auditpol shows "Logon" with "Success and Failure"
W4 17.5.3 Special Logon auditpol shows "Special Logon" with at least "Success"
W5 17.6.1 Sensitive Privilege Use auditpol shows "Sensitive Privilege Use" with "Success and Failure"
W6 17.7.1 Audit Policy Change auditpol shows "Audit Policy Change" with at least "Success"
W7 17.9.1 Process Creation with cmd line auditpol shows "Process Creation" with at least "Success" AND ProcessCreationIncludeCmdLine_Enabled = 1
W8 18.9.100 PowerShell Script Block Logging EnableScriptBlockLogging = 1 in registry
W9 — Sysmon installed and running Sysmon service exists with Status = "Running"
W10 — Security log >= 192MB Security log MaxSizeBytes >= 201326592 (192MB)

Step 4 — Framework crosswalk

For each check result, map it through this crosswalk table. Include ALL four framework columns in the output.

Full Crosswalk Reference

Check CIS NIST 800-53 PCI-DSS 4.0 ATT&CK Impact
L1 4.1.1.1 AU-2 Audit Events, AU-12 Audit Record Generation 10.2.1 Audit logs capture events DS0017 Command — if auditd is off, no Linux command/process auditing
L2 4.1.1.3 AU-9 Protection of Audit Information 10.3.4 Audit logs cannot be modified T1562.001 Impair Defenses: Disable or Modify Tools — mutable rules let attackers flush audit trail
L3 4.1.4 AU-8 Time Stamps 10.6.3 Time synchronization settings T1070.006 Timestomp — without time-change auditing, attackers can manipulate timestamps undetected
L4 4.1.6 AU-2 Audit Events 10.2.1.5 Changes to credentials T1098 Account Manipulation — no visibility into passwd/shadow/group changes
L5 4.1.11 AU-2 Audit Events 10.2.1.5 Privilege escalation T1548 Abuse Elevation Control — sudo/su usage invisible
L6 4.1.17 AU-2 Audit Events 10.2.1.7 System-level object changes T1547.006 Kernel Modules and Extensions — rootkit loading undetected
L7 4.2.1.1 AU-9 Protection of Audit Information 10.3.1 Audit log data protected T1070.002 Clear Linux or Mac System Logs — volatile storage means reboot destroys evidence
L8 4.2.2.1 AU-4 Audit Log Storage, AU-9 10.3.3 Audit logs backed up T1070.002 — logs only exist on compromised host, attacker can destroy
L9 4.2.3 AU-9 Protection of Audit Information 10.3.1 Audit log data protected T1070.002 — world-readable logs leak sensitive data and are easily tampered
L10 4.1.2.3 AU-11 Audit Record Retention 10.7.1 Retain logs 12 months — Log retention too short for incident investigation
W1 17.1.1 AU-2 Audit Events 10.2.1.4 Invalid access attempts T1110 Brute Force — credential attacks invisible
W2 17.2.1 AU-2 Audit Events 10.2.1.5 Changes to credentials T1098 Account Manipulation — account changes untracked
W3 17.5.1 AU-2 Audit Events 10.2.1.1 User access to data T1078 Valid Accounts — logon anomalies invisible
W4 17.5.3 AU-2 Audit Events 10.2.1.2 Admin actions T1078 Valid Accounts — privileged logons untracked
W5 17.6.1 AU-2 Audit Events 10.2.1.5 Privilege escalation T1134 Access Token Manipulation — privilege abuse invisible
W6 17.7.1 AU-2 Audit Events 10.2.1.6 Audit log changes T1562.002 Impair Defenses: Disable Windows Event Logging — policy tampering undetected
W7 17.9.1 AU-2 Audit Events 10.2.1.7 System object changes T1059 Command and Scripting Interpreter — covers 206+ techniques via process creation
W8 18.9.100 AU-2 Audit Events 10.2.1.2 Admin actions T1059.001 PowerShell — obfuscated script execution invisible
W9 — AU-2 Audit Events 10.2.1 Audit logs capture events 206+ techniques — Sysmon provides process, network, DNS, registry, file, and DLL telemetry
W10 — AU-4 Audit Log Storage Capacity 10.3.1 Audit log data protected — Small log rolls over quickly, destroying evidence during active attacks

Step 5 — Generate Obsidian note

Build a note with this structure:

Frontmatter

---
scan_date: YYYY-MM-DD
linux_host: "host or N/A"
windows_host: "host or N/A"
framework: CIS Benchmarks
linux_checks: N
linux_pass: N
linux_fail: N
linux_warn: N
windows_checks: N
windows_pass: N
windows_fail: N
windows_warn: N
overall_compliance_pct: N%
tags:
  - compliance
  - cis-benchmark
  - log-audit
---

TUI Compliance Dashboard

Display as a fenced code block:

Log Compliance Audit — YYYY-MM-DD
═══════════════════════════════════════════════════════════════════
Platform     Pass  Fail  Warn  Score
───────────────────────────────────────────────────────────────────
Linux        ████████████████░░░░   8/10    80%
Windows      ██████████░░░░░░░░░░   5/10    50%
───────────────────────────────────────────────────────────────────
Overall      █████████████░░░░░░░  13/20    65%

NIST 800-53 AU Control Coverage
───────────────────────────────────────────────────────────────────
AU-2  Event Logging          ████████████████░░░░  80%
AU-4  Audit Log Storage      ████████░░░░░░░░░░░░  40%
AU-8  Time Stamps            ████████████████████ 100%
AU-9  Log Protection         ██████████████░░░░░░  60%
AU-11 Record Retention       ████████████████████ 100%
AU-12 Record Generation      ████████████████████ 100%

Use █ for filled and ░ for empty. Scale bars to 20 characters. Calculate percentages from actual pass/fail counts per NIST control.

Findings Table

> [!success] N checks passed | [!failure] N checks failed | [!warning] N checks warned

## Linux Findings

| Status | ID | CIS | Check | Expected | Actual | NIST | PCI-DSS | ATT&CK |
|--------|----|-----|-------|----------|--------|------|---------|--------|
| PASS | L1 | 4.1.1.1 | auditd enabled | active+enabled | active+enabled | AU-2 | 10.2.1 | DS0017 |
| FAIL | L2 | 4.1.1.3 | Rules immutable | -e 2 present | not found | AU-9 | 10.3.4 | T1562.001 |
...

## Windows Findings

| Status | ID | CIS | Check | Expected | Actual | NIST | PCI-DSS | ATT&CK |
|--------|----|-----|-------|----------|--------|------|---------|--------|
...

Use callout icons: PASS = checkmark, FAIL = X, WARN = warning.

ATT&CK Impact Analysis

For each FAIL finding, write a brief paragraph explaining the detection gap:

## ATT&CK Detection Gaps

> [!danger] Critical Detection Gaps from Compliance Failures

### L2 — Audit Rules Not Immutable → T1562.001
Without immutable audit rules, an attacker with root access can flush or modify the audit trail in real-time, covering their tracks for any technique they execute.

### W7 — Process Creation Auditing Disabled → T1059 (206+ techniques)
Process Creation is the single highest-leverage data source in MITRE ATT&CK, covering 206+ techniques. Without it, the majority of post-exploitation activity is invisible.

Only include this section for FAIL results. Focus on the highest-impact gaps.

Remediation

Group by platform, provide copy-paste commands:

## Remediation

### Linux

**L2 — Enable immutable audit rules (CIS 4.1.1.3)**
```bash
echo "-e 2" | sudo tee -a /etc/audit/rules.d/99-finalize.rules
sudo service auditd restart

L7 — Enable journald persistent storage (CIS 4.2.1.1)

sudo sed -i 's/#Storage=auto/Storage=persistent/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald

Windows

W7 — Enable Process Creation auditing with command line (CIS 17.9.1)

auditpol /set /subcategory:"Process Creation" /success:enable
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" /v ProcessCreationIncludeCmdLine_Enabled /t REG_DWORD /d 1 /f

Provide remediation for every FAIL finding. Include the CIS reference in each heading.

#### Write the note

```bash
obsidian create vault="Obsidian Vault" name="Log Compliance — $(date +%Y-%m-%d)" overwrite content="..."

Step 6 — Summary

Report to the user:

  1. Overall compliance percentage
  2. Per-platform pass/fail/warn breakdown
  3. Top 3 most impactful failures (by ATT&CK technique coverage count)
  4. Confirm Obsidian note written with path
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment