mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 13:44:56 +03:00
c47eed6a64
- 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
207 lines
8.4 KiB
Python
207 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Microsoft Entra ID passwordless authentication audit agent using MS Graph API."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
try:
|
|
import requests
|
|
from msal import ConfidentialClientApplication
|
|
except ImportError:
|
|
print("Install: pip install msal requests")
|
|
sys.exit(1)
|
|
|
|
|
|
GRAPH_URL = "https://graph.microsoft.com/v1.0"
|
|
GRAPH_BETA = "https://graph.microsoft.com/beta"
|
|
|
|
|
|
def get_access_token(tenant_id, client_id, client_secret):
|
|
"""Authenticate to Microsoft Graph using client credentials."""
|
|
app = ConfidentialClientApplication(
|
|
client_id, authority=f"https://login.microsoftonline.com/{tenant_id}",
|
|
client_credential=client_secret)
|
|
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
|
|
if "access_token" in result:
|
|
return result["access_token"]
|
|
print(f"[!] Auth failed: {result.get('error_description', 'Unknown error')}")
|
|
sys.exit(1)
|
|
|
|
|
|
def graph_get(token, endpoint, beta=False):
|
|
"""Make authenticated GET request to Microsoft Graph."""
|
|
base = GRAPH_BETA if beta else GRAPH_URL
|
|
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
|
resp = requests.get(f"{base}{endpoint}", headers=headers, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def get_auth_methods_policy(token):
|
|
"""Get authentication methods policy to check passwordless configuration."""
|
|
return graph_get(token, "/policies/authenticationMethodsPolicy", beta=True)
|
|
|
|
|
|
def get_fido2_policy(token):
|
|
"""Get FIDO2 security key configuration."""
|
|
policy = get_auth_methods_policy(token)
|
|
for method in policy.get("authenticationMethodConfigurations", []):
|
|
if method.get("id") == "fido2":
|
|
return {
|
|
"state": method.get("state"),
|
|
"is_attestation_enforced": method.get("isAttestationEnforced"),
|
|
"is_self_service_allowed": method.get("isSelfServiceRegistrationAllowed"),
|
|
"key_restrictions": method.get("keyRestrictions", {}),
|
|
}
|
|
return {"state": "not_configured"}
|
|
|
|
|
|
def get_microsoft_authenticator_policy(token):
|
|
"""Get Microsoft Authenticator configuration."""
|
|
policy = get_auth_methods_policy(token)
|
|
for method in policy.get("authenticationMethodConfigurations", []):
|
|
if method.get("id") == "microsoftAuthenticator":
|
|
return {
|
|
"state": method.get("state"),
|
|
"feature_settings": method.get("featureSettings", {}),
|
|
}
|
|
return {"state": "not_configured"}
|
|
|
|
|
|
def get_windows_hello_policy(token):
|
|
"""Get Windows Hello for Business configuration."""
|
|
policy = get_auth_methods_policy(token)
|
|
for method in policy.get("authenticationMethodConfigurations", []):
|
|
if method.get("id") == "windowsHelloForBusiness":
|
|
return {"state": method.get("state")}
|
|
return {"state": "not_configured"}
|
|
|
|
|
|
def list_user_auth_methods(token, user_id):
|
|
"""List authentication methods registered by a specific user."""
|
|
try:
|
|
methods = graph_get(token, f"/users/{user_id}/authentication/methods")
|
|
return [{"id": m.get("id"), "type": m.get("@odata.type", "").split(".")[-1]}
|
|
for m in methods.get("value", [])]
|
|
except Exception as e:
|
|
return [{"error": str(e)}]
|
|
|
|
|
|
def get_users_without_passwordless(token, max_users=100):
|
|
"""Identify users who have not registered any passwordless method."""
|
|
users = graph_get(token, f"/users?$top={max_users}&$select=id,displayName,userPrincipalName")
|
|
users_without = []
|
|
for user in users.get("value", []):
|
|
methods = list_user_auth_methods(token, user["id"])
|
|
passwordless_types = {"fido2AuthenticationMethod", "microsoftAuthenticatorAuthenticationMethod",
|
|
"windowsHelloForBusinessAuthenticationMethod"}
|
|
user_types = {m.get("type") for m in methods if not m.get("error")}
|
|
if not user_types.intersection(passwordless_types):
|
|
users_without.append({
|
|
"user": user["userPrincipalName"],
|
|
"name": user.get("displayName", ""),
|
|
"methods": [m.get("type") for m in methods if not m.get("error")],
|
|
})
|
|
return users_without
|
|
|
|
|
|
def get_sign_in_logs(token, days=7, passwordless_only=False):
|
|
"""Get recent sign-in logs to analyze authentication methods used."""
|
|
filter_str = ""
|
|
if passwordless_only:
|
|
filter_str = "?$filter=authenticationDetails/any(a:a/authenticationMethod eq 'FIDO2 security key')"
|
|
try:
|
|
logs = graph_get(token, f"/auditLogs/signIns{filter_str}", beta=True)
|
|
return [{"user": log.get("userPrincipalName"),
|
|
"app": log.get("appDisplayName"),
|
|
"status": log.get("status", {}).get("errorCode", 0),
|
|
"auth_detail": [d.get("authenticationMethod") for d in log.get("authenticationDetails", [])],
|
|
"time": log.get("createdDateTime")}
|
|
for log in logs.get("value", [])[:50]]
|
|
except Exception as e:
|
|
return [{"error": str(e)}]
|
|
|
|
|
|
def get_conditional_access_policies(token):
|
|
"""List conditional access policies related to authentication strength."""
|
|
try:
|
|
policies = graph_get(token, "/identity/conditionalAccess/policies", beta=True)
|
|
auth_strength_policies = []
|
|
for p in policies.get("value", []):
|
|
grant = p.get("grantControls", {})
|
|
if grant and "authenticationStrength" in json.dumps(grant):
|
|
auth_strength_policies.append({
|
|
"name": p.get("displayName"),
|
|
"state": p.get("state"),
|
|
"grant_controls": grant,
|
|
})
|
|
return auth_strength_policies
|
|
except Exception as e:
|
|
return [{"error": str(e)}]
|
|
|
|
|
|
def run_passwordless_audit(token):
|
|
"""Run comprehensive passwordless authentication audit."""
|
|
print(f"\n{'='*60}")
|
|
print(f" MICROSOFT ENTRA PASSWORDLESS AUTH AUDIT")
|
|
print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
fido2 = get_fido2_policy(token)
|
|
print(f"--- FIDO2 SECURITY KEYS ---")
|
|
print(f" State: {fido2.get('state', 'unknown')}")
|
|
print(f" Attestation Enforced: {fido2.get('is_attestation_enforced', 'N/A')}")
|
|
print(f" Self-Service Registration: {fido2.get('is_self_service_allowed', 'N/A')}")
|
|
|
|
authenticator = get_microsoft_authenticator_policy(token)
|
|
print(f"\n--- MICROSOFT AUTHENTICATOR ---")
|
|
print(f" State: {authenticator.get('state', 'unknown')}")
|
|
|
|
hello = get_windows_hello_policy(token)
|
|
print(f"\n--- WINDOWS HELLO FOR BUSINESS ---")
|
|
print(f" State: {hello.get('state', 'unknown')}")
|
|
|
|
ca_policies = get_conditional_access_policies(token)
|
|
print(f"\n--- CONDITIONAL ACCESS (Auth Strength) ({len(ca_policies)}) ---")
|
|
for p in ca_policies:
|
|
print(f" {p.get('name', 'N/A')}: {p.get('state', 'N/A')}")
|
|
|
|
users_without = get_users_without_passwordless(token, max_users=50)
|
|
print(f"\n--- USERS WITHOUT PASSWORDLESS ({len(users_without)}) ---")
|
|
for u in users_without[:10]:
|
|
print(f" {u['user']} - Current methods: {', '.join(u['methods']) or 'none'}")
|
|
|
|
print(f"\n{'='*60}\n")
|
|
return {"fido2": fido2, "authenticator": authenticator, "hello": hello,
|
|
"users_without_passwordless": len(users_without)}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Microsoft Entra Passwordless Auth Agent")
|
|
parser.add_argument("--tenant-id", required=True, help="Azure AD tenant ID")
|
|
parser.add_argument("--client-id", required=True, help="App registration client ID")
|
|
parser.add_argument("--client-secret", required=True, help="App registration secret")
|
|
parser.add_argument("--audit", action="store_true", help="Run passwordless audit")
|
|
parser.add_argument("--user-methods", help="List auth methods for specific user")
|
|
parser.add_argument("--output", help="Save report to JSON")
|
|
args = parser.parse_args()
|
|
|
|
token = get_access_token(args.tenant_id, args.client_id, args.client_secret)
|
|
|
|
if args.audit:
|
|
report = run_passwordless_audit(token)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
elif args.user_methods:
|
|
methods = list_user_auth_methods(token, args.user_methods)
|
|
print(json.dumps(methods, indent=2))
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|