mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 11:44:37 +03:00
312 lines
12 KiB
Python
312 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Privileged Account Access Review Automation
|
|
|
|
Discovers privileged accounts from Active Directory, AWS IAM, and Azure AD,
|
|
generates review campaigns, and tracks certification decisions.
|
|
|
|
Requirements:
|
|
pip install ldap3 boto3 msal requests pandas openpyxl
|
|
"""
|
|
|
|
import json
|
|
import csv
|
|
import sys
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import ldap3
|
|
HAS_LDAP = True
|
|
except ImportError:
|
|
HAS_LDAP = False
|
|
|
|
try:
|
|
import boto3
|
|
HAS_BOTO = True
|
|
except ImportError:
|
|
HAS_BOTO = False
|
|
|
|
|
|
class PrivilegedAccountDiscovery:
|
|
"""Discovers privileged accounts across multiple platforms."""
|
|
|
|
def __init__(self):
|
|
self.accounts = []
|
|
|
|
def discover_ad_privileged_accounts(self, server_address, domain_dn,
|
|
bind_user, bind_password):
|
|
"""Enumerate privileged accounts from Active Directory."""
|
|
if not HAS_LDAP:
|
|
print("[WARN] ldap3 not installed, skipping AD discovery")
|
|
return []
|
|
|
|
server = ldap3.Server(server_address, use_ssl=True, get_info=ldap3.ALL)
|
|
conn = ldap3.Connection(server, user=bind_user, password=bind_password,
|
|
auto_bind=True)
|
|
|
|
privileged_groups = [
|
|
"Domain Admins",
|
|
"Enterprise Admins",
|
|
"Schema Admins",
|
|
"Administrators",
|
|
"Account Operators",
|
|
"Backup Operators",
|
|
"Server Operators",
|
|
"Print Operators",
|
|
]
|
|
|
|
discovered = []
|
|
|
|
for group_name in privileged_groups:
|
|
search_filter = f"(&(objectClass=group)(cn={group_name}))"
|
|
conn.search(domain_dn, search_filter,
|
|
attributes=["member", "distinguishedName"])
|
|
|
|
for entry in conn.entries:
|
|
members = entry.member.values if hasattr(entry.member, "values") else []
|
|
for member_dn in members:
|
|
conn.search(member_dn, "(objectClass=user)",
|
|
attributes=["sAMAccountName", "displayName",
|
|
"mail", "lastLogonTimestamp",
|
|
"whenCreated", "userAccountControl",
|
|
"adminCount", "servicePrincipalName"])
|
|
for user_entry in conn.entries:
|
|
uac = int(str(user_entry.userAccountControl)) if user_entry.userAccountControl else 0
|
|
is_disabled = bool(uac & 0x0002)
|
|
is_service = bool(user_entry.servicePrincipalName)
|
|
|
|
account = {
|
|
"platform": "Active Directory",
|
|
"username": str(user_entry.sAMAccountName),
|
|
"display_name": str(user_entry.displayName) if user_entry.displayName else "",
|
|
"email": str(user_entry.mail) if user_entry.mail else "",
|
|
"privileged_group": group_name,
|
|
"account_type": "service" if is_service else "user",
|
|
"is_disabled": is_disabled,
|
|
"created_date": str(user_entry.whenCreated) if user_entry.whenCreated else "",
|
|
"last_logon": str(user_entry.lastLogonTimestamp) if user_entry.lastLogonTimestamp else "Never",
|
|
"admin_count": str(user_entry.adminCount) if user_entry.adminCount else "0",
|
|
"risk_level": "Critical" if group_name in ["Domain Admins", "Enterprise Admins"] else "High",
|
|
}
|
|
discovered.append(account)
|
|
|
|
conn.unbind()
|
|
self.accounts.extend(discovered)
|
|
return discovered
|
|
|
|
def discover_aws_privileged_accounts(self, profile_name=None):
|
|
"""Enumerate privileged IAM users and roles in AWS."""
|
|
if not HAS_BOTO:
|
|
print("[WARN] boto3 not installed, skipping AWS discovery")
|
|
return []
|
|
|
|
session = boto3.Session(profile_name=profile_name)
|
|
iam = session.client("iam")
|
|
discovered = []
|
|
|
|
admin_policies = [
|
|
"arn:aws:iam::aws:policy/AdministratorAccess",
|
|
"arn:aws:iam::aws:policy/IAMFullAccess",
|
|
"arn:aws:iam::aws:policy/PowerUserAccess",
|
|
]
|
|
|
|
paginator = iam.get_paginator("list_users")
|
|
for page in paginator.paginate():
|
|
for user in page["Users"]:
|
|
username = user["UserName"]
|
|
create_date = user["CreateDate"]
|
|
|
|
attached_policies = iam.list_attached_user_policies(UserName=username)
|
|
user_policies = [p["PolicyArn"] for p in attached_policies["AttachedPolicies"]]
|
|
|
|
is_admin = any(p in admin_policies for p in user_policies)
|
|
|
|
user_groups = iam.list_groups_for_user(UserName=username)
|
|
for group in user_groups["Groups"]:
|
|
group_policies = iam.list_attached_group_policies(GroupName=group["GroupName"])
|
|
for gp in group_policies["AttachedPolicies"]:
|
|
if gp["PolicyArn"] in admin_policies:
|
|
is_admin = True
|
|
|
|
if is_admin:
|
|
try:
|
|
last_used = iam.get_user(UserName=username)
|
|
password_last_used = user.get("PasswordLastUsed", "Never")
|
|
except Exception:
|
|
password_last_used = "Unknown"
|
|
|
|
mfa_devices = iam.list_mfa_devices(UserName=username)
|
|
has_mfa = len(mfa_devices["MFADevices"]) > 0
|
|
|
|
access_keys = iam.list_access_keys(UserName=username)
|
|
active_keys = [k for k in access_keys["AccessKeyMetadata"]
|
|
if k["Status"] == "Active"]
|
|
|
|
account = {
|
|
"platform": "AWS IAM",
|
|
"username": username,
|
|
"display_name": username,
|
|
"email": "",
|
|
"privileged_group": ", ".join(user_policies),
|
|
"account_type": "user",
|
|
"is_disabled": False,
|
|
"created_date": create_date.isoformat(),
|
|
"last_logon": str(password_last_used),
|
|
"admin_count": str(len(active_keys)),
|
|
"risk_level": "Critical",
|
|
"mfa_enabled": has_mfa,
|
|
"active_access_keys": len(active_keys),
|
|
}
|
|
discovered.append(account)
|
|
|
|
self.accounts.extend(discovered)
|
|
return discovered
|
|
|
|
def generate_review_report(self, output_path):
|
|
"""Generate CSV report of all discovered privileged accounts for review."""
|
|
if not self.accounts:
|
|
print("[INFO] No accounts discovered. Run discovery methods first.")
|
|
return
|
|
|
|
fieldnames = [
|
|
"platform", "username", "display_name", "email",
|
|
"privileged_group", "account_type", "is_disabled",
|
|
"created_date", "last_logon", "risk_level",
|
|
"reviewer", "decision", "justification", "review_date"
|
|
]
|
|
|
|
with open(output_path, "w", newline="", encoding="utf-8") as f:
|
|
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
|
|
writer.writeheader()
|
|
for account in self.accounts:
|
|
account.setdefault("reviewer", "")
|
|
account.setdefault("decision", "")
|
|
account.setdefault("justification", "")
|
|
account.setdefault("review_date", "")
|
|
writer.writerow(account)
|
|
|
|
print(f"[OK] Review report generated: {output_path}")
|
|
print(f"[OK] Total privileged accounts: {len(self.accounts)}")
|
|
|
|
critical = sum(1 for a in self.accounts if a["risk_level"] == "Critical")
|
|
high = sum(1 for a in self.accounts if a["risk_level"] == "High")
|
|
disabled = sum(1 for a in self.accounts if a.get("is_disabled"))
|
|
|
|
print(f" Critical: {critical} | High: {high} | Disabled: {disabled}")
|
|
|
|
|
|
class AccessReviewTracker:
|
|
"""Tracks review campaign progress and generates compliance reports."""
|
|
|
|
def __init__(self, review_file):
|
|
self.review_file = Path(review_file)
|
|
self.accounts = []
|
|
if self.review_file.exists():
|
|
self._load_reviews()
|
|
|
|
def _load_reviews(self):
|
|
with open(self.review_file, "r", encoding="utf-8") as f:
|
|
reader = csv.DictReader(f)
|
|
self.accounts = list(reader)
|
|
|
|
def get_review_status(self):
|
|
"""Calculate review campaign metrics."""
|
|
total = len(self.accounts)
|
|
reviewed = sum(1 for a in self.accounts if a.get("decision"))
|
|
approved = sum(1 for a in self.accounts if a.get("decision") == "Approve")
|
|
revoked = sum(1 for a in self.accounts if a.get("decision") == "Revoke")
|
|
flagged = sum(1 for a in self.accounts if a.get("decision") == "Flag")
|
|
pending = total - reviewed
|
|
|
|
return {
|
|
"total_accounts": total,
|
|
"reviewed": reviewed,
|
|
"pending": pending,
|
|
"approved": approved,
|
|
"revoked": revoked,
|
|
"flagged": flagged,
|
|
"completion_rate": f"{(reviewed/total*100):.1f}%" if total else "0%",
|
|
}
|
|
|
|
def identify_dormant_accounts(self, days_threshold=90):
|
|
"""Find accounts not used within the threshold period."""
|
|
cutoff = datetime.now(timezone.utc) - timedelta(days=days_threshold)
|
|
dormant = []
|
|
|
|
for account in self.accounts:
|
|
last_logon = account.get("last_logon", "")
|
|
if last_logon in ("Never", "", "Unknown"):
|
|
dormant.append(account)
|
|
continue
|
|
try:
|
|
logon_date = datetime.fromisoformat(last_logon.replace("Z", "+00:00"))
|
|
if logon_date < cutoff:
|
|
dormant.append(account)
|
|
except (ValueError, TypeError):
|
|
continue
|
|
|
|
return dormant
|
|
|
|
def generate_compliance_report(self, output_path):
|
|
"""Generate compliance-ready review summary."""
|
|
status = self.get_review_status()
|
|
dormant = self.identify_dormant_accounts()
|
|
|
|
report = {
|
|
"report_title": "Privileged Account Access Review - Compliance Report",
|
|
"generated_date": datetime.now(timezone.utc).isoformat(),
|
|
"review_period": "Quarterly",
|
|
"metrics": status,
|
|
"dormant_accounts": len(dormant),
|
|
"dormant_account_list": [
|
|
{"username": a["username"], "platform": a["platform"],
|
|
"last_logon": a.get("last_logon", "Unknown")}
|
|
for a in dormant
|
|
],
|
|
"findings": [],
|
|
}
|
|
|
|
if status["pending"] > 0:
|
|
report["findings"].append({
|
|
"finding": f"{status['pending']} accounts have not been reviewed",
|
|
"severity": "High",
|
|
"recommendation": "Complete reviews within SLA or auto-revoke"
|
|
})
|
|
|
|
if dormant:
|
|
report["findings"].append({
|
|
"finding": f"{len(dormant)} dormant privileged accounts detected",
|
|
"severity": "Critical",
|
|
"recommendation": "Disable dormant accounts and rotate credentials"
|
|
})
|
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
json.dump(report, f, indent=2)
|
|
|
|
print(f"[OK] Compliance report generated: {output_path}")
|
|
return report
|
|
|
|
|
|
if __name__ == "__main__":
|
|
discovery = PrivilegedAccountDiscovery()
|
|
|
|
print("=" * 60)
|
|
print("Privileged Account Access Review Tool")
|
|
print("=" * 60)
|
|
print()
|
|
print("Usage:")
|
|
print(" 1. Run discovery against your environment")
|
|
print(" 2. Generate review CSV for reviewer certification")
|
|
print(" 3. Track review progress and generate compliance report")
|
|
print()
|
|
print("Example:")
|
|
print(" discovery = PrivilegedAccountDiscovery()")
|
|
print(" discovery.discover_ad_privileged_accounts(server, dn, user, pw)")
|
|
print(" discovery.discover_aws_privileged_accounts(profile='prod')")
|
|
print(" discovery.generate_review_report('review_campaign.csv')")
|
|
print()
|
|
print(" tracker = AccessReviewTracker('review_campaign.csv')")
|
|
print(" print(tracker.get_review_status())")
|
|
print(" tracker.generate_compliance_report('compliance_report.json')")
|