A cryptominer called Denonia ran quietly inside AWS Lambda for months, disguised as legitimate function invocations, before anyone noticed the CPU bill looked wrong. By the time a Cado Security team went looking, the execution environment that had run the miner no longer existed. It had been recycled, reused for someone else’s function, and overwritten dozens of times over. What was left was a script embedded in the function code, three URLs, and a CloudTrail timestamp marking the first invocation. That is the entire evidentiary universe in a serverless breach: no disk to image, no memory to dump, just an event trail and whatever the attacker happened to leave in a place someone remembered to log.

cover

In brief

  • Serverless compute has no persistent server to seize, so investigations depend entirely on what was logged before the execution environment vanished.
  • AWS Lambda forensics centers on CloudTrail data events, CloudWatch Logs, and the function’s deployment package itself, since the runtime disappears within minutes of idling.
  • Azure Functions forensics relies on Application Insights, the Storage Account backing the Function App, and Activity Log entries, with similarly short retention windows unless explicitly extended.
  • MITRE ATT&CK formally tracks Serverless Execution (T1648) as an attacker technique, reflecting how common Lambda and Cloud Functions abuse has become for cryptomining and C2.
  • Automated forensic snapshotting, triggered the moment a suspicious invocation fires, is the only reliable way to capture a serverless execution environment before it disappears.
  • Cross-referencing IAM role assumptions with function invocation timing is often more revealing than anything found inside the function code.

Why there is nothing to seize

Traditional DFIR assumes persistence. A compromised server keeps a disk, a memory image, a set of running processes you can freeze in place. Serverless compute breaks that assumption on purpose, because the entire value proposition of Lambda and Azure Functions is that the platform manages the lifecycle for you. AWS spins up an execution environment on the first invocation, keeps it “warm” for a while to serve subsequent calls quickly, and then tears it down when it is no longer needed. That teardown can happen in minutes. Nothing about the platform is designed with evidence preservation in mind, because nothing about the platform is designed with you touching the underlying compute at all.

This is functionally similar to the volatility problem I described in container forensics, except worse: a container at least persists for the life of a task and can sometimes be paused or committed to an image mid-incident. A Lambda execution environment gives you no such courtesy. MITRE ATT&CK formalized this attack surface as Serverless Execution (T1648), explicitly calling out Lambda, Google Cloud Functions, and Azure Functions as documented vectors for creating malicious workflows that blend into legitimate automation traffic.

What actually survives in AWS Lambda

The practical forensic surface in Lambda breaks down into four sources, in descending order of reliability:

  • CloudTrail data events, which log every invocation, the IAM role assumed, the caller identity, and any API calls the function itself made to other AWS services. Data events for Lambda are not enabled by default and must be explicitly turned on in your CloudTrail trail’s data events configuration, which is the single most common reason investigators find nothing.
  • CloudWatch Logs, which capture whatever the function printed to stdout/stderr during execution, subject to the retention period configured on the log group (seven days by default in many accounts, which is not enough).
  • The deployment package, retrievable from the Lambda console or API as long as the function has not been deleted, giving you the actual code that ran, cryptominer binaries and all.
  • EBS/environment snapshots, if and only if an automated response pipeline captured one before teardown, since there is no manual “pause and image” option once execution completes.

A minimal CloudTrail query to establish an invocation timeline looks like this:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=Invoke \
  --start-time 2026-06-01T00:00:00Z \
  --end-time 2026-07-01T00:00:00Z \
  --query 'Events[].{Time:EventTime,User:Username,Resources:Resources}'

For newer Lambda durable functions, checkpoint operations add another layer worth pulling, since they record state transitions independently of the main invocation log:

{
  "eventName": "CheckpointDurableExecution",
  "eventSource": "lambda.amazonaws.com",
  "requestParameters": {
    "functionName": "myDurableFunction",
    "executionId": "exec-abc123",
    "stepId": "step-1"
  },
  "eventType": "AwsApiCall"
}

As documented in AWS’s security guidance for durable functions, that checkpoint trail matters more than it looks, because it can show you exactly which step of a multi-stage function executed and when, even after the underlying compute is gone.

