Series: Agent-Driven Workflows - CrowdStrike Detection Triage. Post 2 of 5. Originally published on LinkedIn. Previous in series: Cloud Account Discovery at Scale.
Detection Context
Defense evasion detections in AWS are high-signal because attackers disable logging and open network paths before moving laterally or exfiltrating data. This class maps to MITRE T1562 - Impair Defenses - and covers three distinct behaviors: silencing CloudTrail, punching holes in security groups, and re-enabling IMDSv1 to expose credential endpoints. When these fire in a production environment, the window between detection and impact is short.
Scenario
The team opens the CrowdStrike Falcon console and finds 100+ defense evasion detections sitting in the queue, including: CloudTrail tampering events, security group modifications, and IMDSv1 enablements. The environment spans multiple AWS accounts with a mix of production workloads and CDE subnets falling under the scope of PCI. Manual triage is slow and inefficient.
The bulk of the CloudTrail hits are StopLogging and UpdateTrail/PutEventSelectors - two patterns that look very different at the response level. The security group alerts split between ingress 0.0.0.0/0 and egress 0.0.0.0/0, with a handful touching subnets recognized as PCI scope. The IMDSv1 detections are all ModifyInstanceAttribute calls setting httpTokens to optional - low individual severity, but a forced-pivot enabler if any of those instances carried a high-privilege role.
The following alert fields are handed to an agent harness so it runs structured triage across all three sub-types in parallel:
display_name, event_name, source_ip_address, aws_access_key_id, cloud_account_id, source_account_name, request_parameters, first_timestamp.
Prompt Chain
Prompt 1 - Classify the alert sub-type and pull the raw event
You are a cloud security triage agent.
Alert fields:
- event_name: {{event_name}}
- source_ip_address: {{source_ip_address}}
- aws_access_key_id: {{aws_access_key_id}}
- cloud_account_id: {{cloud_account_id}}
- source_account_name: {{source_account_name}}
- request_parameters: {{request_parameters}}
- first_timestamp: {{first_timestamp}}
Step 1: Classify this alert as one of: CloudTrail-Tampering, SecurityGroup-Modification, or IMDSv1-Enablement. Step 2: Call the relevant AWS API to retrieve the full event record within a 5-minute window around first_timestamp. Step 3: Return the classification, the raw event JSON, and the caller identity (ARN, access key, source IP).
Prompt 2 - Change ticket lookup and authorization check
Given the following triage context:
- Event classification: {{classification}}
- Caller ARN: {{caller_arn}}
- Event time: {{first_timestamp}}
- Account: {{cloud_account_id}}
Query Jira for any open or recently closed change tickets that:
- Cover this action type in account {{cloud_account_id}}
- Were active within a 2-hour window of {{first_timestamp}}
- List {{caller_arn}} or the associated team as the authorized implementer
Return: ticket_id, ticket_status, authorized_implementer, ticket_scope, match (yes/no).
If no ticket is found, return match=no and flag for escalation.
Prompt 3 - Sub-type specific risk assessment
Using the classification and event data from prior steps, run the appropriate risk check:
If CloudTrail-Tampering:
- Map event_name to severity: StopLogging or DeleteTrail = CRITICAL, UpdateTrail or PutEventSelectors = HIGH if scope reduced
- Check if logging was restored after the event
If SecurityGroup-Modification:
- Extract groupId, port range, protocol, CIDR from request_parameters
- Identify attached instances and check subnet against CDE network map
- Apply port risk table: SSH/RDP = HIGH, all-port = CRITICAL, app port = MEDIUM, ephemeral = LOW
- Confirm whether the rule is still active
If IMDSv1-Enablement:
- Retrieve the instanceId and its attached IAM role
- Flag HIGH if role has admin or cross-account permissions, flag CDE scope separately
Return: severity, scope_flags (CDE, PCI, production), rule_still_active (yes/no/n-a).
Prompt 4 - Decision and response action
Given:
- Classification: {{classification}}
- Severity: {{severity}}
- Change ticket match: {{ticket_match}}
- Scope flags: {{scope_flags}}
- Rule still active: {{rule_still_active}}
Apply the decision table for this classification and output:
- Verdict: False Positive, True Positive, or Undetermined
- Immediate actions (list, in order)
- Falcon alert status to set: closed, in-progress, or escalate
- Slack message draft summarizing the finding for the on-call channel
Agent Workflow
CloudTrail Tampering
Step 1 - Map the event to its impact
The agent reads event_name and maps it against this table:
| event_name | Meaning |
|---|---|
| StopLogging | CloudTrail logging paused - gap in audit record begins |
| DeleteTrail | Trail deleted - logging gone until recreated |
| UpdateTrail | Trail configuration changed - scope or destination may have shifted |
| PutEventSelectors | Event selectors narrowed - specific event types may be excluded |
Step 2 - Pull the raw CloudTrail event
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue={{event_name}} \
--start-time {{first_timestamp - 5min}} \
--end-time {{first_timestamp + 5min}} \
--region {{region}}
For StopLogging, the agent extracts the trail ARN. For PutEventSelectors, it extracts the new selector configuration and compares it against the previous baseline to determine what was excluded.
Step 3 - Jira change ticket check
The agent queries the Jira REST API for tickets in the relevant account’s change board, filtering for tickets active within a 2-hour window of first_timestamp. It checks whether the caller ARN matches the authorized implementer on the ticket.
Step 4 - Decision table
| Condition | Verdict | Action |
|---|---|---|
| StopLogging + no ticket | CRITICAL | Escalate immediately, restore logging |
| StopLogging + valid ticket | Document | Verify logging was restored post-change |
| DeleteTrail + no ticket | CRITICAL | Escalate, open incident |
| UpdateTrail + no ticket | HIGH | Review scope change, hold for analyst |
| PutEventSelectors + no ticket | HIGH | Review excluded events, hold for analyst |
| UpdateTrail + valid ticket + scope unchanged | Close | Document and close |
Security Group Modifications
Step 1 - Extract the rule details
The agent parses request_parameters for: groupId, port range, protocol, CIDR block (checking both 0.0.0.0/0 and ::/0).
Step 2 - Find attached instances
aws ec2 describe-instances \
--filters Name=instance.group-id,Values={{groupId}} \
--query 'Reservations[].Instances[].{InstanceId:InstanceId,SubnetId:SubnetId,VpcId:VpcId,Tags:Tags}'
Step 3 - Check scope
The agent compares each instance’s SubnetId against the CDE network map and checks instance tags for Environment:production and Compliance:PCI.
Step 4 - Port risk table
| Port / Range | Risk |
|---|---|
| 22 (SSH) or 3389 (RDP) | HIGH |
| 0-65535 (all ports) | CRITICAL |
| Application port (80, 443, 8080) | MEDIUM |
| Ephemeral range (32768-60999) | LOW |
Step 5 - Confirm rule is still active
aws ec2 describe-security-groups \
--group-ids {{groupId}} \
--query 'SecurityGroups[].IpPermissions'
Step 6 - Decision
- Port 22 or 3389 + CDE subnet + no ticket: revoke rule immediately, open incident
- All-port rule (0-65535): CRITICAL, escalate for revocation
- Non-CDE + valid ticket: document, close
IMDSv1 Enablement
Step 1 - Get the instance
The agent extracts instanceId from request_parameters.
Step 2 - Check the attached IAM role
aws ec2 describe-iam-instance-profile-associations \
--filters Name=instance-id,Values={{instanceId}}
The agent retrieves the role ARN and checks for admin-level or cross-account policies.
Step 3 - Remediate
Always remediate, regardless of ticket status:
aws ec2 modify-instance-metadata-options \
--instance-id {{instanceId}} \
--http-tokens required \
--http-endpoint enabled
Step 4 - Escalation criteria
- CDE subnet: escalate
- High-privilege IAM role (admin, PowerUser, or cross-account AssumeRole): escalate and review CloudTrail for that instance over the exposure window
CQL Hunt Queries
Use these in CrowdStrike NG-SIEM / LogScale to sweep for activity across the full fleet, independent of what Falcon surfaced.
Hunt 1 - CloudTrail tampering
#repo=search-all eventSource="cloudtrail.amazonaws.com"
(
eventName="StopLogging"
OR eventName="DeleteTrail"
OR eventName="UpdateTrail"
OR eventName="PutEventSelectors"
)
| select([
eventTime,
eventName,
userIdentity.arn,
userIdentity.accessKeyId,
sourceIPAddress,
requestParameters
])
| sort(eventTime, order=desc)
Hunt 2 - Security group rules opened to internet
#repo=search-all eventSource="ec2.amazonaws.com"
(
eventName="AuthorizeSecurityGroupIngress"
OR eventName="AuthorizeSecurityGroupEgress"
)
| requestParameters.ipPermissions.items[].ipRanges.items[].cidrIp = "0.0.0.0/0"
OR requestParameters.ipPermissions.items[].ipv6Ranges.items[].cidrIpv6 = "::/0"
| select([
eventTime,
eventName,
userIdentity.arn,
requestParameters.groupId,
requestParameters.ipPermissions,
sourceIPAddress
])
| sort(eventTime, order=desc)
Output and Decision
False Positive
Conditions: Valid Jira change ticket, caller matches authorized implementer, action is within documented scope, logging was restored (CloudTrail), rule was temporary and already removed (SG), or IMDSv1 was re-enabled for a documented legacy workload with no sensitive role attached.
Actions:
- Set Falcon alert status to closed
- Document ticket reference and analyst name in the alert notes
- No further action required
True Positive
Conditions: No change ticket, caller ARN does not match any authorized team, action originated from an external IP outside VPN CIDRs, rule still active (SG), logging still paused (CloudTrail), or IMDSv1 active on a high-privilege or CDE instance.
Actions for authorized operator (by sub-type):
CloudTrail:
- Restore logging immediately via
aws cloudtrail start-logging --name {{trail_arn}} - Revoke or rotate the access key used
- Pull 24 hours of CloudTrail events for that access key
- Open a P1 incident
Security Group:
- Revoke the rule via
aws ec2 revoke-security-group-ingressorrevoke-security-group-egress - Pull VPC Flow Logs for the security group during the full exposure window
- Open a P1 or P2 incident depending on CDE scope
IMDSv1:
- Run
modify-instance-metadata-options --http-tokens requiredimmediately - Open a tracking ticket
- If extended exposure + high-privilege role: pull CloudTrail for that instance and open an investigation
Undetermined
Conditions: Ticket exists but caller does not match, ticket is expired, source IP is ambiguous (shared NAT), or the event is within a change window but request_parameters show unexpected scope.
Actions:
- Set Falcon alert status to in-progress
- Page the on-call engineer and the account owner
- Do not close until a human confirms authorization
- Document all findings in the alert before handing off
Prerequisites
Access:
| System | Permission Needed |
|---|---|
| CrowdStrike Falcon | Alerts read/write, Detections read |
| AWS (per account) | cloudtrail:LookupEvents, ec2:DescribeInstances, ec2:DescribeSecurityGroups, ec2:DescribeIamInstanceProfileAssociations, ec2:ModifyInstanceMetadataOptions, ec2:RevokeSecurityGroupIngress, ec2:RevokeSecurityGroupEgress |
| Jira | API token with read access to the change management board |
| Slack | Webhook URL for the on-call security channel |
Context (what you feed the harness). The harness needs:
- Falcon Alerts API reference and field schema
- CQL/LogScale syntax guide for the version in use
- CloudTrail event schema for
cloudtrail.amazonaws.comandec2.amazonaws.comsources, or access to ingest - Environment context: VPN CIDR ranges, CDE subnet list, AWS account IDs mapped to account names
- MITRE T1562 sub-technique descriptions for in-prompt classification grounding
Defense evasion triage benefits from codification more than almost any other alert class. Once you encode the decision tables - ticket match, scope check, port risk, logging state - the agent runs the same logic consistently across a volume of alerts that would take a human analyst the better part of a day.
Each run produces a structured audit trail: the raw event, the Jira lookup result, the scope flags, the verdict, and the response actions taken. That trail compounds over time. Future investigations get faster because you can query past verdicts by account, caller ARN, or event type. The workflow itself is tool-agnostic - the same prompt chain runs against any LLM with API access to CrowdStrike, AWS, and Jira.
Next in series: Cloud Impact - triaging data exfiltration and resource abuse detections in AWS.
Building AI-driven detection and triage for your own environment? See how 543HQ designs security automation workflows.
Need a hand with this at your company?
543HQ helps startups and growing companies build security programs that actually work. Start with a free 30-minute call.
Book a Free Call