mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-15 12:15:16 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
218 lines
8.0 KiB
Python
218 lines
8.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Osquery Endpoint Monitoring Agent - Generates configs, deploys queries, and analyzes results."""
|
|
|
|
import json
|
|
import os
|
|
import logging
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
SECURITY_QUERIES = {
|
|
"process_not_on_disk": {
|
|
"query": "SELECT pid, name, path, cmdline, uid FROM processes WHERE on_disk = 0;",
|
|
"interval": 300,
|
|
"description": "Detect processes running from deleted binaries",
|
|
},
|
|
"listening_ports": {
|
|
"query": (
|
|
"SELECT lp.pid, lp.port, lp.protocol, lp.address, p.name, p.path "
|
|
"FROM listening_ports lp JOIN processes p ON lp.pid = p.pid "
|
|
"WHERE lp.port NOT IN (22, 80, 443, 3306, 5432);"
|
|
),
|
|
"interval": 600,
|
|
"description": "Monitor unexpected listening ports",
|
|
},
|
|
"outbound_connections": {
|
|
"query": (
|
|
"SELECT pid, remote_address, remote_port, local_port, p.name, p.path "
|
|
"FROM process_open_sockets pos JOIN processes p ON pos.pid = p.pid "
|
|
"WHERE remote_address NOT IN ('0.0.0.0', '127.0.0.1', '::1', '') "
|
|
"AND remote_address NOT LIKE '10.%' AND remote_address NOT LIKE '192.168.%';"
|
|
),
|
|
"interval": 300,
|
|
"description": "Monitor external outbound connections",
|
|
},
|
|
"cron_persistence": {
|
|
"query": "SELECT * FROM crontab WHERE command NOT LIKE '%logrotate%' AND command NOT LIKE '%anacron%';",
|
|
"interval": 3600,
|
|
"description": "Detect new cron job persistence",
|
|
},
|
|
"suid_binaries": {
|
|
"query": "SELECT path, mode, uid, gid FROM suid_bin WHERE path NOT LIKE '/usr/%' AND path NOT LIKE '/bin/%';",
|
|
"interval": 3600,
|
|
"description": "Detect SUID binaries outside standard paths",
|
|
},
|
|
"file_integrity_etc": {
|
|
"query": (
|
|
"SELECT path, mtime, size, sha256 FROM file "
|
|
"WHERE path LIKE '/etc/%%' AND mtime > (SELECT CAST(strftime('%s', 'now', '-1 hour') AS INTEGER));"
|
|
),
|
|
"interval": 600,
|
|
"description": "Monitor file changes in /etc",
|
|
},
|
|
"kernel_modules": {
|
|
"query": "SELECT name, size, status, address FROM kernel_modules WHERE status = 'Live';",
|
|
"interval": 3600,
|
|
"description": "Monitor loaded kernel modules",
|
|
},
|
|
"authorized_keys": {
|
|
"query": "SELECT uid, algorithm, key, key_file FROM authorized_keys;",
|
|
"interval": 3600,
|
|
"description": "Monitor SSH authorized keys",
|
|
},
|
|
"startup_items": {
|
|
"query": "SELECT name, path, source, status, username FROM startup_items;",
|
|
"interval": 3600,
|
|
"description": "Monitor startup/login items",
|
|
},
|
|
"shell_history": {
|
|
"query": "SELECT uid, command, history_file FROM shell_history WHERE command LIKE '%curl%pipe%sh%' OR command LIKE '%wget%';",
|
|
"interval": 1800,
|
|
"description": "Detect suspicious shell history entries",
|
|
},
|
|
}
|
|
|
|
|
|
def generate_osquery_config(queries, log_dir="/var/log/osquery"):
|
|
"""Generate osquery.conf with security monitoring queries."""
|
|
config = {
|
|
"options": {
|
|
"logger_plugin": "filesystem",
|
|
"logger_path": log_dir,
|
|
"disable_logging": "false",
|
|
"schedule_splay_percent": "10",
|
|
"events_expiry": "3600",
|
|
"database_path": "/var/osquery/osquery.db",
|
|
"verbose": "false",
|
|
"worker_threads": "2",
|
|
"enable_monitor": "true",
|
|
},
|
|
"schedule": {},
|
|
"file_paths": {
|
|
"etc": ["/etc/%%"],
|
|
"binaries": ["/usr/bin/%%", "/usr/sbin/%%", "/bin/%%", "/sbin/%%"],
|
|
"tmp": ["/tmp/%%"],
|
|
},
|
|
}
|
|
for name, query_def in queries.items():
|
|
config["schedule"][name] = {
|
|
"query": query_def["query"],
|
|
"interval": query_def["interval"],
|
|
"description": query_def["description"],
|
|
}
|
|
logger.info("Generated osquery config with %d scheduled queries", len(queries))
|
|
return config
|
|
|
|
|
|
def parse_osquery_results(results_dir):
|
|
"""Parse osquery differential result logs from the results directory."""
|
|
all_results = []
|
|
for filename in sorted(os.listdir(results_dir)):
|
|
if not filename.endswith(".log"):
|
|
continue
|
|
filepath = os.path.join(results_dir, filename)
|
|
with open(filepath, "r") as f:
|
|
for line in f:
|
|
try:
|
|
entry = json.loads(line.strip())
|
|
all_results.append(entry)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
logger.info("Parsed %d result entries from %s", len(all_results), results_dir)
|
|
return all_results
|
|
|
|
|
|
def analyze_results(results):
|
|
"""Analyze osquery results for security findings."""
|
|
findings = []
|
|
for entry in results:
|
|
name = entry.get("name", "")
|
|
action = entry.get("action", "")
|
|
columns = entry.get("columns", {})
|
|
if name == "process_not_on_disk" and action == "added":
|
|
findings.append({
|
|
"type": "Process without binary",
|
|
"severity": "critical",
|
|
"details": columns,
|
|
"query": name,
|
|
})
|
|
elif name == "listening_ports" and action == "added":
|
|
port = int(columns.get("port", 0))
|
|
if port > 1024:
|
|
findings.append({
|
|
"type": "New listening port",
|
|
"severity": "high",
|
|
"details": columns,
|
|
"query": name,
|
|
})
|
|
elif name == "cron_persistence" and action == "added":
|
|
findings.append({
|
|
"type": "New cron job",
|
|
"severity": "high",
|
|
"details": columns,
|
|
"query": name,
|
|
})
|
|
elif name == "suid_binaries" and action == "added":
|
|
findings.append({
|
|
"type": "New SUID binary",
|
|
"severity": "critical",
|
|
"details": columns,
|
|
"query": name,
|
|
})
|
|
elif name == "authorized_keys" and action == "added":
|
|
findings.append({
|
|
"type": "New SSH authorized key",
|
|
"severity": "high",
|
|
"details": columns,
|
|
"query": name,
|
|
})
|
|
logger.info("Analysis: %d security findings from %d results", len(findings), len(results))
|
|
return findings
|
|
|
|
|
|
def generate_report(config, results, findings):
|
|
"""Generate osquery monitoring report."""
|
|
report = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"scheduled_queries": len(config.get("schedule", {})),
|
|
"total_results_parsed": len(results),
|
|
"security_findings": len(findings),
|
|
"critical_findings": len([f for f in findings if f["severity"] == "critical"]),
|
|
"findings": findings[:50],
|
|
}
|
|
print(f"OSQUERY REPORT: {len(findings)} findings ({report['critical_findings']} critical)")
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Osquery Endpoint Monitoring Agent")
|
|
parser.add_argument("--generate-config", help="Output path for osquery.conf")
|
|
parser.add_argument("--results-dir", help="Osquery results log directory")
|
|
parser.add_argument("--output", default="osquery_report.json")
|
|
args = parser.parse_args()
|
|
|
|
config = generate_osquery_config(SECURITY_QUERIES)
|
|
|
|
if args.generate_config:
|
|
with open(args.generate_config, "w") as f:
|
|
json.dump(config, f, indent=2)
|
|
logger.info("Config saved to %s", args.generate_config)
|
|
|
|
results = []
|
|
findings = []
|
|
if args.results_dir and os.path.isdir(args.results_dir):
|
|
results = parse_osquery_results(args.results_dir)
|
|
findings = analyze_results(results)
|
|
|
|
report = generate_report(config, results, findings)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
logger.info("Report saved to %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|