18 min read

Evidence Acquisition

Collection

Evidence acquisition turns incident response activity into defensible facts. Collect the minimum data needed to answer the investigation question, preserve integrity with hashes and custody records, and avoid changing original evidence whenever possible.

Preserve Before You Poke

Capture volatile data and document system state before containment actions that may destroy evidence. Use write blockers for physical media and record who approved any live-response collection.

Collection Decision Tree

The decision is about where the question lives, not about which tool is convenient. Pick the row that matches what you need to answer, and collect the next one only if the first leaves the question open.

Live Host

  • Time, hostname, user sessions, network connections.
  • Process list, services, scheduled tasks, persistence clues.
  • Memory image when malware, credential theft, or fileless activity is suspected.

Disk or Cloud Volume

  • Snapshot or forensic image before rebuild.
  • Hash image and exported logs.
  • Preserve metadata, labels, and source identifiers.

SaaS / Identity

  • Export audit logs with UTC timestamps.
  • Record query filters and retention windows.
  • Store raw exports before enrichment.

Forensic Imaging and Verification

Image to a destination large enough for the full device, and hash as part of the acquisition rather than after it — a digest written by a separate pass proves less, and costs the same. Every command below assumes the source is behind a hardware write blocker.

evidence-acquisition.sh
# Linux disk imaging with dc3dd, including log and hashes
dc3dd if=/dev/sda of=/evidence/host01-disk.dd hash=sha256 log=/evidence/host01-dc3dd.log

# Standard dd fallback with error handling
dd if=/dev/sda of=/evidence/host01-disk.raw bs=64K conv=noerror,sync status=progress
sha256sum /evidence/host01-disk.raw > /evidence/host01-disk.raw.sha256

# E01 acquisition with libewf
ewfacquire /dev/sda -t /evidence/host01-disk -f encase6 -c best

# Mount image read-only for analysis
mkdir -p /mnt/evidence
mount -o ro,loop,noexec,noload /evidence/host01-disk.raw /mnt/evidence

# Hash and inventory collected artifacts
find /evidence -type f -print0 | sort -z | xargs -0 sha256sum > /evidence/SHA256SUMS.txt

conv=noerror,sync is a trade: it keeps an acquisition running across unreadable sectors and pads them, which means a bad source produces a readable image with zeroed holes. Note every unreadable sector in the manifest rather than letting the padding look like original content, and re-verify the digest before you mount.

Cloud and Identity Evidence

Most incidents now begin with a credential rather than an exploit, so the first evidence is not a disk. It is a record of an API call, a sign-in or a role assumption, written by a provider into storage you do not own. That changes the acquisition problem in three ways: nothing sits behind a write blocker, the artefact has a retention window measured in days, and the only way to get it out is a query whose result you must then prove you did not edit. Treat the export as the evidence and the console as a viewer, and treat the retention window as the deadline that outranks every other task in the first hour.

Source Default retention Answers Does not tell you
CloudTrail event history90 days, management events, per regionWhich principal called which API, from where, with what parametersData-plane reads such as S3 GetObject, and anything older than 90 days
CloudTrail trail in S3As long as the bucket keeps itThe same plus the data events you selected, with a digest chainEvent types the trail never selected, and objects an actor deleted
GuardDuty findings90 daysThat a detector fired on which resource, with what severityThe underlying activity itself; fetch the CloudTrail event it cites
Entra sign-in and audit logs7 days on Free, 30 days on P1 or P2Who authenticated, with what MFA and Conditional Access outcomeWhat the issued token was then used for
Purview unified audit log180 days; one year for four workloads on E5Mailbox access, inbox rules, consent grants, admin role changesWorkloads that do not emit to it, and anything past retention
Google Cloud Admin Activity400 days, always on, cannot be disabledConfiguration and IAM changes, with the calling principalData reads, unless Data Access logging was enabled first

AWS: Event History, Trails and Snapshots

