From 34ac9d6b9b0d5b8b7ad24b8a391ff158b208ac9e Mon Sep 17 00:00:00 2001 From: mukul975 Date: Tue, 17 Mar 2026 12:26:21 +0100 Subject: [PATCH 1/4] Trigger contributor recalculation From 9fce1caaf76d61c2a51097cbb316d3e95c4cc138 Mon Sep 17 00:00:00 2001 From: MAGI Date: Tue, 17 Mar 2026 10:38:15 -0600 Subject: [PATCH 2/4] Add skill: performing-cloud-native-threat-hunting-with-aws-detective (fixes #6) --- .../SKILL.md | 155 ++++++++++++++++++ .../assets/template.md | 34 ++++ .../references/standards.md | 19 +++ .../references/workflows.md | 30 ++++ .../scripts/process.py | 0 5 files changed, 238 insertions(+) create mode 100644 skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md create mode 100644 skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md create mode 100644 skills/performing-cloud-native-threat-hunting-with-aws-detective/references/standards.md create mode 100644 skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md create mode 100644 skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md new file mode 100644 index 00000000..83bcd2cd --- /dev/null +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md @@ -0,0 +1,155 @@ +--- +name: performing-cloud-native-threat-hunting-with-aws-detective +description: Hunt for threats in AWS environments using Detective behavior graphs, entity investigation timelines, GuardDuty finding correlation, and automated entity profiling across IAM users, EC2 instances, and IP addresses. +domain: cybersecurity +subdomain: cloud-security +tags: [aws-detective, threat-hunting, cloud-security, guardduty, behavior-graph, aws, iam, ec2, incident-investigation] +version: "1.0" +author: juliosuas +license: Apache-2.0 +--- + +# Performing Cloud-Native Threat Hunting with AWS Detective + +## Overview + +AWS Detective automatically collects and analyzes log data from AWS CloudTrail, VPC Flow Logs, GuardDuty findings, and EKS audit logs to build interactive behavior graphs. These graphs enable security analysts to investigate entities (IAM users, roles, IP addresses, EC2 instances) across time, identify anomalous API calls, detect lateral movement between accounts, and correlate GuardDuty findings into coherent attack narratives — all without manual log parsing. + +## Prerequisites + +- AWS account with Detective enabled (requires GuardDuty active for 48+ hours) +- AWS CLI v2 configured with appropriate IAM permissions (`detective:*`, `guardduty:List*`) +- Python 3.9+ with boto3 +- IAM policy: `AmazonDetectiveFullAccess` or custom policy with `detective:SearchGraph`, `detective:GetInvestigation`, `detective:ListIndicators` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| **Behavior Graph** | Data structure linking CloudTrail, VPC Flow, GuardDuty, and EKS logs for an account/region | +| **Entity** | Investigable object: IAM user, IAM role, EC2 instance, IP address, S3 bucket, EKS cluster | +| **Finding Group** | Correlated set of GuardDuty findings linked to the same attack campaign | +| **Entity Profile** | Timeline of API calls, network connections, and resource access for a specific entity | +| **Scope Time** | Investigation window (default 24h, max 1 year) for behavioral analysis | + +## Steps + +### Step 1: List Available Behavior Graphs + +```bash +aws detective list-graphs --output table +``` + +### Step 2: Investigate a Suspicious IAM User + +```bash +# Get entity profile for an IAM user +aws detective get-investigation \ + --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ + --investigation-id inv-1234567890 +``` + +### Step 3: Search Entities Programmatically + +```python +#!/usr/bin/env python3 +"""Search AWS Detective for suspicious entities.""" +import boto3 +import json +from datetime import datetime, timedelta + +detective = boto3.client('detective') + +def list_behavior_graphs(): + """List all Detective behavior graphs.""" + response = detective.list_graphs() + return response.get('GraphList', []) + +def get_entity_history(graph_arn, entity_arn, hours=24): + """Get API call history for an entity.""" + end_time = datetime.utcnow() + start_time = end_time - timedelta(hours=hours) + + response = detective.list_indicators( + GraphArn=graph_arn, + InvestigationId=entity_arn, + MaxResults=50 + ) + return response.get('Indicators', []) + +def investigate_guardduty_findings(graph_arn): + """List finding groups correlated by Detective.""" + response = detective.list_investigations( + GraphArn=graph_arn, + FilterCriteria={ + 'Statuses': ['RUNNING', 'FAILED'], + 'Severities': ['HIGH', 'CRITICAL'] + }, + MaxResults=20 + ) + + for investigation in response.get('InvestigationDetails', []): + print(f"Investigation: {investigation['InvestigationId']}") + print(f" Entity: {investigation['EntityArn']}") + print(f" Status: {investigation['Status']}") + print(f" Severity: {investigation['Severity']}") + print(f" Created: {investigation['CreatedTime']}") + print() + +if __name__ == "__main__": + graphs = list_behavior_graphs() + for graph in graphs: + print(f"Graph: {graph['Arn']}") + investigate_guardduty_findings(graph['Arn']) +``` + +### Step 4: Analyze Finding Groups for Attack Campaigns + +```bash +# List investigations with high severity +aws detective list-investigations \ + --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ + --filter-criteria '{"Severities":["HIGH","CRITICAL"]}' \ + --max-results 10 +``` + +### Step 5: Check Entity Indicators + +```bash +# Get indicators for a specific investigation +aws detective list-indicators \ + --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ + --investigation-id inv-1234567890 \ + --max-results 50 +``` + +## Expected Output + +```json +{ + "InvestigationDetails": [ + { + "InvestigationId": "inv-0a1b2c3d4e5f", + "Severity": "CRITICAL", + "Status": "RUNNING", + "EntityArn": "arn:aws:iam::123456789012:user/suspicious-user", + "EntityType": "IAM_USER", + "CreatedTime": "2026-03-15T14:30:00Z", + "Indicators": { + "ImpossibleTravel": true, + "NewGeoLocation": "ru-central-1", + "UnusualApiCalls": ["CreateAccessKey", "AttachUserPolicy", "PutBucketPolicy"], + "RelatedFindings": 7 + } + } + ] +} +``` + +## Verification + +1. Confirm behavior graph has data: `aws detective list-graphs` returns non-empty list +2. Validate investigation results contain entity timelines with API call sequences +3. Cross-reference Detective findings with raw CloudTrail logs for accuracy +4. Verify finding group correlations match manual investigation conclusions +5. Confirm automated alerts trigger for HIGH/CRITICAL severity investigations diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md new file mode 100644 index 00000000..5924e83c --- /dev/null +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md @@ -0,0 +1,34 @@ +# AWS Detective Investigation Checklist + +## Pre-Investigation +- [ ] Confirm Detective is enabled and receiving data +- [ ] Identify trigger (GuardDuty finding, alert, manual hunt) +- [ ] Define scope time window +- [ ] Document initial IOCs + +## Entity Investigation +- [ ] IAM User/Role profile reviewed +- [ ] API call timeline analyzed +- [ ] Geographic anomalies checked (impossible travel) +- [ ] New API calls identified (never seen before) +- [ ] Privilege escalation attempts documented +- [ ] AssumeRole chain traced + +## Network Analysis +- [ ] VPC Flow Logs reviewed for entity +- [ ] Outbound connections to suspicious IPs identified +- [ ] Data transfer volumes assessed +- [ ] DNS query patterns checked + +## Finding Correlation +- [ ] All related GuardDuty findings grouped +- [ ] MITRE ATT&CK techniques mapped +- [ ] Attack timeline constructed +- [ ] Initial access vector identified + +## Response Actions +- [ ] Compromised credentials disabled +- [ ] Active sessions revoked +- [ ] Affected resources isolated +- [ ] Evidence preserved +- [ ] Stakeholders notified diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/standards.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/standards.md new file mode 100644 index 00000000..79fdedbf --- /dev/null +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/standards.md @@ -0,0 +1,19 @@ +# Standards & References + +## MITRE ATT&CK Cloud Matrix +- **TA0001** Initial Access: T1078 (Valid Accounts), T1190 (Exploit Public-Facing Application) +- **TA0003** Persistence: T1098 (Account Manipulation), T1136 (Create Account) +- **TA0004** Privilege Escalation: T1078, T1484 (Domain Policy Modification) +- **TA0005** Defense Evasion: T1562 (Impair Defenses), T1070 (Indicator Removal) +- **TA0006** Credential Access: T1528 (Steal Application Access Token) +- **TA0007** Discovery: T1580 (Cloud Infrastructure Discovery), T1526 (Cloud Service Discovery) +- **TA0009** Collection: T1530 (Data from Cloud Storage) +- **TA0010** Exfiltration: T1537 (Transfer Data to Cloud Account) + +## AWS Documentation +- [AWS Detective User Guide](https://docs.aws.amazon.com/detective/latest/userguide/) +- [AWS Detective API Reference](https://docs.aws.amazon.com/detective/latest/APIReference/) +- [GuardDuty Finding Types](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) + +## CIS AWS Foundations Benchmark +- Section 4: Monitoring (relevant to Detective integration) diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md new file mode 100644 index 00000000..688b70f1 --- /dev/null +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md @@ -0,0 +1,30 @@ +# AWS Detective Investigation Workflow + +## Phase 1: Triage +1. Review GuardDuty HIGH/CRITICAL findings +2. Open Detective console → Finding Groups +3. Identify clustered findings pointing to same entity + +## Phase 2: Entity Investigation +1. Select entity (IAM user/role, EC2, IP) +2. Review 24h behavior timeline +3. Identify unusual API calls, new geolocations, impossible travel +4. Check for privilege escalation patterns (CreateAccessKey, AttachPolicy) + +## Phase 3: Scope Assessment +1. Trace lateral movement via AssumeRole chains +2. Check S3 data access patterns +3. Review VPC Flow Logs for unusual outbound connections +4. Identify all compromised credentials + +## Phase 4: Correlation +1. Map findings to MITRE ATT&CK techniques +2. Build attack timeline from entity profiles +3. Identify initial access vector +4. Document indicators of compromise (IOCs) + +## Phase 5: Response +1. Disable compromised credentials +2. Revoke active sessions +3. Isolate affected resources +4. Preserve evidence (CloudTrail logs, flow logs) diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py b/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py new file mode 100644 index 00000000..e69de29b From 610338bc0b31e184fa81e1dd68f5e95ed77d6428 Mon Sep 17 00:00:00 2001 From: MAGI Date: Wed, 18 Mar 2026 10:38:13 -0600 Subject: [PATCH 3/4] fix: add missing process.py implementation for aws-detective skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The process.py script was empty (0 bytes). Added a functional implementation that lists behavior graphs, retrieves investigations, queries indicators, and exports results — matching the pattern of other skills in the repository. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../scripts/process.py | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py b/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py index e69de29b..d9bc1658 100644 --- a/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +AWS Detective Threat Hunting Script + +Lists behavior graphs, retrieves investigations, and analyzes entity +indicators for cloud-native threat hunting. +""" + +import boto3 +import json +import sys +import os +from datetime import datetime, timedelta + + +def list_behavior_graphs(session): + """List all Detective behavior graphs in the account.""" + client = session.client('detective') + response = client.list_graphs() + graphs = response.get('GraphList', []) + + if not graphs: + print("[!] No behavior graphs found. Enable Detective first.") + return [] + + print(f"[+] Found {len(graphs)} behavior graph(s)\n") + for graph in graphs: + print(f" ARN: {graph['Arn']}") + created = graph.get('CreatedTime', 'N/A') + print(f" Created: {created}") + print() + + return graphs + + +def list_investigations(session, graph_arn, severities=None, max_results=20): + """List investigations filtered by severity.""" + client = session.client('detective') + + filter_criteria = {} + if severities: + filter_criteria['Severities'] = severities + + kwargs = { + 'GraphArn': graph_arn, + 'MaxResults': max_results, + } + if filter_criteria: + kwargs['FilterCriteria'] = filter_criteria + + response = client.list_investigations(**kwargs) + investigations = response.get('InvestigationDetails', []) + + if not investigations: + print("[+] No investigations found matching criteria") + return [] + + print(f"[+] Found {len(investigations)} investigation(s)\n") + for inv in investigations: + inv_id = inv.get('InvestigationId', 'N/A') + severity = inv.get('Severity', 'N/A') + status = inv.get('Status', 'N/A') + entity = inv.get('EntityArn', 'N/A') + created = inv.get('CreatedTime', 'N/A') + print(f" Investigation: {inv_id}") + print(f" Severity: {severity} | Status: {status}") + print(f" Entity: {entity}") + print(f" Created: {created}") + print() + + return investigations + + +def get_investigation_detail(session, graph_arn, investigation_id): + """Get detailed information about a specific investigation.""" + client = session.client('detective') + + response = client.get_investigation( + GraphArn=graph_arn, + InvestigationId=investigation_id, + ) + + print(f"[+] Investigation: {investigation_id}") + print(f" Entity: {response.get('EntityArn', 'N/A')}") + print(f" Entity Type: {response.get('EntityType', 'N/A')}") + print(f" Severity: {response.get('Severity', 'N/A')}") + print(f" Status: {response.get('Status', 'N/A')}") + print(f" Created: {response.get('CreatedTime', 'N/A')}") + print(f" Scope Start: {response.get('ScopeStartTime', 'N/A')}") + print(f" Scope End: {response.get('ScopeEndTime', 'N/A')}") + + return response + + +def list_indicators(session, graph_arn, investigation_id, max_results=50): + """List indicators for a specific investigation.""" + client = session.client('detective') + + response = client.list_indicators( + GraphArn=graph_arn, + InvestigationId=investigation_id, + MaxResults=max_results, + ) + + indicators = response.get('Indicators', []) + if not indicators: + print("[+] No indicators found for this investigation") + return [] + + print(f"[+] Found {len(indicators)} indicator(s)\n") + for ind in indicators: + ind_type = ind.get('IndicatorType', 'N/A') + detail = ind.get('IndicatorDetail', {}) + print(f" Type: {ind_type}") + if detail: + print(f" Detail: {json.dumps(detail, default=str)[:200]}") + print() + + return indicators + + +def export_results(data, output_dir): + """Export investigation results to JSON.""" + os.makedirs(output_dir, exist_ok=True) + out_path = os.path.join(output_dir, "detective_results.json") + with open(out_path, "w") as f: + json.dump(data, f, indent=2, default=str) + print(f"[+] Results exported to {out_path}") + return out_path + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="AWS Detective Threat Hunting Tool" + ) + parser.add_argument( + "--graphs", action="store_true", help="List behavior graphs" + ) + parser.add_argument( + "--investigations", action="store_true", help="List investigations" + ) + parser.add_argument("--graph-arn", type=str, help="Behavior graph ARN") + parser.add_argument( + "--investigation-id", type=str, help="Investigation ID for detail view" + ) + parser.add_argument( + "--indicators", action="store_true", help="List indicators" + ) + parser.add_argument( + "--severity", + nargs="+", + default=None, + help="Severity filter (e.g. HIGH CRITICAL)", + ) + parser.add_argument("--max-results", type=int, default=20) + parser.add_argument("--region", default="us-east-1") + parser.add_argument("--profile", type=str, help="AWS profile name") + parser.add_argument( + "--output", type=str, help="Output directory for JSON export" + ) + args = parser.parse_args() + + kwargs = {"region_name": args.region} + if args.profile: + kwargs["profile_name"] = args.profile + session = boto3.Session(**kwargs) + + results = {} + + if args.graphs: + results["graphs"] = list_behavior_graphs(session) + + if args.investigations: + if not args.graph_arn: + print("[!] --graph-arn required for --investigations") + sys.exit(1) + results["investigations"] = list_investigations( + session, args.graph_arn, args.severity, args.max_results + ) + + if args.investigation_id: + if not args.graph_arn: + print("[!] --graph-arn required for --investigation-id") + sys.exit(1) + results["detail"] = get_investigation_detail( + session, args.graph_arn, args.investigation_id + ) + + if args.indicators: + if not args.graph_arn or not args.investigation_id: + print("[!] --graph-arn and --investigation-id required for --indicators") + sys.exit(1) + results["indicators"] = list_indicators( + session, args.graph_arn, args.investigation_id, args.max_results + ) + + if args.output and results: + export_results(results, args.output) From 1ff706ebea9790f0006ed41d9e3ff3828f020698 Mon Sep 17 00:00:00 2001 From: MAGI Date: Sun, 22 Mar 2026 16:06:14 -0600 Subject: [PATCH 4/4] Fix review comments: correct AWS Detective API usage and forensic ordering - Fix FilterCriteria to use singular Severity/Status with Value objects instead of invalid plural Severities/Statuses arrays (SKILL.md + process.py) - Fix get_entity_history: rename to get_investigation_indicators, use investigation_id instead of entity_arn for InvestigationId parameter - Replace invalid inv-* placeholders with 21-digit numeric IDs - Fix Expected Output to match real API response structure (no embedded Indicators; document separate list-indicators call and indicator types) - Fix CLI --filter-criteria example to use correct format - Update process.py --severity to accept single value with validation - Add --max-results validation (1-100 range) - Add pagination via _collect_all_pages helper for all list API calls - Reorder Response Actions checklist: evidence preservation before containment - Reorder Phase 5 workflow: preserve evidence first when safe Co-Authored-By: Claude Opus 4.6 (1M context) --- .../SKILL.md | 40 ++++++++--------- .../assets/template.md | 2 +- .../references/workflows.md | 9 ++-- .../scripts/process.py | 43 +++++++++++++------ 4 files changed, 54 insertions(+), 40 deletions(-) diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md index 83bcd2cd..0b0ae103 100644 --- a/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md @@ -46,7 +46,7 @@ aws detective list-graphs --output table # Get entity profile for an IAM user aws detective get-investigation \ --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ - --investigation-id inv-1234567890 + --investigation-id 000000000000000000001 ``` ### Step 3: Search Entities Programmatically @@ -65,29 +65,26 @@ def list_behavior_graphs(): response = detective.list_graphs() return response.get('GraphList', []) -def get_entity_history(graph_arn, entity_arn, hours=24): - """Get API call history for an entity.""" - end_time = datetime.utcnow() - start_time = end_time - timedelta(hours=hours) - +def get_investigation_indicators(graph_arn, investigation_id, max_results=50): + """Get indicators for a specific investigation.""" response = detective.list_indicators( GraphArn=graph_arn, - InvestigationId=entity_arn, - MaxResults=50 + InvestigationId=investigation_id, + MaxResults=max_results ) return response.get('Indicators', []) def investigate_guardduty_findings(graph_arn): - """List finding groups correlated by Detective.""" + """List high-severity investigations correlated by Detective.""" response = detective.list_investigations( GraphArn=graph_arn, FilterCriteria={ - 'Statuses': ['RUNNING', 'FAILED'], - 'Severities': ['HIGH', 'CRITICAL'] + 'Severity': {'Value': 'CRITICAL'}, + 'Status': {'Value': 'RUNNING'} }, MaxResults=20 ) - + for investigation in response.get('InvestigationDetails', []): print(f"Investigation: {investigation['InvestigationId']}") print(f" Entity: {investigation['EntityArn']}") @@ -109,7 +106,7 @@ if __name__ == "__main__": # List investigations with high severity aws detective list-investigations \ --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ - --filter-criteria '{"Severities":["HIGH","CRITICAL"]}' \ + --filter-criteria '{"Severity":{"Value":"HIGH"}}' \ --max-results 10 ``` @@ -119,33 +116,32 @@ aws detective list-investigations \ # Get indicators for a specific investigation aws detective list-indicators \ --graph-arn arn:aws:detective:us-east-1:123456789012:graph:a1b2c3d4 \ - --investigation-id inv-1234567890 \ + --investigation-id 000000000000000000001 \ --max-results 50 ``` ## Expected Output +The `list-investigations` command returns investigation metadata: + ```json { "InvestigationDetails": [ { - "InvestigationId": "inv-0a1b2c3d4e5f", + "InvestigationId": "000000000000000000001", "Severity": "CRITICAL", "Status": "RUNNING", + "State": "ACTIVE", "EntityArn": "arn:aws:iam::123456789012:user/suspicious-user", "EntityType": "IAM_USER", - "CreatedTime": "2026-03-15T14:30:00Z", - "Indicators": { - "ImpossibleTravel": true, - "NewGeoLocation": "ru-central-1", - "UnusualApiCalls": ["CreateAccessKey", "AttachUserPolicy", "PutBucketPolicy"], - "RelatedFindings": 7 - } + "CreatedTime": "2026-03-15T14:30:00Z" } ] } ``` +Indicators are retrieved separately via `list-indicators` and include types such as `TTP_OBSERVED`, `IMPOSSIBLE_TRAVEL`, `FLAGGED_IP_ADDRESS`, `NEW_GEOLOCATION`, `NEW_ASO`, `NEW_USER_AGENT`, `RELATED_FINDING`, and `RELATED_FINDING_GROUP`. + ## Verification 1. Confirm behavior graph has data: `aws detective list-graphs` returns non-empty list diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md index 5924e83c..66eb65e5 100644 --- a/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md @@ -27,8 +27,8 @@ - [ ] Initial access vector identified ## Response Actions +- [ ] Evidence preserved (or capture rationale if immediate containment required) - [ ] Compromised credentials disabled - [ ] Active sessions revoked - [ ] Affected resources isolated -- [ ] Evidence preserved - [ ] Stakeholders notified diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md index 688b70f1..69f32f74 100644 --- a/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md @@ -24,7 +24,8 @@ 4. Document indicators of compromise (IOCs) ## Phase 5: Response -1. Disable compromised credentials -2. Revoke active sessions -3. Isolate affected resources -4. Preserve evidence (CloudTrail logs, flow logs) +1. Preserve evidence (CloudTrail logs, flow logs, snapshots) when safe +2. Disable compromised credentials +3. Revoke active sessions +4. Isolate affected resources +5. If active impact is ongoing, contain first and document evidence trade-offs diff --git a/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py b/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py index d9bc1658..6cf15f4f 100644 --- a/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py +++ b/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py @@ -13,11 +13,23 @@ from datetime import datetime, timedelta +def _collect_all_pages(client_method, result_key, **kwargs): + """Paginate through all pages of an AWS Detective API call.""" + all_items = [] + while True: + response = client_method(**kwargs) + all_items.extend(response.get(result_key, [])) + next_token = response.get('NextToken') + if not next_token: + break + kwargs['NextToken'] = next_token + return all_items + + def list_behavior_graphs(session): """List all Detective behavior graphs in the account.""" client = session.client('detective') - response = client.list_graphs() - graphs = response.get('GraphList', []) + graphs = _collect_all_pages(client.list_graphs, 'GraphList') if not graphs: print("[!] No behavior graphs found. Enable Detective first.") @@ -33,13 +45,13 @@ def list_behavior_graphs(session): return graphs -def list_investigations(session, graph_arn, severities=None, max_results=20): +def list_investigations(session, graph_arn, severity=None, max_results=20): """List investigations filtered by severity.""" client = session.client('detective') filter_criteria = {} - if severities: - filter_criteria['Severities'] = severities + if severity: + filter_criteria['Severity'] = {'Value': severity} kwargs = { 'GraphArn': graph_arn, @@ -48,8 +60,9 @@ def list_investigations(session, graph_arn, severities=None, max_results=20): if filter_criteria: kwargs['FilterCriteria'] = filter_criteria - response = client.list_investigations(**kwargs) - investigations = response.get('InvestigationDetails', []) + investigations = _collect_all_pages( + client.list_investigations, 'InvestigationDetails', **kwargs + ) if not investigations: print("[+] No investigations found matching criteria") @@ -96,13 +109,12 @@ def list_indicators(session, graph_arn, investigation_id, max_results=50): """List indicators for a specific investigation.""" client = session.client('detective') - response = client.list_indicators( + indicators = _collect_all_pages( + client.list_indicators, 'Indicators', GraphArn=graph_arn, InvestigationId=investigation_id, MaxResults=max_results, ) - - indicators = response.get('Indicators', []) if not indicators: print("[+] No indicators found for this investigation") return [] @@ -150,11 +162,13 @@ def export_results(data, output_dir): ) parser.add_argument( "--severity", - nargs="+", + type=str, default=None, - help="Severity filter (e.g. HIGH CRITICAL)", + choices=["INFORMATIONAL", "LOW", "MEDIUM", "HIGH", "CRITICAL"], + help="Severity filter (e.g. HIGH)", ) - parser.add_argument("--max-results", type=int, default=20) + parser.add_argument("--max-results", type=int, default=20, + help="Max results per API call (1-100)") parser.add_argument("--region", default="us-east-1") parser.add_argument("--profile", type=str, help="AWS profile name") parser.add_argument( @@ -162,6 +176,9 @@ def export_results(data, output_dir): ) args = parser.parse_args() + if args.max_results < 1 or args.max_results > 100: + parser.error("--max-results must be between 1 and 100") + kwargs = {"region_name": args.region} if args.profile: kwargs["profile_name"] = args.profile