Author: golde

log in with credential user that can read only. Export creds, validate session.

Run: aws cloudtrail lookup-events —max-results 50 > events.json read events.json and look for the flags.

For more notes hit me up.

Description

Stonepass’s surviving CloudTrail history is the only record of activity surrounding the copied stamp. You hold read-only investigator access. Reconstruct the final recorded actions and determine which identity stopped the trail from logging.

Answers: 8/8 investigation questions solved — see Findings below.

TL;DR

We’re handed read-only stonepass-investigator credentials against a CloudTrail-compatible API. Rather than digging through an S3-exported trail, everything needed is still sitting in the account’s 90-day default event history, queryable directly with aws cloudtrail lookup-events. First describe-trails / get-trail-status confirm a trail exists and is no longer logging. From there, lookup-events filtered by EventName (StopLogging, DeleteTrail, GetTrailStatus, ListAccessKeys, etc.) and by time reconstructs the full sequence: an internal session enumerating access keys, an attacker session from an external IP probing the trail and its S3 log bucket, a denied DeleteTrail attempt, and finally the successful StopLogging call that killed the audit trail.

Solution

1. Confirming access

Credentials for a read-only investigator role were provided:

export AWS_ENDPOINT_URL=http://<challenge-ip>:<aws-port>
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=AKIA0CYGKIFQA151DE8Z
export AWS_SECRET_ACCESS_KEY=D+yx2V+cpv6bSjzvN91xFCWRDgYS5oZR4ugjWurX
unset AWS_SESSION_TOKEN
 
aws sts get-caller-identity
{
    "UserId": "AIDAK9A2MBNKBOEA7ZYB",
    "Account": "491827305948",
    "Arn": "arn:aws:iam::491827305948:user/stonepass-investigator"
}

Confirmed: this identity is stonepass-investigator, scoped for read-only CloudTrail investigation.

2. Background: how CloudTrail delivers events

Two relevant delivery mechanisms, and the distinction matters for this challenge:

  • Event history (default, always-on, 90 days) — every AWS account automatically retains the last 90 days of management events, queryable directly via API/CLI, no setup required.
  • Trails (opt-in, configured) — a Trail continuously exports events to an S3 bucket (and optionally CloudWatch Logs) for long-term retention beyond 90 days. StopLogging / DeleteTrail / UpdateTrail only affect this export — they don’t erase the underlying 90-day history.

Since we only have read-only access and no guarantee of S3 access to the export bucket, the play is to query the event history API directly rather than chase the S3-exported trail.

3. Checking the trail’s state

aws cloudtrail describe-trails
aws cloudtrail get-trail-status --name <trail-name>

get-trail-status shows whether the trail IsLogging (true/false) and the timestamp of its last delivery — confirming the trail had, in fact, stopped logging, and roughly when.

4. Reconstructing the incident from event history

The core tool for the whole investigation is:

aws cloudtrail lookup-events \
  [--lookup-attributes AttributeKey=X,AttributeValue=Y] \
  [--start-time] [--end-time] [--max-results]

This queries the 90-day event history directly — no S3 bucket access required. Valid AttributeKey values include EventName, Username, ResourceName, EventSource, AccessKeyId, and more.

Start broad, dump everything to a file for review:

aws cloudtrail lookup-events --max-results 50 > events.json

Then narrow down with targeted filters once specific API calls are of interest, e.g.:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=StopLogging

Repeating this pattern for the other relevant event names (GetTrailStatus, DeleteTrail, ListAccessKeys, etc.) and cross-referencing eventTime, sourceIPAddress, and userIdentity across the results makes it possible to lay out the full timeline: who did what, from where, and in what order — including which calls succeeded and which were denied.

Findings

Reconstructing the timeline from the event history answered all eight investigation questions:

#QuestionAnswer
1API action used to disable the audit trailStopLogging
2IAM username whose credentials disabled the trailstonepass-warden
3IP address the trail was disabled from192.0.2.55
4Name of the CloudTrail trail that was stoppedstonepass-audit-trail
5First CloudTrail API action called from the attacker IPGetTrailStatus
6API action attempted and explicitly denied before the trail was stoppedDeleteTrail
7S3 bucket enumerated by the attacker before stopping the trailstonepass-audit-trail-logs
8Last API action by the compromised user from the internal IP, immediately before the attacker session beganListAccessKeys (internal IP 10.30.41.118)

Takeaways

  • The 90-day default event history is queryable purely via the CloudTrail API — no S3 or CloudWatch Logs access is needed to reconstruct an incident, which matters a lot when working from a read-only role.
  • StopLogging/DeleteTrail stop the S3 export, they don’t erase what already happened — the account’s own event history is effectively the audit trail’s alibi.
  • Filtering lookup-events by EventName one call at a time, then correlating on sourceIPAddress and userIdentity, is enough to walk an attacker’s session end-to-end: reconnaissance (GetTrailStatus, S3 enumeration) → a denied destructive attempt (DeleteTrail) → the successful cover-up call (StopLogging).
  • A compromised internal session’s last action (ListAccessKeys here) is often the pivot point worth flagging first — it’s usually how the attacker got the credentials used in the following external session.