Two records exist and they are not interchangeable. Event history is the API's own 90-day view of management events in one region, readable with lookup-events; it is what you use in the first hour, because trail delivery to S3 runs on a delay and the newest events are often not in the bucket yet. The trail is the durable record — one gzipped JSON object per file under AWSLogs/<account>/CloudTrail/<region>/<year>/<month>/<day>/ — and it is the only copy that carries a digest chain you can validate later.

cloud-evidence-aws.sh
# 1. Management events from the API's own 90-day history
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=CreateAccessKey \
  --start-time 2026-05-01T00:00:00Z --end-time 2026-05-27T00:00:00Z \
  --output json > ct-createaccesskey.json

# 2. Pull the delivered trail objects for the incident day, then verify the chain
aws s3 sync "s3://org-cloudtrail/AWSLogs/111122223333/CloudTrail/eu-west-1/2026/05/26/" ./trail-2026-05-26/
aws cloudtrail validate-logs \
  --trail-arn arn:aws:cloudtrail:eu-west-1:111122223333:trail/org-trail \
  --start-time 2026-05-26T00:00:00Z --end-time 2026-05-27T00:00:00Z

# 3. Snapshot the volume without touching the instance, then make it immutable
aws ec2 create-snapshot --volume-id vol-0a1b2c3d4e5f60718 \
  --description "IR-2026-042 host01 /dev/xvda" \
  --tag-specifications 'ResourceType=snapshot,Tags=[{Key=case,Value=IR-2026-042}]'
aws ec2 lock-snapshot --snapshot-id snap-0123456789abcdef0 \
  --lock-mode compliance --lock-duration 90

# 4. Copy into the forensics account, re-encrypted with that account's key
aws ec2 copy-snapshot --source-region eu-west-1 \
  --source-snapshot-id snap-0123456789abcdef0 \
  --encrypted --kms-key-id alias/forensics-evidence \
  --description "IR-2026-042 host01 copy" --region eu-central-1

lookup-events returns at most 50 events per call, and a truncated export that looks complete is the most common cloud acquisition error. Let the CLI paginator run, then record the page count and whether the last response still carried a token.

Digest validation proves the files in the bucket are the ones the trail wrote and that none was altered afterwards. It does not prove that nothing was removed, because the digest files live in the same bucket: an actor who deletes a log file and its digest together leaves no cryptographic trace. What catches that is an expectation that there is one object set per hour, so the bucket's own server access logging and the controls outside the API matter more than the digest chain. Build them before the incident: an organisation trail writing to a bucket in a separate account, S3 Object Lock in compliance mode on that bucket, and a policy that denies cloudtrail:StopLogging, cloudtrail:DeleteTrail and cloudtrail:UpdateTrail to every principal except the break-glass role.

A GuardDuty finding is a detection, not a record of activity: it is a derived assertion with a severity, a resource and a pointer to the events that triggered it, and it expires after 90 days. Export the finding JSON and keep it beside the raw CloudTrail events it cites — the pair is what survives review, because the finding alone proves only that a detector fired.

For a host volume, snapshot rather than attach. Attaching a volume to an analysis instance means detaching it from the source, and either way it changes the machine whose running state you were preserving. A snapshot is a crash-consistent, hypervisor-side copy taken without touching the guest. It captures no memory, no instance-store volume and no pending guest writes. Two key-management details decide whether it is usable: an encrypted volume needs kms:CreateGrant on the key for the EC2 service to take the snapshot at all, and a snapshot encrypted under the AWS-managed aws/ebs key cannot be shared across accounts — the copy must be re-encrypted with a customer-managed key in the destination account. Copy into an account the incident team controls, not the account under investigation, or the actor's own administrative rights reach the evidence.

Microsoft: Entra ID and the Unified Audit Log

Entra ID keeps audit and sign-in logs for seven days on the free tier and 30 days on P1 or P2, and upgrading the licence does not restore what already expired. Sign-in logs answer the authentication question — which account, which application, which address and network, whether MFA and Conditional Access allowed it — but not what the issued token was used for. That second question lives in the resource's own trail: Exchange mailbox audit, SharePoint, the Azure resource's activity log, or the Microsoft Graph activity log, which is not retained at all unless a diagnostic setting streams it somewhere.

