| argument-hint | [linux=user@host] [windows=host] | ||||
|---|---|---|---|---|---|
| allowed-tools |
|
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.
$ARGUMENTS optionally contains:
linux=user@host— Linux target (default:debian@10.2.10.15)windows=host— Windows target (uses$WIN_USERand$WIN_PASSenv vars for NTLM auth)
If neither is provided, default to Linux only with debian@10.2.10.15.
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).
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)This is a multi-agent workflow. The main agent orchestrates two parallel subagents for data collection, then evaluates and reports.
- Create the working directory:
mkdir -p /tmp/log-compliance- 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>&1Where $LINUX_PASS defaults to debian if the env var is not set.
- 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_USERand$WIN_PASSare set - Write the embedded Windows collection script to
/tmp/log-compliance/win_collect.py
- Verify pypsrp is installed:
Spawn agents in parallel using the Agent tool. If both platforms are specified, launch both agents simultaneously in a single message.
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@HOSTwhere LINUX_PASS defaults todebianif not set.Run these 7 commands via SSH. Collect ALL output. If a command fails or returns empty, record that — it IS the finding.
sudo auditctl -l 2>/dev/null || echo "AUDITCTL_UNAVAILABLE"systemctl is-active auditd 2>/dev/null; echo "---"; systemctl is-enabled auditd 2>/dev/nullcat /etc/systemd/journald.conf 2>/dev/null || echo "FILE_NOT_FOUND"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; donestat -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"cat /etc/audit/auditd.conf 2>/dev/null || echo "FILE_NOT_FOUND"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/nullWrite ALL raw output to
/tmp/log-compliance/linux_raw.txtusing the Write tool. Use section headers like=== COMMAND N: description ===between each command's output so the results are clearly separated.
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.jsonusing 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.
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)
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).
| 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 |
| 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) |
For each check result, map it through this crosswalk table. Include ALL four framework columns in the output.
| 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 |
Build a note with this structure:
---
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
---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.
> [!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.
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.
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 restartL7 — 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-journaldW7 — 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="..."
Report to the user:
- Overall compliance percentage
- Per-platform pass/fail/warn breakdown
- Top 3 most impactful failures (by ATT&CK technique coverage count)
- Confirm Obsidian note written with path