AWS #
Log sources - what each one actually covers
AWS timestamps 101: every CloudTrail record carries eventTime in ISO-8601 UTC with Z suffix (second resolution; some data events have fractional), set by the CloudTrail service at the moment the API call is processed, not when the client initiated it (clock skew between caller and service is possible). CloudTrail delivers to S3 within an average of about 5 minutes of the API call, and AWS does not guarantee it - allow for longer. VPC Flow Logs are aggregated into a maximum interval of 10 minutes (default) or 1 minute (there is no 10-second option; Nitro-based ENIs are always 1 minute or less) - start/end are the first and last packet times seen in the window, so you cannot sequence activity below roughly a minute from flow logs alone. GuardDuty findings have createdAt, updatedAt, service.eventFirstSeen, service.eventLastSeen - a finding's updatedAt can be hours after the underlying event because GuardDuty keeps updating one finding as new occurrences arrive.
| Source | Covers | Gotchas | Parsers / tools |
|---|---|---|---|
| CloudTrail - management events | Control-plane API calls (IAM, EC2, S3 bucket config, Lambda create, etc.) | Default retention in Event history = 90 days UI only. Durable logging requires a trail → S3. Multi-region, org-trail required for full coverage. ts: eventTime ISO-8601 UTC (second); S3 object key uses UTC date partition. Delivery to S3 averages ~5 min (not guaranteed) after the API call. requestID + eventID are stable keys to join across delayed deliveries. | Athena (w/ partition projection), CloudTrail Lake, aws cloudtrail lookup-events, CloudTrail-Partitioner (Duo), Panther, Splunk |
| CloudTrail - data events | S3 object-level, Lambda invoke, DynamoDB item ops, more | Off by default and billed separately. Many investigations fail because data events weren't on. ts: same eventTime format but higher-frequency events can batch - multiple records for the same object within a second may share an eventTime but have unique eventIDs. S3 object-level reads also stamp the S3 server-access log (different ts, different delivery lag - up to several hours). | Athena, CloudTrail Lake, Mitiga IR, Cado Response |
| CloudTrail Insights | Anomaly detection on API call rate / error rate | Not forensic-grade; triage aid. | Console, aws cloudtrail get-insight-selectors |
| CloudTrail Lake | Managed data lake with SQL | Closed to new customers since 31 May 2026 (existing users only; critical fixes only). AWS points new users to CloudWatch. If already in use, retention can be up to 10 years (one-year extendable pricing) or a fixed 7 years (seven-year pricing); otherwise query the trail with Athena. | aws cloudtrail start-query, Lake console, CloudWatch Logs Insights |
| VPC Flow Logs | 5-tuple + bytes/packets + action (ACCEPT/REJECT) | No payload. Granularity: subnet, ENI, or VPC. Build a custom format with the fields you need - tcp-flags is a v3 field, flow-direction / traffic-path are v5. Transit Gateway has its own flow logs. ts: start and end are Unix epoch seconds (UTC) bounding the aggregation window, max 600 s (default) or 60 s, Nitro always ≤60 s; each can be off from the real packet time by up to a minute. Delivery lags the window end by ~5 min to CloudWatch, ~10 min to S3. | Athena, CloudWatch Logs Insights, Flowlogs-reader (obsrvbl) |
| Route 53 Resolver query logs | DNS from VPC | Different from Route 53 public DNS logs; both exist. ts: query_timestamp ISO-8601 UTC - per AWS, the time the query was submitted. | Athena, CloudWatch, Chronicle/Google SecOps ingest |
| GuardDuty | Managed detections. Foundational sources (always on with GuardDuty): CloudTrail management events, VPC Flow Logs, DNS logs. Protection plans (opt-in): S3, EKS Audit, EKS/EC2 Runtime Monitoring (eBPF agent), Malware Protection, RDS login, Lambda. ts: each finding has createdAt (first opened) and updatedAt (last correlated occurrence); service.eventFirstSeen/eventLastSeen bound the activity. New findings export in ~5 min; updates to an existing finding export every 6 h by default (set to 15 min / 1 h), so updatedAt can lag the activity. | GuardDuty console, aws guardduty get-findings, Security Hub, SIEM | |
| Security Hub | Aggregator for GuardDuty, Macie, Inspector, Config, 3P | Security Hub CSPM (formerly Security Hub; ASFF, best-practice controls) and the new Security Hub (OCSF, correlation/risk analytics, GA Dec 2025) - a client may have either or both. Dedupe matters. | aws securityhub get-findings, console, SIEM |
| Detective | Graph of entity behavior over ~1yr | Great pivoting UI; expensive at scale. | Detective console (no CLI equiv for the graph) |
| Config | Resource configuration timeline | Lets you reconstruct "when did this SG become 0.0.0.0/0?" | aws configservice get-resource-config-history, Config advanced queries (SQL), Athena |
| S3 server access logs / S3 access analyzer | Bucket-level access | Access analyzer finds public/externally shared resources. | Athena on S3 access logs, IAM Access Analyzer |
| Athena | SQL over CloudTrail / Flow / etc in S3 | Expect to write ad hoc queries under time pressure. | Console, aws athena start-query-execution, pyathena |
IAM & identity attack surface
- Long-lived access keys (AKIA…) - leaked keys are the #1 initial access vector. Rotate via
aws iam list-access-keys,UpdateAccessKey. - STS short-lived creds (ASIA… tokens from
sts:AssumeRole,GetFederationToken,GetSessionToken). Appear as separateuserIdentity.sessionContext. - AssumeRoleWithWebIdentity - OIDC federation. GitHub Actions commonly uses this; misconfigured trust policy (
sub: *, missing audience) → cross-tenant takeover. - Role chaining - AssumeRole from one session into another, limited to 1-hour max session unless using original creds; attackers chain to launder lineage.
- Session tags / SourceIdentity - enterprises require
sts:SourceIdentityto trace end-user identity through role assumption. - IMDS - v1 unauthenticated; v2 requires token (PUT /latest/api/token). A PUT-response hop limit of 1 stops containers on the instance from getting a token; AMIs flagged
imds-support v2.0(e.g. Amazon Linux 2023) default to 2, which lets containers reach IMDS - check the instance's metadata options. Account-level defaults and IMDSv2 enforcement can also be set per Region. SSRF to v2 still possible if app proxies PUT. Capital One (2019) = IMDSv1 SSRF; today container metadata (169.254.170.2) for ECS task roles is the modern analogue. - Service control policies (SCPs) at org - denies ride higher than permissions boundaries. But SCPs never restrict the management account or service-linked roles, so they can't contain an attacker operating in the management account. An attacker with org admin can also detach them.
Common adversary API calls - "the greatest hits"
Real CloudTrail eventNames, grouped by what the attacker is doing. Names here are CloudTrail event names, which can differ from the IAM permission (for example the IAM action s3:ListAllMyBuckets appears in CloudTrail as ListBuckets). Most recon is read-only management events, on by default; the big blind spots are flagged inline (S3 object reads need data events). Pivot on userIdentity.arn / sessionContext, sourceIPAddress, userAgent and errorCode.
| CloudTrail event (service) | What it does / why it matters |
|---|---|
| Initial access / sign-in | |
ConsoleLogin | Console sign-in. additionalEventData.MFAUsed=No matters for IAM users / root only - federated and SSO sign-ins always record No (MFA happened at the IdP), so check IdP logs for those. errorMessage "Failed authentication" = password spray. |
GetFederationToken, GetSessionToken (sts) | Mint short-lived creds that are harder to tie back to the source identity. |
AssumeRoleWithWebIdentity, AssumeRoleWithSAML (sts) | Federated entry (GitHub Actions OIDC, SAML SSO). A loose trust policy (sub:*, missing audience) is cross-tenant takeover. |
| Reconnaissance & enumeration (mostly read-only management events - on by default, low-noise to the attacker) | |
GetCallerIdentity (sts) | "Who am I?" - the first call from stolen creds. |
GetAccountAuthorizationDetails (iam) | Dumps every user, group, role and attached/inline policy in one call, the IAM map. Pull it yourself for a point-in-time snapshot too. |
ListUsers, ListRoles, ListGroups, ListPolicies, ListAccessKeys, ListMFADevices, GetLoginProfile, ListAttachedUserPolicies, GetPolicyVersion (iam) | Enumerate identities, their keys, whether MFA/console login is set, and exactly what each can do. |
SimulatePrincipalPolicy, SimulateCustomPolicy (iam) | Ask AWS directly "what can this principal do?" without triggering denies, quiet privesc path-finding. |
ListBuckets (s3; IAM action s3:ListAllMyBuckets) | List every bucket in the account. Then GetBucketPolicy, GetBucketAcl, GetBucketPublicAccessBlock, GetBucketPolicyStatus, ListObjects/ListObjectsV2 to find readable and public data. |
DescribeInstances, DescribeSecurityGroups, DescribeVpcs, DescribeSubnets, DescribeSnapshots, DescribeImages, DescribeVolumes, DescribeKeyPairs, DescribeInstanceAttribute (ec2) | Map compute, networking and where disks/AMIs live (snapshot/AMI exfil targets). |
ListSecrets (secretsmanager), DescribeParameters (ssm), ListFunctions (lambda), DescribeDBInstances/DescribeDBClusters (rds), ListTables (dynamodb) | Find where secrets, code and data live before touching them. |
DescribeOrganization, ListAccounts (organizations) | Map the org - other accounts to pivot into, and whether this is the management account. |
| Tooling: enumerate-iam (Andres Riancho), Pacu recon modules, ScoutSuite, Prowler | Fire hundreds of List/Describe/Get calls fast - a burst of read-only calls from one new principal is itself a signal. |
| Privilege escalation (Rhino Security catalogued ~21 IAM privesc paths - these are the common CloudTrail events) | |
CreatePolicyVersion + --set-as-default, or SetDefaultPolicyVersion (iam) | Rewrite a policy the attacker can edit to grant *, or roll the default to an old permissive version. |
AttachUserPolicy, AttachGroupPolicy, AttachRolePolicy (e.g. AdministratorAccess) | Attach a managed admin policy to a controlled principal. |
PutUserPolicy, PutGroupPolicy, PutRolePolicy (iam) | Add an inline *:* policy, easy to miss because it's not a named managed policy. |
AddUserToGroup (iam) | Join a privileged group. |
UpdateAssumeRolePolicy (iam) | Rewrite a role's trust policy so the attacker principal can assume it, then AssumeRole. |
iam:PassRole + RunInstances / CreateFunction / CreateInstanceProfile + AddRoleToInstanceProfile + AssociateIamInstanceProfile | Pass a more-privileged role to a resource you can run code on, then use its credentials. In CloudTrail you see the consuming call carrying the role ARN. |
UpdateFunctionCode / UpdateFunctionConfiguration (lambda) | Overwrite the code of a function that already has a privileged execution role. |
CreateLoginProfile / UpdateLoginProfile on another user (iam) | Set a console password on a privileged user that only had API keys. |
| Persistence | |
CreateUser + CreateAccessKey + AttachUserPolicy AdministratorAccess | Classic backdoor admin user. Detect via Config / GuardDuty / the event sequence. |
CreateAccessKey on an existing user (iam) | A second access key on a real user - survives the owner's password reset; watch for a user with two active keys. |
CreateLoginProfile (iam) | Add console access to a service-only user. |
CreateRole / UpdateAssumeRolePolicy with trust to an external account or Principal:* | Durable cross-account backdoor the attacker can assume from their own account. |
CreateSAMLProvider / UpdateSAMLProvider, CreateOpenIDConnectProvider (iam) | Add a federation identity provider the attacker controls, a quiet, long-lived way back in. |
CreateFunction + AddPermission + EventBridge rule (PutRule/PutTargets) | Lambda backdoor triggered on a schedule or event, running with an attached role. |
ImportKeyPair / CreateKeyPair (ec2) | Add an SSH key for later access to instances. |
| Credential access | |
GetSecretValue (secretsmanager) | Read stored secrets - DB passwords, API keys. Logged as a management event; rotate anything the principal could read. |
GetParameter / GetParameters WithDecryption=true (ssm) | Read SecureString parameters (often credentials). |
Decrypt, GenerateDataKey (kms) | Decrypt attacker-reachable ciphertext; spikes can indicate bulk decryption. |
GetPasswordData (ec2) | Retrieve the encrypted Windows administrator password for an instance. |
GetAuthorizationToken (ecr) | Get a registry token to pull private container images (may contain secrets). |
| Lateral movement | |
AssumeRole (sts) | Move between roles and accounts; role chaining launders lineage. Follow sessionContext and SourceIdentity. |
SendCommand, StartSession (ssm) | Run commands / get an interactive shell on a private EC2 instance with no SSH or inbound port, a top real-world lateral path. |
SendSSHPublicKey (ec2-instance-connect) | Push a one-time SSH key to an instance, then connect. |
ModifyInstanceAttribute (userData) then stop/start (ec2) | Rewrite boot-time user-data to run attacker code as root on restart. |
CreateDBSnapshot + RestoreDBInstanceFromDBSnapshot (rds) | Clone a database into an instance the attacker controls to read it. |
| Defense evasion & anti-forensics | |
StopLogging, DeleteTrail, UpdateTrail, PutEventSelectors (narrowing scope) (cloudtrail) | Stop or blind the audit trail. StopLogging itself is logged, a high-signal event. |
DeleteFlowLogs (ec2), DeleteResolverQueryLogConfig (route53resolver) | Remove network and DNS visibility. |
UpdateDetector (Enable=false), DeleteDetector, CreateFilter (AUTO_ARCHIVE), CreateIPSet/UpdateIPSet (trusted-IP allowlist), publishing-destination changes, member disassociation (guardduty) | Kill or blind GuardDuty. (DisableGuardDutyDetector is not a real API - don't search for it.) |
StopConfigurationRecorder, DeleteConfigRule (config); DeleteLogGroup, DeleteLogStream (logs); DeleteAlarms (cloudwatch) | Remove config history, logs and alerting. |
DisableSecurityHub, DeleteMembers (securityhub) | Turn off the findings aggregator. |
DeactivateMFADevice, DeleteVirtualMFADevice (iam) | Weaken a target account's MFA. |
PutBucketLogging (off) (s3) | Stop S3 access logging before reading objects. |
| Exfiltration | |
GetObject (s3) - data event, off by default | Bulk object download. Without data events on, you only see it indirectly (S3 server access logs, bytes in Flow Logs). The most common reason an S3 exfil can't be proven. |
ModifySnapshotAttribute (ec2), ModifyDBSnapshotAttribute (rds), ModifyImageAttribute (ec2) | Share an EBS/RDS snapshot or AMI to an attacker account, then they copy it out. Emulated by Stratus aws.exfiltration.ec2-share-ebs-snapshot. |
PutBucketPolicy / PutBucketAcl allowing Principal:* or an external account; PutBucketReplication to an attacker bucket | Open a bucket or replicate it out continuously. |
SendEmail, SendRawEmail (ses) | Abuse the victim's SES (good deliverability from their domain) to phish downstream, and as a low-and-slow exfil channel. |
| Impact / destruction | |
DisableKey, ScheduleKeyDeletion (kms) | Destructive extortion - but reversible while it lasts: EnableKey and CancelKeyDeletion reverse it during the 7-30 day window (default 30). Permanent only after the window or if imported key material was destroyed - cancel immediately. |
S3 ransom via SSE-C (x-amz-server-side-encryption-customer-algorithm on PutObject/CopyObject) | Re-encrypt objects with an attacker-held key (Codefinger, 2025) - the data is intact but unreadable without their key. |
TerminateInstances, DeleteVolume, DeleteSnapshot (ec2), DeleteDBInstance/DeleteDBCluster (rds), DeleteBucket/DeleteObject (s3), DeleteStack (cloudformation) | Wipe compute, disks, databases, buckets and whole stacks - destructive/extortion. |
RunInstances (large or GPU types), RequestSpotInstances (ec2) | Spin up expensive instances for crypto-mining - watch for a sudden spike in Compute spend and unusual instance types/regions. |
Forensic acquisition in AWS
- EBS cross-account snapshot workflow: snapshot the instance's EBS volume(s). A snapshot encrypted with the default
aws/ebskey can't be shared - first copy it in the victim account re-encrypting with a customer-managed key and grant the IR accountkms:Decrypt/CreateGrant, thenModifySnapshotAttributeto share it; in the IR account copy and re-encrypt with an IR-owned key, create a volume and attach it read-only to the forensic instance (preserve hashes). Document every step for CoC. - Memory - snapshot the running instance via
CreateImagewithno-reboot=trueis not a memory capture; actually acquire RAM via SSM Agent + AVML/WinPmem pulled over Session Manager. - Lambda - pull the deployment package (
GetFunction→Code.Locationpresigned URL), check layers, environment vars, resource policy, invoke history via CloudTrail data events. - Containers - ECS: pull task definition revision, task role, image digest; EKS: audit log + control-plane logs + runtime (GD Runtime Monitoring / Falcon Cloud Workload Protection).
- IAM -
get-account-authorization-detailssnapshot (one JSON of entire IAM state). - Automate with IR tools:
aws_ir(ThreatResponse / Andrew Krug et al., deprecated but reference), Cado (Darktrace), Mitiga, Cloudtracker, Prowler, ScoutSuite, CloudSplaining.
Common AWS compromise patterns
- Leaked long-lived keys in public GitHub / Docker Hub → recon →
CreateAccessKeyon self →AssumeRoleprivesc → S3 bulk exfil → ransom note / Codefinger (SSE-C extortion). - SSRF on app (IMDSv1 or proxied PUT) → EC2 role creds → S3/EBS access.
- GitHub Actions misconfig (OIDC trust with
sub:*) →AssumeRoleWithWebIdentity→ account takeover. - Entra ID / Okta → federated SSO into AWS (SAML) → persistence by adding IAM user as fallback.
- SES abuse - attacker uses victim's SES to phish downstream customers (deliverability from victim domain).
- Codefinger (2025) - ransoming S3 via
x-amz-server-side-encryption-customer-algorithm(SSE-C) using attacker-held key.