entra-signin-export.ps1
Connect-MgGraph -Scopes 'AuditLog.Read.All','Directory.Read.All'

Get-MgAuditLogSignIn -All -Filter "createdDateTime ge 2026-05-01T00:00:00Z" |
  Select-Object CreatedDateTime, UserPrincipalName, AppDisplayName, IpAddress,
    ConditionalAccessStatus, @{n='Status';e={$_.Status.ErrorCode}} |
  ConvertTo-Json -Depth 4 |
  Out-File -Encoding utf8 entra-signins.json

The unified audit log is the other half, and it is where the actions that matter in a business email compromise appear: MailItemsAccessed, New-InboxRule, Add-MailboxPermission, application consent and role assignment. Microsoft announced in October 2026 that the Search-UnifiedAuditLog cmdlet retires on 15 October 2027, leaving the Graph audit log query API (asynchronous search jobs) as the only supported path. Until then, page the cmdlet with a fixed -SessionId, because that is what keeps the result set stable across calls, and remember the 5,000-record ceiling per call.

ual-export.ps1
Connect-ExchangeOnline
$session = 'IR-2026-042-ual-01'
$out = 'C:\evidence\ual-2026-05.json'

do {
  $page = Search-UnifiedAuditLog -StartDate 2026-05-01 -EndDate 2026-05-27 -SessionId $session -SessionCommand ReturnLargeSet -ResultSize 5000
  $page | Select-Object -ExpandProperty AuditData | Out-File -Append -Encoding utf8 $out
} while ($page.Count -eq 5000)

Do not treat the compliance portal's CSV export as the record. It truncates long AuditData values, and the truncated field is usually the operation's parameters — the part that says what was actually done. Pull objects as JSON and keep the raw response. Retention is 180 days for Audit (Standard); on E5 the Entra, Exchange, SharePoint and OneDrive records default to one year, other workloads stay at 180 days, and ten years needs the 10-year add-on plus a retention policy. Guest and non-E5 users get 180 days whatever the tenant's tier.

Google Cloud: Audit Logs and Sinks

Google Cloud splits audit logging by consequence. Admin Activity records configuration and IAM changes, is always on, and is held for 400 days in a required bucket that cannot be disabled or shortened. Data Access records reads and writes of the data itself, is off by default for almost every service, and sits in the default bucket for 30 days. The consequence is blunt: if nobody enabled Data Access logging before the incident, "who read this object" has no answer, and no post-incident work creates one.

gcp-audit-export.sh
# Admin Activity: IAM changes in one project over the last 30 days
gcloud logging read \
  'logName:"cloudaudit.googleapis.com%2Factivity" AND protoPayload.methodName:"SetIamPolicy"' \
  --project prod-payments --freshness 30d --format json > gcp-setiampolicy.json

# Data Access reads of a bucket, only if that log type was enabled beforehand
gcloud logging read \
  'logName:"cloudaudit.googleapis.com%2Fdata_access" AND resource.type="gcs_bucket"' \
  --project prod-payments --freshness 7d --format json > gcp-data-access.json

The durable copy is a log sink to Cloud Storage with a retention policy and bucket lock, or to BigQuery. Like every other provider here, the sink is not retroactive: it captures what happens after you create it. The principal is in protoPayload.authenticationInfo.principalEmail, and a service account's entry there is a fact about the account, not about the person who obtained its key.

The Export Is Not Self-Authenticating

