Files
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

234 lines
8.0 KiB
Python

#!/usr/bin/env python3
"""Privileged session monitoring agent.
Monitors SSH auth logs, Windows RDP events, and database sessions to
track privileged access, detect anomalies, and generate audit reports.
"""
import argparse
import json
import re
import sys
import datetime
import collections
try:
import subprocess
HAS_SUBPROCESS = True
except ImportError:
HAS_SUBPROCESS = False
SESSION_POLICIES = {
"max_duration_hours": 8,
"max_idle_minutes": 30,
"require_mfa": True,
"record_session": True,
"allowed_hours": {"start": 6, "end": 22},
"restricted_commands": [
r"rm\s+-rf\s+/", r"dd\s+if=", r"mkfs\.", r"shutdown", r"reboot",
r"passwd\s+root", r"visudo", r"chmod\s+777", r"iptables\s+-F",
],
}
PRIVILEGED_ACCOUNTS = [
"root", "administrator", "admin", "dba", "sa",
"sysadmin", "service_account", "deploy",
]
def parse_ssh_auth_log(log_path, max_lines=10000):
"""Parse SSH authentication log for session events."""
sessions = []
pattern = re.compile(
r"(\w+\s+\d+\s+[\d:]+)\s+(\S+)\s+sshd\[(\d+)\]:\s+"
r"(Accepted|Failed)\s+(\S+)\s+for\s+(\S+)\s+from\s+(\S+)\s+port\s+(\d+)"
)
try:
with open(log_path, "r") as f:
for i, line in enumerate(f):
if i >= max_lines:
break
m = pattern.search(line)
if m:
sessions.append({
"timestamp": m.group(1),
"hostname": m.group(2),
"pid": m.group(3),
"status": m.group(4).lower(),
"auth_method": m.group(5),
"username": m.group(6),
"source_ip": m.group(7),
"source_port": int(m.group(8)),
"privileged": m.group(6).lower() in PRIVILEGED_ACCOUNTS,
})
except FileNotFoundError:
return {"error": f"Log file not found: {log_path}"}
return sessions
def parse_rdp_events_windows():
"""Parse Windows RDP logon events via PowerShell."""
cmd = (
"Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624} "
"| Where-Object {$_.Properties[8].Value -eq 10} "
"| Select-Object -First 50 "
"| ForEach-Object { @{TimeCreated=$_.TimeCreated.ToString('o');"
"User=$_.Properties[5].Value;Domain=$_.Properties[6].Value;"
"SourceIP=$_.Properties[18].Value} } | ConvertTo-Json"
)
try:
result = subprocess.run(
["powershell", "-NoProfile", "-Command", cmd],
capture_output=True, text=True, timeout=30
)
if result.stdout.strip():
data = json.loads(result.stdout)
if isinstance(data, dict):
data = [data]
return [
{
"timestamp": d.get("TimeCreated", ""),
"username": d.get("User", ""),
"domain": d.get("Domain", ""),
"source_ip": d.get("SourceIP", ""),
"session_type": "RDP",
"privileged": d.get("User", "").lower() in PRIVILEGED_ACCOUNTS,
}
for d in data
]
return []
except (subprocess.SubprocessError, json.JSONDecodeError):
return []
def check_command_policy(command):
"""Check if a command violates session policy."""
violations = []
for pattern in SESSION_POLICIES["restricted_commands"]:
if re.search(pattern, command, re.IGNORECASE):
violations.append({
"pattern": pattern,
"command": command[:100],
"action": "alert_and_log",
})
return violations
def detect_session_anomalies(sessions):
"""Detect anomalous session patterns."""
anomalies = []
ip_counts = collections.Counter(s.get("source_ip") for s in sessions)
user_counts = collections.Counter(s.get("username") for s in sessions)
for ip, count in ip_counts.items():
if count > 20:
anomalies.append({
"type": "brute_force_candidate",
"source_ip": ip,
"attempt_count": count,
"severity": "HIGH",
})
failed = [s for s in sessions if s.get("status") == "failed"]
failed_by_user = collections.Counter(s.get("username") for s in failed)
for user, count in failed_by_user.items():
if count >= 5:
anomalies.append({
"type": "failed_auth_spike",
"username": user,
"failed_count": count,
"severity": "HIGH" if user in PRIVILEGED_ACCOUNTS else "MEDIUM",
})
priv_sessions = [s for s in sessions if s.get("privileged")]
for ps in priv_sessions:
anomalies.append({
"type": "privileged_session",
"username": ps.get("username"),
"source_ip": ps.get("source_ip"),
"severity": "MEDIUM",
})
return anomalies
def generate_audit_report(sessions, anomalies):
"""Generate privileged session audit report."""
total = len(sessions)
privileged = sum(1 for s in sessions if s.get("privileged"))
accepted = sum(1 for s in sessions if s.get("status") == "accepted")
failed = sum(1 for s in sessions if s.get("status") == "failed")
unique_users = len(set(s.get("username") for s in sessions))
unique_ips = len(set(s.get("source_ip") for s in sessions))
return {
"report_time": datetime.datetime.utcnow().isoformat() + "Z",
"total_sessions": total,
"accepted": accepted,
"failed": failed,
"privileged_sessions": privileged,
"unique_users": unique_users,
"unique_source_ips": unique_ips,
"anomaly_count": len(anomalies),
"policy": SESSION_POLICIES,
}
def main():
parser = argparse.ArgumentParser(description="Privileged session monitoring agent")
parser.add_argument("--ssh-log", help="Path to SSH auth.log")
parser.add_argument("--rdp", action="store_true", help="Parse Windows RDP events")
parser.add_argument("--check-command", help="Check a command against policy")
parser.add_argument("--output", "-o", help="Output JSON report path")
args = parser.parse_args()
print("[*] Privileged Session Monitoring Agent")
report = {"timestamp": datetime.datetime.utcnow().isoformat() + "Z"}
sessions = []
if args.ssh_log:
sessions = parse_ssh_auth_log(args.ssh_log)
if isinstance(sessions, dict) and "error" in sessions:
print(f"[!] {sessions['error']}")
sessions = []
else:
print(f"[*] SSH sessions parsed: {len(sessions)}")
if args.rdp and sys.platform == "win32":
rdp_sessions = parse_rdp_events_windows()
sessions.extend(rdp_sessions)
print(f"[*] RDP sessions parsed: {len(rdp_sessions)}")
if args.check_command:
violations = check_command_policy(args.check_command)
if violations:
print(f"[!] Command policy violations: {len(violations)}")
for v in violations:
print(f" Pattern: {v['pattern']}")
else:
print("[*] Command passes policy check")
report["command_check"] = {"command": args.check_command, "violations": violations}
if sessions:
anomalies = detect_session_anomalies(sessions)
audit = generate_audit_report(sessions, anomalies)
report["sessions"] = audit
report["anomalies"] = anomalies
print(f"[*] Anomalies detected: {len(anomalies)}")
else:
print("\n[DEMO] Usage: python agent.py --ssh-log /var/log/auth.log")
print(" Monitors: SSH, RDP, privileged account access")
print(" Detects: brute force, failed auth spikes, off-hours access")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps({"sessions": len(sessions),
"anomalies": len(report.get("anomalies", []))}, indent=2))
if __name__ == "__main__":
main()