The single most effective mitigation against losing all of this is automation that fires before an analyst is even paged. A published framework combining Lambda-triggered EBS snapshots, Velociraptor for live collection, and Timesketch for timeline analysis demonstrates the pattern well: GuardDuty detects the anomaly, EventBridge routes it, and a dedicated Lambda function immediately snapshots whatever forensic artifact still exists, well before a human opens a ticket.

What actually survives in Azure Functions

Azure Functions follows a broadly similar lifecycle with different naming and different default gaps. The practical sources here are:

  • Application Insights, which, if enabled on the Function App, captures request traces, dependency calls, and exceptions with configurable retention.
  • The backing Storage Account, since every Function App is tied to one for deployment packages, logs, and (for Durable Functions) the orchestration history table.
  • Azure Activity Log, which records control-plane operations like Function App creation, configuration changes, and role assignments, but not the content of individual invocations.
  • Diagnostic settings exported to a Log Analytics workspace, which is the only reliable way to get execution-level detail retained beyond the platform’s short default window. Microsoft documents the diagnostic settings configuration for Azure Functions in detail.

A query against Log Analytics to reconstruct execution timing looks like this in Kusto:

FunctionAppLogs
| where TimeGenerated between (datetime(2026-06-01) .. datetime(2026-07-01))
| where FunctionName == "SuspiciousFunction"
| project TimeGenerated, Level, Message, InvocationId
| order by TimeGenerated asc

The recurring failure mode is identical to Lambda: diagnostic settings were never configured, Application Insights was left disabled to save cost, and by the time someone asks “what did that function actually do,” the answer is nothing recoverable. Unlike Lambda, Azure gives you a slightly more durable record of orchestration state for Durable Functions through the storage account’s history table, which is worth checking even when everything else has expired.

Building the timeline without a server to anchor it

The methodology that works, across both platforms, mirrors what I described for cloud forensics and jurisdictional complexity: shift focus from the compute layer to correlating the control plane. IAM role assumptions, permission escalations, and unexpected outbound network connections triggered by a function invocation tell a more complete story than the function’s own logs ever will, because attackers control what a function logs about itself but rarely control the IAM and network telemetry surrounding it.

A useful triage sequence:

  1. Identify anomalous IAM role usage tied to a function’s execution role, not the function name itself, since roles get reused across deployments.
  2. Correlate invocation timestamps against known-good baselines to flag off-hours or unusually frequent executions, the same periodic-beaconing logic used in the Spyrtacus spyware investigation.
  3. Retrieve the deployment package or function code while it still exists, since deletion is often the attacker’s cleanup step and the retention window can be shorter than your approval process for evidence requests.
  4. Cross-reference outbound network destinations against threat intelligence, treating hardcoded URLs in function code with the same seriousness as C2 domains in a traditional malware sample.

The uncomfortable part and why this is also an offensive playbook

It is worth being blunt about something most vendor content glosses over: serverless platforms are attractive for command and control precisely because of the properties that make them hard to investigate. A SANS presentation on abusing AWS serverless technology for end-to-end C2 laid out the appeal explicitly, noting the combination of low operational overhead, trusted TLS certificates, and dynamic response to defender activity.

That symmetry should inform how security teams budget for this problem. If an attacker can stand up ephemeral, trusted-domain infrastructure in minutes, the defensive posture has to assume that any serverless function without explicit logging enabled is effectively a blind spot by design, not by accident. Enabling CloudTrail data events and Azure diagnostic settings by default, before deployment rather than after an incident, is the only fix that actually closes the gap described in this piece.

FAQ

Can you image a Lambda function like a traditional server? Not in the classic sense. There is no persistent disk to image; instead investigators reconstruct events from CloudTrail data events, execution logs, and, where enabled, snapshot the underlying execution environment before it is recycled.

What is the single most important log source for Lambda incident response? AWS CloudTrail data events for Lambda, since they capture invocation metadata, IAM context, and API calls made by the function, which is often the only record left after the execution environment is torn down.

Why is serverless forensics harder than container forensics? Containers at least persist for the duration of a task and can sometimes be paused or committed to an image; Lambda and Azure Functions execution environments are reused opportunistically and destroyed on a schedule you do not control, so the acquisition window is frequently measured in minutes, not hours.