A JSON or CSV export is an ordinary file. Nothing in it proves which tenant it came from, which query produced it, or that a row was not removed on the way to your evidence store, and an opposing expert is entitled to say so. The chain of custody for cloud evidence is therefore built from four things that must all be recorded: the hash taken at capture, the exact query and time window, the identity and role of whoever ran it, and a copy stored where the party under investigation cannot reach it.

  • Hash before you open it. Digest the export on the machine that received it, write the digest into the case record, and re-verify before every analysis pass.
  • Record the query, not the screenshot. A picture of a console proves a screen looked a certain way; the query text plus the response proves what was asked and what came back.
  • Keep the paging state. Page count, tokens and any throttle message are how a reviewer distinguishes an empty window from a truncated export.
  • Prefer provider-side immutability where it exists. CloudTrail digests in an Object Lock bucket, EBS snapshot lock in compliance mode, a locked Cloud Storage bucket behind the log sink, an immutable blob policy on the Azure diagnostic export, or a Purview retention policy. Each of these survives an administrator who would rather the record did not.
  • Move it out of the blast radius. The forensics account, subscription or tenant is a different trust boundary from the one under investigation; the same-account copy is a working copy, not the original.
cloud-export-manifest.yaml
evidence_id: EV-2026-0017
source: aws-cloudtrail
account_id: "111122223333"
region: eu-west-1
query: "lookup-events AttributeKey=EventName,AttributeValue=CreateAccessKey"
window_utc: 2026-05-01T00:00:00Z/2026-05-27T00:00:00Z
captured_utc: 2026-05-26T18:42:11Z
collector: analyst.name
role: SecurityAudit
api: cloudtrail:LookupEvents (max 50 per page, 3 pages, final token null)
sha256: <SHA256>
immutable_copy: s3://forensics-evidence/IR-2026-042/cloudtrail/ (Object Lock, compliance)

What these records do not prove is worth stating in the report rather than leaving to the reader. A clean sign-in log does not show an account was unused: tokens issued before a revocation remain valid until they expire, and their use appears in the resource's log, not the identity provider's. A CloudTrail entry proves an API call was accepted, not who was at the keyboard — the principal is a role or an access key, and sourceIPAddress is frequently an AWS service principal rather than a person. A finding proves a detector fired; its absence proves only that the detector did not fire.

Retention Is the Deadline

Every source above is deleting evidence while you read this. Sequence the first hour by expiry, not by importance: 7-day Entra logs and 30-day Data Access logs go before 90-day CloudTrail history, which goes before the trail bucket. The attack surface that generated these records is covered in Cloud Pentesting, and the merged timeline they feed is built on Timeline Analysis.

Mobile Device Evidence

Mobile acquisition is a support-matrix problem before it is a tooling problem. What you can get is decided by the device model, the operating-system build, whether the device has been unlocked since it last booted, and whether the data protection keys are available — not by which licence your team owns. Establish those four facts before you promise anyone a result.

Tier Yields Misses Requires
Manual reviewWhat is on screen: messages, call log, photosEverything deleted, everything hidden, every timestamp not displayedThe passcode and a camera to photograph the screen
LogicalBackup or agent-mediated extraction of app data, media and some databasesDeleted rows, caches, unallocated space, apps that opt out of backupAn unlocked device that trusts the host
FilesystemThe full data partition, including deleted SQLite rowsKeychain material held in hardware, and anything already overwrittenRoot, a jailbreak, or a supported agent exploit
PhysicalThe whole NAND image, keyspace includedNothing in the storage itselfA lab with per-build exploit support

iOS

A logical backup is the realistic path without a jailbreak, and an encrypted backup is worth far more than an unencrypted one: it carries Keychain items, Health data, call history and the per-app databases that the unencrypted path omits. The backup directory is not opaque — it holds a Manifest.db SQLite database that maps each stored file to its domain and relative path, with the file's identifier being the SHA-1 of that path. That identifier is a naming convention, not an integrity check: it proves the file is where the backup put it, not that the file is unaltered.

ios-logical-acquisition.sh
idevice_id -l
ideviceinfo -u <UDID> -k ProductVersion
ideviceinfo -u <UDID> -k BuildVersion

# Encryption must be on before the backup, or the keychain never comes across
idevicebackup2 -u <UDID> encryption on
idevicebackup2 -u <UDID> backup --full /evidence/ios-alice

# Crash and diagnostic logs, including sysdiagnose output
idevicecrashreport -u <UDID> -e /evidence/ios-alice-crash

