mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-01 08:47:41 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
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
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,54 @@
|
||||
# API Reference: Securing AWS IAM Permissions
|
||||
|
||||
## boto3 IAM Client
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
pip install boto3
|
||||
```
|
||||
|
||||
### Key Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `list_users()` | List all IAM users in the account |
|
||||
| `list_roles()` | List all IAM roles |
|
||||
| `list_access_keys()` | List access keys for a user |
|
||||
| `get_access_key_last_used()` | Get last usage info for an access key |
|
||||
| `list_attached_role_policies()` | List managed policies attached to a role |
|
||||
| `list_role_policies()` | List inline policy names for a role |
|
||||
| `get_role_policy()` | Get inline policy document for a role |
|
||||
| `list_mfa_devices()` | List MFA devices for a user |
|
||||
| `get_login_profile()` | Check if user has console access |
|
||||
| `generate_credential_report()` | Trigger credential report generation |
|
||||
| `get_credential_report()` | Download the credential report (CSV, base64) |
|
||||
| `simulate_principal_policy()` | Test effective permissions for a principal |
|
||||
| `update_access_key()` | Activate or deactivate an access key |
|
||||
| `put_role_permissions_boundary()` | Apply a permission boundary to a role |
|
||||
|
||||
## boto3 Access Analyzer Client
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `create_analyzer()` | Create an IAM Access Analyzer (type: ACCOUNT or ORGANIZATION) |
|
||||
| `list_analyzers()` | List existing analyzers |
|
||||
| `list_findings()` | Get active findings for external access |
|
||||
| `start_policy_generation()` | Generate least-privilege policy from CloudTrail |
|
||||
| `get_generated_policy()` | Retrieve a generated policy by job ID |
|
||||
| `validate_policy()` | Validate a policy against IAM best practices |
|
||||
|
||||
### Credential Report CSV Fields
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `user` | IAM username |
|
||||
| `arn` | User ARN |
|
||||
| `password_enabled` | Whether console password is set |
|
||||
| `mfa_active` | Whether MFA is enabled |
|
||||
| `access_key_1_active` | Whether first access key is active |
|
||||
| `access_key_1_last_used_date` | Last usage timestamp |
|
||||
| `access_key_1_last_rotated` | Last rotation timestamp |
|
||||
|
||||
## References
|
||||
- boto3 IAM docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html
|
||||
- IAM Access Analyzer: https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html
|
||||
- IAM Best Practices: https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for auditing and hardening AWS IAM permissions using least-privilege principles."""
|
||||
|
||||
import boto3
|
||||
import json
|
||||
import csv
|
||||
import sys
|
||||
import argparse
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from base64 import b64decode
|
||||
|
||||
|
||||
def get_credential_report():
|
||||
"""Generate and parse the IAM credential report."""
|
||||
iam = boto3.client("iam")
|
||||
iam.generate_credential_report()
|
||||
import time
|
||||
for _ in range(10):
|
||||
try:
|
||||
response = iam.get_credential_report()
|
||||
content = b64decode(response["Content"]).decode("utf-8")
|
||||
lines = content.strip().split("\n")
|
||||
reader = csv.DictReader(lines)
|
||||
report = list(reader)
|
||||
print(f"[*] Credential report: {len(report)} users")
|
||||
return report
|
||||
except iam.exceptions.CredentialReportNotReadyException:
|
||||
time.sleep(2)
|
||||
return []
|
||||
|
||||
|
||||
def find_stale_access_keys(max_age_days=90):
|
||||
"""Find access keys older than the specified threshold."""
|
||||
iam = boto3.client("iam")
|
||||
cutoff = datetime.now(timezone.utc) - timedelta(days=max_age_days)
|
||||
stale_keys = []
|
||||
users = iam.list_users()["Users"]
|
||||
for user in users:
|
||||
keys = iam.list_access_keys(UserName=user["UserName"])["AccessKeyMetadata"]
|
||||
for key in keys:
|
||||
if key["CreateDate"] < cutoff and key["Status"] == "Active":
|
||||
last_used = iam.get_access_key_last_used(AccessKeyId=key["AccessKeyId"])
|
||||
last_used_date = last_used["AccessKeyLastUsed"].get("LastUsedDate", "Never")
|
||||
stale_keys.append({
|
||||
"user": user["UserName"], "key_id": key["AccessKeyId"],
|
||||
"created": key["CreateDate"].isoformat(), "last_used": str(last_used_date),
|
||||
})
|
||||
print(f" [!] Stale key: {user['UserName']} - {key['AccessKeyId']} "
|
||||
f"(created: {key['CreateDate'].strftime('%Y-%m-%d')})")
|
||||
print(f"[*] Found {len(stale_keys)} stale access keys (>{max_age_days} days)")
|
||||
return stale_keys
|
||||
|
||||
|
||||
def find_overprivileged_roles():
|
||||
"""Identify roles with AdministratorAccess or wildcard policies."""
|
||||
iam = boto3.client("iam")
|
||||
findings = []
|
||||
roles = iam.list_roles()["Roles"]
|
||||
for role in roles:
|
||||
if role["Path"].startswith("/aws-service-role/"):
|
||||
continue
|
||||
attached = iam.list_attached_role_policies(RoleName=role["RoleName"])["AttachedPolicies"]
|
||||
for policy in attached:
|
||||
if policy["PolicyName"] in ("AdministratorAccess", "PowerUserAccess"):
|
||||
findings.append({
|
||||
"role": role["RoleName"], "policy": policy["PolicyName"],
|
||||
"severity": "CRITICAL" if policy["PolicyName"] == "AdministratorAccess" else "HIGH",
|
||||
})
|
||||
print(f" [!] {role['RoleName']} has {policy['PolicyName']}")
|
||||
inline_policies = iam.list_role_policies(RoleName=role["RoleName"])["PolicyNames"]
|
||||
for pol_name in inline_policies:
|
||||
pol_doc = iam.get_role_policy(RoleName=role["RoleName"], PolicyName=pol_name)["PolicyDocument"]
|
||||
for stmt in pol_doc.get("Statement", []):
|
||||
actions = stmt.get("Action", [])
|
||||
resources = stmt.get("Resource", [])
|
||||
if isinstance(actions, str):
|
||||
actions = [actions]
|
||||
if isinstance(resources, str):
|
||||
resources = [resources]
|
||||
if "*" in actions and "*" in resources and stmt.get("Effect") == "Allow":
|
||||
findings.append({
|
||||
"role": role["RoleName"], "policy": f"inline:{pol_name}",
|
||||
"severity": "CRITICAL",
|
||||
})
|
||||
print(f" [!] {role['RoleName']} inline policy '{pol_name}' has *:* Allow")
|
||||
print(f"[*] Found {len(findings)} overprivileged roles")
|
||||
return findings
|
||||
|
||||
|
||||
def check_mfa_status():
|
||||
"""Check which IAM users have MFA enabled."""
|
||||
iam = boto3.client("iam")
|
||||
users_without_mfa = []
|
||||
users = iam.list_users()["Users"]
|
||||
for user in users:
|
||||
mfa_devices = iam.list_mfa_devices(UserName=user["UserName"])["MFADevices"]
|
||||
if not mfa_devices:
|
||||
login_profile = None
|
||||
try:
|
||||
iam.get_login_profile(UserName=user["UserName"])
|
||||
login_profile = True
|
||||
except iam.exceptions.NoSuchEntityException:
|
||||
login_profile = False
|
||||
if login_profile:
|
||||
users_without_mfa.append(user["UserName"])
|
||||
print(f" [!] {user['UserName']} has console access but NO MFA")
|
||||
print(f"[*] {len(users_without_mfa)} console users without MFA")
|
||||
return users_without_mfa
|
||||
|
||||
|
||||
def check_access_analyzer(region="us-east-1"):
|
||||
"""Check IAM Access Analyzer findings for external access."""
|
||||
aa = boto3.client("accessanalyzer", region_name=region)
|
||||
analyzers = aa.list_analyzers(Type="ACCOUNT")["analyzers"]
|
||||
if not analyzers:
|
||||
print(" [-] No Access Analyzer configured. Creating one...")
|
||||
aa.create_analyzer(analyzerName="account-analyzer", type="ACCOUNT")
|
||||
return []
|
||||
analyzer_arn = analyzers[0]["arn"]
|
||||
findings = aa.list_findings(analyzerArn=analyzer_arn,
|
||||
filter={"status": {"eq": ["ACTIVE"]}})["findings"]
|
||||
print(f"[*] Access Analyzer active findings: {len(findings)}")
|
||||
for f in findings[:10]:
|
||||
print(f" [!] {f['resourceType']}: {f['resource']} - {f.get('condition', {})}")
|
||||
return findings
|
||||
|
||||
|
||||
def generate_audit_report(stale_keys, overpriv_roles, no_mfa, aa_findings, output_path):
|
||||
"""Generate a consolidated IAM audit report."""
|
||||
report = {
|
||||
"audit_date": datetime.now().isoformat(),
|
||||
"summary": {
|
||||
"stale_access_keys": len(stale_keys),
|
||||
"overprivileged_roles": len(overpriv_roles),
|
||||
"users_without_mfa": len(no_mfa),
|
||||
"access_analyzer_findings": len(aa_findings),
|
||||
},
|
||||
"stale_access_keys": stale_keys,
|
||||
"overprivileged_roles": overpriv_roles,
|
||||
"users_without_mfa": no_mfa,
|
||||
}
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"\n[*] Audit report saved to {output_path}")
|
||||
total = len(stale_keys) + len(overpriv_roles) + len(no_mfa) + len(aa_findings)
|
||||
print(f"[*] Total findings: {total}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="AWS IAM Security Audit Agent")
|
||||
parser.add_argument("action", choices=["full-audit", "stale-keys", "overpriv-roles", "mfa-check",
|
||||
"access-analyzer", "credential-report"])
|
||||
parser.add_argument("--max-key-age", type=int, default=90, help="Max access key age in days")
|
||||
parser.add_argument("--region", default="us-east-1")
|
||||
parser.add_argument("-o", "--output", default="iam_audit_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.action == "credential-report":
|
||||
get_credential_report()
|
||||
elif args.action == "stale-keys":
|
||||
find_stale_access_keys(args.max_key_age)
|
||||
elif args.action == "overpriv-roles":
|
||||
find_overprivileged_roles()
|
||||
elif args.action == "mfa-check":
|
||||
check_mfa_status()
|
||||
elif args.action == "access-analyzer":
|
||||
check_access_analyzer(args.region)
|
||||
elif args.action == "full-audit":
|
||||
print("[*] Running full IAM security audit...")
|
||||
stale = find_stale_access_keys(args.max_key_age)
|
||||
overpriv = find_overprivileged_roles()
|
||||
no_mfa = check_mfa_status()
|
||||
aa = check_access_analyzer(args.region)
|
||||
generate_audit_report(stale, overpriv, no_mfa, aa, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user