Containment and eradication
Lesson 3 of 5 in Responding to Security Events.
Containment stops the spread. Eradication removes the cause. They are different phases with different success criteria, and conflating them is how an incident gets declared closed twice.
Containment is a ladder rather than a switch, and every rung trades something. Low rungs are surgical and preserve everything; high rungs are decisive and expensive. The skill the exam tests is picking the lowest rung that actually stops this attacker — because responders under pressure reach too high (terminating a host they needed) or too low (detaching a policy from a role whose stolen session is still working perfectly). Climb from the network, through the identity, to the resource itself.
| Rung | What it stops | What it costs | What it preserves |
|---|---|---|---|
The instance’s network reach — inbound and outbound, including the command-and-control channel | Nothing, if the group is pre-created and the instance is managed by SSM. The app on that host is down | Everything: the instance runs, memory is intact, Systems Manager still reaches it | |
Network ACL deny | Traffic at the subnet edge, statelessly, on the next packet | The whole subnet — every other instance in it, too | The instance and its memory; only connectivity is affected |
Remove the route out | Egress to the internet for the subnet, by deleting the route to the NAT or internet gateway | All egress for everything in that route table — including your own agents | The host entirely; useful when you must stop exfiltration subnet-wide |
Detach the instance profile | Future credential retrieval from the instance metadata service | The application’s AWS access breaks | Everything — but it does not stop credentials already issued, which stay valid until expiry |
Explicit deny on the principal | Every API call by that role or user, immediately — authorization is evaluated per request | Every session using that principal, including legitimate workloads sharing the role | All evidence; the principal still exists for investigation |
Only sessions issued before the cut-off — the stolen one | Almost nothing: new sessions still work, so legitimate workloads recover on their next refresh | All evidence, and service continuity for everything that re-authenticates | |
Deactivate the access key | A long-term IAM user credential, instantly and completely | Anything genuinely using that key — which is why you find out what that is first | The key record itself if you deactivate rather than delete, which keeps the evidence |
Stop or terminate the instance | Everything that host was doing | Memory, instance store, and — on terminate with delete-on-termination — the volumes | Nothing volatile. The last rung, and only after capture |
Three rungs need the detail, because each one carries a fact the exam likes.
Network containment is two different mechanisms. A security group is stateful: it tracks connections, so a rule change takes effect for new connections and an already-established, tracked session can persist for a time after you remove the rule that permitted it. A network ACL is stateless and evaluated per packet, in rule-number order, so a deny entry lands on the very next packet — but it applies to the entire subnet, so you have just contained every instance that shares it. The practical pattern in a hurry: isolation security group for the surgical cut, network ACL deny when you need it stopped now and can accept the collateral damage. A quarantine subnet with no route out is the third shape, and it comes with an honest constraint worth knowing: you cannot move a running EC2 instance to a different subnet. In practice "quarantine" for a live host means an isolation group plus a subnet-level deny plus removing the egress route; a true quarantine subnet is where you restore the forensic copy for analysis.
Detaching the instance profile is not credential revocation. Removing the profile stops the metadata service handing out new credentials, which is useful — but the credentials the attacker already exfiltrated keep working until they expire. One prevention note worth carrying: AWS blocks EC2 role credentials retrieved through IMDSv2 from being used outside the instance, which is among the strongest arguments for requiring IMDSv2 organization-wide.
And you cannot un-issue an STS session. This is the one that catches people. There is no API that invalidates an issued set of temporary credentials; they are valid until their expiry. Authorization, however, is evaluated against the role’s current policies on every request — so the way you stop a stolen session is to make the requests fail. The blunt version is attaching a deny-all policy to the role, which works and takes down every other session using that role with it. The surgical version is a deny conditioned on aws:TokenIssueTime: deny everything for sessions issued before the moment of compromise, and sessions issued afterwards are unaffected. That is exactly what the console’s "Revoke active sessions" does — it attaches an inline policy to the role. One more property to remember: aws:TokenIssueTime is present only in requests signed with temporary credentials, so the trick does nothing against a long-term IAM user access key. For those you deactivate the key.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSRevokeOlderSessions",
"Effect": "Deny",
"Action": ["*"],
"Resource": ["*"],
"Condition": {
"DateLessThan": { "aws:TokenIssueTime": "{{INCIDENT_CUTOFF_UTC}}" }
}
}
]
}Attached as an inline policy on the compromised role — this is what the console’s "Revoke active sessions" writes. Sessions issued before the cut-off are denied on their next request; sessions issued after it are untouched, so legitimate workloads recover as soon as they refresh. It has no effect on long-term IAM user access keys, because aws:TokenIssueTime only appears in requests signed with temporary credentials. Set the cut-off to just before the first confirmed malicious call, not to "now".
Which containment control fits this incident?
Interactive decision tree — outcomes:
- Replace the security groups with an isolation group
The lowest rung that works: attach a pre-created group permitting nothing. The host loses its channel, keeps running, keeps its memory, and stays reachable through Systems Manager so you can still collect. Pair it with detaching the instance profile if credentials may have leaked, and remember that a stateful group may let an already-tracked connection linger — if seconds matter, add the network ACL deny.
- Deny at the network ACL, accepting the subnet-wide cost
Stateless and immediate: the deny entry applies to the next packet in both directions. The cost is that it contains the whole subnet, so declare the collateral damage as part of the decision — and follow up with per-instance isolation groups so you can lift the subnet-level block once the bleeding stops. Removing the route to the NAT or internet gateway is the sibling control when exfiltration specifically is the problem.
- Terminate — but only because capture is complete
Legitimate only when memory and disk are already acquired and recorded, or when there is genuinely no forensic requirement. Terminate destroys RAM, instance store, and delete-on-termination volumes. If you are unsure whether capture is complete, you are not at this rung yet.
- Revoke sessions issued before the compromise
Attach the deny conditioned on
aws:TokenIssueTimeset to just before the first malicious call. The stolen session fails on its next request; sessions issued after the cut-off are untouched, so the other workloads recover on their next credential refresh. This is the surgical answer, and it is what the console’s "Revoke active sessions" button writes for you. - Attach an explicit deny to the role
With no innocent sessions to protect, a blanket
Deny *on the role is simpler and absolute: every request using it fails immediately, because authorization is evaluated per request against current policy. Then fix the leak — the instance profile, the exposed configuration, the committed secret — because the role will need its permissions back. - Deactivate the access key, then delete it
aws:TokenIssueTimedoes not exist for long-term credentials, so session revocation is not the tool. Deactivate the key (which preserves the record for investigation), confirm from CloudTrail that nothing legitimate was using it, then delete it and disable or delete the IAM user. Then ask the real question: a long-term key existed at all — which workload needs replacing with a role, IAM Roles Anywhere, or Identity Center. - Contain in both places — AWS and the identity provider
Deny or revoke the AWS role sessions, and in the same breath disable the account, revoke its sessions, and reset credentials at the identity provider, because that is where the attacker authenticates. Containing only the AWS side leaves them able to start a fresh, fully legitimate session — and in IAM Identity Center you can also delete the user’s active sessions directly.
- Cut public access at the resource and the account level
Remove the offending statement from the resource policy, then switch on the account-level block so a repeat is impossible rather than merely fixed — S3 Block Public Access at the account level for buckets, and the equivalent account-wide controls for snapshots and AMIs. Then find out what was read, using data event logs if they were enabled, and treat that answer as the notification trigger rather than the technical fix.
Containment holds the line; eradication removes the reason you needed it. Four categories, and the exam’s instinct is that you cannot skip any of them.
Remove persistence. On the host: cron jobs and systemd timers, modified authorized_keys, replaced binaries, loaded kernel modules, startup scripts. In the account — the part people forget — new IAM users, access keys, roles, and especially modified role trust policies; new identity providers; Lambda functions and the EventBridge rules that invoke them; modified launch templates, Auto Scaling configurations, and AMIs; new VPC peering or endpoint services; and new routes. A host rebuilt from a clean AMI into an account that still has the attacker’s access key is not eradicated.
Kill the malicious processes and close the entry point. Patch the vulnerability that allowed the breach, or fix the misconfiguration — the exposed management port, the overly permissive bucket policy, the secret in the repository, the WAF rule that was in count mode. Eradication without this step hands the attacker the same door.
Rotate everything the attacker could have seen. Not just the credential that was abused: anything readable from the compromised position. Instance profile permissions and whatever secrets the application could fetch, database passwords, API keys in the environment or in Secrets Manager, and TLS private keys if they were on disk.
Then verify. Scan the rebuilt resource with Amazon Inspector, compare its configuration against the known-good baseline, re-run the detection that caught it, and check that the indicators from the investigation no longer appear anywhere. Eradication is a claim; verification is the evidence for it.
Key terms: Containment, Eradication, Isolation security group, Quarantine subnet, Session revocation, aws:TokenIssueTime
Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.