Lock state decides everything. A device that has not been unlocked since it booted — before first unlock — has its data-protection keys unavailable, and the same extraction that works on an unlocked device returns metadata and nothing else. Apple's inactivity reboot, introduced in iOS 18.1, returns an idle device to that state after roughly three days without an unlock, which is why "leave it powered off in a bag" and "keep it unlocked and charging" are different evidence strategies rather than the same one. Neither is safe to improvise: ask the lab which state its tool needs before you touch the device.

Android

Android's honest default is a bug report plus the shared storage, which answers configuration and crash questions and says almost nothing about app data. adb backup is deprecated and, on modern builds, mostly returns nothing: apps targeting recent API levels opt out by default, and the transport no longer carries what it once did. File-based encryption means a device locked before first unlock cannot be read through adb at all, whatever the host is authorised to do.

android-logical-acquisition.sh
adb devices -l
adb shell getprop ro.product.model
adb shell getprop ro.build.version.release
adb shell getprop ro.crypto.state

# One archive: dumpsys, logcat, props, package list and system state
adb bugreport /evidence/bugreport-alice.zip

# Shared storage only - not app-private data
adb pull /sdcard/DCIM /evidence/alice-dcim

A bug report is a zip of text, and it is genuinely useful: package inventory, battery and network history, recent crashes and the full property set. It is not app data, and it will not contain the message the case is about. Full filesystem extraction needs root, an unlocked bootloader with a wiped device (which destroys the evidence), or a commercial agent that lists the exact model and build in its support matrix.

Consent, Preservation and Escalation

A phone is the most personal artefact in the estate, so authority has to be explicit: an acceptable-use policy and a written authorisation for a corporate-owned device, the owner's informed consent for a personal one, and counsel's decision whenever the device may hold privileged or third-party data. Record the consent in the case file, not in a chat thread.

Preserve by hashing the whole extraction directory and by recording what the device was doing when you started: power state, lock state, network state, and whether you changed any of them. If the device is on and connected, a remote wipe or a remote lock is a live possibility, which is the argument for a shielded bag; putting it in airplane mode is a change to the device and belongs in the notes. Then escalate rather than improvise when any of these is true: the device is before first unlock and the model is current, the encrypted-backup password is unknown, the question is about app-private data, proceedings are likely, or the tool's support matrix does not list the exact build. A lab that says "not supported on that build" is a better outcome than an extraction you cannot defend.

The application layer these extractions expose — containers, keychains, local storage, IPC — is documented from the attacker's side in iOS Testing and Android Testing, which is worth reading before you interpret what you found.

Evidence Manifest Fields

The manifest is the machine-readable half of the custody record; the signing log from DFIR Fundamentals is the human half. Keep the field names stable across cases so exports, dashboards, and review tooling can consume them without a mapping layer.

evidence-manifest.yaml
evidence_id: EV-2026-0001
case_id: IR-2026-042
source_system: host01.example.local
source_owner: endpoint-team
collection_type: disk_image
collector: analyst.name
approval: incident-commander.name
start_time_utc: 2026-05-26T18:05:00Z
end_time_utc: 2026-05-26T19:10:00Z
hash_sha256: <SHA256>
storage_location: encrypted-evidence-vault/path
handling_notes:
  - acquired using write blocker
  - original media returned to custodian
  - analysis performed on verified copy

Two fields are load-bearing and routinely omitted: approval, without which the collection has no authority, and handling_notes, which is where an unreadable sector, a clock offset, or a skipped partition gets recorded. A manifest that only records what went right will not survive review.

Chain of Custody

  • Unique evidence ID: Label physical media, images, exported logs, screenshots, and notes.
  • Collector and custodian: Record names, roles, contact details, and transfer times.
  • Integrity: Use SHA-256 for every file and re-verify before analysis and before delivery.
  • Storage: Encrypt evidence at rest with access limited to the case team.
  • Retention: Define deletion or legal hold requirements before closing the incident.

The transfer log itself is the part auditors read line by line: every handover needs a from, a to, a reason, and a signature, and the original never leaves controlled storage while analysis runs against the verified copy.