T1610

Deploy Container

Defense Evasion Execution Last updated:

This detection identifies adversaries deploying containers with dangerous configurations to execute malicious payloads or escape defense controls. The detection monitors container runtime CLI invocations (docker, kubectl, podman, crictl) for high-risk flags such as --privileged, --net=host, --pid=host, and host filesystem volume mounts that are commonly abused by threat actors such as TeamTNT, Kinsing, and Doki to achieve container escape, cryptomining, and lateral movement. Risk scoring prioritizes privileged and host-mount combinations that enable direct node access in Kubernetes environments.

What is T1610 Deploy Container?

Deploy Container (T1610) maps to the Defense Evasion and Execution tactics — the adversary is trying to avoid being detected in MITRE ATT&CK.

This page provides production-ready detection logic for Deploy Container, covering the data sources and telemetry it touches: Microsoft Defender for Endpoint. The queries below are rated high severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.

MITRE ATT&CK

Tactic
Defense Evasion Execution
Technique
T1610 Deploy Container
Canonical reference
https://attack.mitre.org/techniques/T1610/
Microsoft Sentinel / Defender
kusto
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where FileName in~ ("docker", "kubectl", "podman", "nerdctl", "crictl", "ctr")
| where ProcessCommandLine has_any ("run ", "create ", "apply ", "exec ")
| extend IsPrivileged = ProcessCommandLine has "--privileged"
| extend IsHostNet = ProcessCommandLine has "--net=host" or ProcessCommandLine has "--network=host"
| extend IsHostPid = ProcessCommandLine has "--pid=host"
| extend IsHostIpc = ProcessCommandLine has "--ipc=host"
| extend IsHostMount = ProcessCommandLine has_any ("-v /:/", "--volume /:/", "-v /proc", "--volume /proc", "-v /sys", "-v /dev", "--volume /dev")
| extend HasCapAdd = ProcessCommandLine has "--cap-add=SYS_ADMIN" or ProcessCommandLine has "--cap-add=ALL" or ProcessCommandLine has "--cap-add NET_ADMIN"
| extend NoSeccomp = ProcessCommandLine has "seccomp=unconfined" or ProcessCommandLine has "apparmor=unconfined"
| extend HasEnvSecret = ProcessCommandLine has_any ("-e AWS_", "-e KUBECONFIG", "-e TOKEN", "--env AWS_", "--env TOKEN")
| extend RiskScore = (toint(IsPrivileged) * 40)
    + (toint(IsHostNet) * 20)
    + (toint(IsHostPid) * 25)
    + (toint(IsHostIpc) * 15)
    + (toint(IsHostMount) * 40)
    + (toint(HasCapAdd) * 20)
    + (toint(NoSeccomp) * 10)
    + (toint(HasEnvSecret) * 15)
| where RiskScore >= 20
| extend ContainerImage = extract(@"(?:run|create)\s+(?:--?[\w=:\-]+\s+)*([\w./:@-]+)", 1, ProcessCommandLine)
| extend SuspiciousFlags = strcat(
    iff(IsPrivileged, "[PRIVILEGED] ", ""),
    iff(IsHostNet, "[HOST_NET] ", ""),
    iff(IsHostPid, "[HOST_PID] ", ""),
    iff(IsHostMount, "[HOST_MOUNT] ", ""),
    iff(HasCapAdd, "[CAP_ADD] ", ""),
    iff(NoSeccomp, "[NO_SECCOMP] ", "")
  )
| project
    TimeGenerated,
    DeviceName,
    AccountName,
    AccountDomain,
    FileName,
    ProcessCommandLine,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine,
    InitiatingProcessAccountName,
    ContainerImage,
    SuspiciousFlags,
    RiskScore
| sort by RiskScore desc, TimeGenerated desc

Detects container deployment commands (docker, kubectl, podman) with high-risk flags associated with privilege escalation and container escape: --privileged, host namespace sharing (--net=host, --pid=host, --ipc=host), root filesystem volume mounts (-v /:/), dangerous capability additions (SYS_ADMIN, ALL), and disabled security profiles. A risk score is calculated to prioritize the most dangerous combinations. Triggers on both Docker and Kubernetes CLI invocations.

high severity medium confidence

Data Sources

Microsoft Defender for Endpoint

Required Tables

DeviceProcessEvents

False Positives

  • Legitimate container infrastructure teams running privileged containers for monitoring agents (e.g., Datadog, Falco, Sysdig) that require host-level access
  • Kubernetes node-level tooling such as DaemonSets for log collection (Fluentd, Filebeat) that mount /var/log or /proc on the host
  • CI/CD pipelines (Jenkins, GitLab Runner, GitHub Actions self-hosted) that use docker-in-docker (DinD) with --privileged to build container images
  • Authorized security tooling like vulnerability scanners (Trivy, Anchore) that inspect host filesystems
  • Container runtime health checks by orchestration platforms that invoke crictl or ctr with management subcommands

Sigma rule & cross-platform mapping

The detection logic for Deploy Container (T1610) above is provided in a vendor-neutral form so you can deploy it on any SIEM. The same logic is shipped here as native KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the following logsource:

logsource:
  category: process_creation
  product: windows

Browse the community-maintained Sigma rules for this technique:


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Deploy Privileged Container with Host Filesystem Mount

    Expected signal: Sysmon EventCode=1: Image=docker, CommandLine contains '--privileged' and '-v /:/host'. Follow-up EventCode=1 for 'docker exec' accessing /host/etc/passwd. Linux auditd EXECVE record for docker invocation.

  2. Test 2Deploy Container with Host Network and PID Namespace

    Expected signal: Sysmon EventCode=1: CommandLine contains '--net=host --pid=host'. DeviceNetworkEvents will show container traffic attributed to host network interface rather than docker0 bridge. Docker daemon log records container creation with HostConfig.NetworkMode=host.

  3. Test 3Deploy Privileged Pod via kubectl with hostPath Mount

    Expected signal: Sysmon EventCode=1: Image=kubectl, CommandLine='kubectl apply -f /tmp/atomic-t1610-pod.yaml'. Kubernetes API server audit log: CREATE verb on pods resource by current user with pod spec containing securityContext.privileged=true and hostPath volume. Second EventCode=1 for 'kubectl exec' access.


Response Playbook

Triage

  1. Step 1: Identify the container image being deployed — extract the image name from ProcessCommandLine. Search the organization's approved image registry (ECR, ACR, Docker Hub org) to determine if this image is authorized. Unknown or external images (e.g., docker.io/teamtnt/*, xmrig/*) are immediate escalation triggers.
  2. Step 2: Identify the account and process that executed the container command. Determine if AccountName is a service account, CI/CD runner, or interactive user. Verify whether this account has documented authorization to deploy containers in this environment via your RBAC records or change management system.
  3. Step 3: Inspect the full ProcessCommandLine for the combination of dangerous flags: --privileged with -v /:/ is the highest-severity pattern (direct host root access). --pid=host combined with --privileged allows ptrace across all host processes. Document each flag present.
  4. Step 4: Check InitiatingProcessFileName — if the container command was launched by a web server process (nginx, apache2, php-fpm, node, python) rather than a shell (bash, sh, zsh) or expected automation tool, this indicates possible remote code execution exploiting a web application vulnerability.
  5. Step 5: Examine DeviceNetworkEvents for the container host within 5 minutes before and after the deployment event. Look for outbound connections to non-standard ports (4444, 8888, 31337), known mining pools (xmr.pool, monero.hashvault.pro, nanopool.org), or pastebin-style services (pastebin.com, hastebin.com) that may indicate payload download.
  6. Step 6: Query DeviceProcessEvents for child processes spawned within the container runtime (containerd-shim, runc, kata-runtime) in the 10 minutes following deployment. Unexpected shells (bash, sh with -i flag), cryptocurrency miners (xmrig, minerd, cgminer), or network scanners (nmap, masscan) confirm malicious intent.
  7. Step 7: For Kubernetes environments, retrieve the pod specification via kubectl get pod <name> -o yaml and review securityContext.privileged, hostNetwork, hostPID, hostIPC, and volumes for hostPath mounts. Compare against your organization's PodSecurityPolicy or OPA/Gatekeeper policies to confirm policy bypass.

Containment

  1. If malicious container confirmed: immediately stop the container with 'docker stop <container_id>' or 'kubectl delete pod <pod_name> --grace-period=0 --force'. Do NOT use 'docker rm' yet — preserve the container filesystem for forensics.
  2. Revoke or suspend the account credentials used to deploy the container. If a service account token or kubectl context was used, rotate the token immediately and audit all API calls made with that token in the past 24 hours via Kubernetes API server audit logs.
  3. If the container used --privileged or mounted the host filesystem, treat the underlying host node as potentially compromised. Cordon the Kubernetes node ('kubectl cordon <node>') to prevent new pod scheduling, then initiate host-level IR process.
  4. Block the container image digest/tag at the registry level (ECR lifecycle policy, Docker Hub organization policy) to prevent re-deployment. If the image was pulled from an external registry, add the registry domain to your network deny list.
  5. Capture all running processes inside the container before stopping: 'docker top <container_id>' or 'kubectl exec <pod> -- ps aux'. Document any active cryptocurrency miner processes, reverse shells, or data exfiltration tools.
  6. If TeamTNT or similar cryptojacking group is suspected, scan all other nodes in the cluster for similar container deployments: 'docker ps -a --filter ancestor=<malicious_image>' across all nodes.

Evidence Collection

  1. Export the container filesystem layer for malware analysis: 'docker export <container_id> > container_evidence_$(date +%Y%m%d).tar'. This captures all files written during container execution.
  2. Collect the container runtime log: 'docker logs <container_id> > container_logs_$(date +%Y%m%d).txt'. For Kubernetes: 'kubectl logs <pod_name> --previous > pod_logs.txt' (--previous retrieves logs from crashed/restarted containers).
  3. Extract container metadata and full configuration: 'docker inspect <container_id> > container_inspect.json'. This includes all mount points, environment variables (may contain stolen credentials or miner config), network settings, and the entrypoint command.
  4. For Kubernetes: export the API server audit log for the deployment time window. Audit logs record who created the pod, from which IP, and what RBAC roles authorized the action. Location varies by distribution (typically /var/log/apiserver/audit.log or via cloud provider logging).
  5. Collect host-level artifacts if --privileged or hostPath mounts were used: /proc/net/tcp (active connections at time of compromise), /var/log/auth.log (authentication events), and crontab entries (cryptominers often install persistence via cron).
  6. Network capture: if container is still running, capture outbound traffic with 'tcpdump -i any -w container_net_$(date +%Y%m%d).pcap dst net not 10.0.0.0/8 and dst net not 172.16.0.0/12' to document C2 or mining pool communication.
  7. Collect RBAC audit trail: enumerate who has 'pods/create' or 'deployments/create' permissions in the namespace where the malicious container was deployed using 'kubectl auth can-i --list --namespace <ns>' for each service account.

Escalation Criteria

  • ! Escalate immediately if the deployed container successfully mounted the host root filesystem (/-v /:/host) — this constitutes a confirmed container escape with full host filesystem access, a P1 incident.
  • ! Escalate if the account used to deploy the container belongs to a CI/CD service account, as this indicates supply chain compromise or pipeline injection affecting all repositories that service account has access to.
  • ! Escalate if child processes within the container executed lateral movement tools (ssh with new key material, kubectl with --kubeconfig pointing to newly created credentials, curl/wget downloading additional payloads to other hosts).
  • ! Escalate if the same malicious container image was deployed across multiple nodes or namespaces, indicating automated worm-like spread (TeamTNT DaemonSet abuse pattern).
  • ! Escalate if environment variables in the container include cloud provider credential patterns (AWS_ACCESS_KEY_ID, AZURE_CLIENT_SECRET, GOOGLE_APPLICATION_CREDENTIALS) — these may enable cloud-scope lateral movement far beyond the initial container.
  • ! Escalate if any active cryptocurrency miner is confirmed — Kinsing and Doki operators are known to install persistent cron-based redeployment scripts on the host that survive container deletion.

Investigation Guide

Forensic Artifacts

  • > Docker daemon log: /var/log/docker.log or journalctl -u docker — records all container create/start/stop events with image names and runtime parameters
  • > Container filesystem layers: /var/lib/docker/overlay2/<layer_id>/ — contains all files written during container execution, including downloaded malware binaries
  • > Kubernetes API server audit log: records pod creation with requesting user, source IP, pod spec, and RBAC authorization decision
  • > Linux audit log: /var/log/audit/audit.log — auditd EXECVE records for docker/kubectl invocations if audit rules are configured for container binaries
  • > cgroup hierarchy: /sys/fs/cgroup/memory/docker/<container_id>/ — confirms which processes belong to a specific container at the OS level
  • > Container runtime state files: /var/run/docker/runtime-runc/moby/<container_id>/state.json — contains full container configuration at runtime
  • > Network namespace entries: /var/run/docker/netns/ — each container network namespace, inspectable with 'ip netns exec <ns> ss -tulnp'
  • > /proc/<pid>/net/tcp, /proc/<pid>/environ — for container PID on host, reveals environment variables (including injected credentials) and active TCP connections
  • > Kubernetes etcd: stores full desired state including all created pods; forensic etcd dump may reveal deleted malicious pods that were removed to cover tracks

Tuning Guidance

Start by building an allowlist of approved container images from your internal registry (ECR account IDs, ACR registry FQDNs, approved Docker Hub organizations). Filter ProcessCommandLine to exclude commands where the image name matches this allowlist and the initiating process is a known CI/CD tool (jenkins, gitlab-runner, github-actions-runner). For Kubernetes environments, create exclusions for namespace-scoped service accounts that are documented to deploy privileged infrastructure components (monitoring, storage, networking). Raise the risk score threshold from 20 to 40 if your environment has many legitimate privileged containers — this reduces noise while still catching the most dangerous combinations (privileged + host mount). Consider reducing severity for kubectl commands run during business hours by known SRE accounts while keeping high severity for off-hours or unknown accounts. If DaemonSets routinely deploy privileged agents, exclude the specific image hashes (not tags, which can be hijacked) from detection.


Hunting Queries

Hunt for container deployment commands spawned by web server or scripting engine processes — a strong indicator of RCE exploitation (e.g., Log4Shell, PHP webshell, Django deserialization) leading to adversary-controlled container deployment.

Hunting — KQL
kql
// Hunt for containers launched by non-interactive processes (web servers, scripting engines) suggesting RCE-to-container-deployment pivot
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("docker", "kubectl", "podman")
| where ProcessCommandLine has_any ("run ", "create ")
| where InitiatingProcessFileName in~ (
    "nginx", "apache2", "httpd", "php", "php-fpm", "php-cgi",
    "python", "python3", "ruby", "node", "java", "tomcat",
    "perl", "gunicorn", "uwsgi"
  )
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by TimeGenerated desc
Hunting — SPL
spl
index=* sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
| eval proc=lower(Image)
| eval parent=lower(ParentImage)
| where match(proc, "(docker|kubectl|podman)$")
| where match(CommandLine, "(run|create)\s")
| where match(parent, "(nginx|apache|httpd|php|python|node|java|ruby|perl|gunicorn|uwsgi)")
| table _time, host, user, Image, CommandLine, ParentImage, ParentCommandLine
| sort - _time

Hunt for cryptocurrency mining pool connections originating from container host nodes within 10 minutes of a container deployment event — confirms Kinsing, TeamTNT, or Doki-style cryptojacking campaigns.

Hunting — KQL
kql
// Hunt for network connections to known cryptomining pools or C2 infrastructure from container host nodes within 10 minutes of a container deployment
let containerDeployments = DeviceProcessEvents
    | where TimeGenerated > ago(7d)
    | where FileName in~ ("docker", "kubectl", "podman")
    | where ProcessCommandLine has_any ("run ", "create ")
    | project DeployTime = TimeGenerated, DeviceName, AccountName, ProcessCommandLine;
let miningPools = dynamic([
    "xmr.pool", "monero.hashvault", "nanopool.org", "minergate.com",
    "f2pool.com", "antpool.com", "slushpool.com", "nicehash.com",
    "supportxmr.com", "hashvault.pro", "xmrpool.eu"
]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemoteUrl has_any (miningPools) or RemotePort in (3333, 4444, 5555, 7777, 8888, 14444, 45560)
| join kind=inner containerDeployments on DeviceName
| where TimeGenerated between (DeployTime .. (DeployTime + 10m))
| project TimeGenerated, DeviceName, AccountName, RemoteIP, RemoteUrl, RemotePort, ProcessCommandLine
| sort by TimeGenerated desc
Hunting — SPL
spl
index=* sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=3
| eval dest_domain=lower(DestinationHostname)
| where match(dest_domain, "(xmr\.pool|hashvault|nanopool|minergate|f2pool|nicehash|supportxmr|xmrpool)")
    OR DestinationPort IN (3333, 4444, 5555, 7777, 8888, 14444, 45560)
| join host [
    search index=* sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
    | where match(Image, "(docker|kubectl|podman)$")
    | where match(CommandLine, "(run|create)\s")
    | table _time AS deploy_time, host
  ]
| where _time >= deploy_time AND _time <= deploy_time+600
| table _time, host, DestinationIp, DestinationHostname, DestinationPort
| sort - _time

Hunt for hosts with unusually high container deployment frequency (5+ deployments per hour) that may indicate automated worm propagation, DaemonSet abuse, or scripted mass deployment campaigns characteristic of TeamTNT infrastructure takeovers.

Hunting — KQL
kql
// Hunt for unusual volume of container deployments suggesting automated worm propagation (TeamTNT DaemonSet pattern)
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where FileName in~ ("docker", "kubectl", "podman")
| where ProcessCommandLine has_any ("run ", "create ")
| summarize
    DeploymentCount = count(),
    UniqueImages = dcount(tostring(extract(@"(?:run|create)\s+(?:--?[\w=:\-]+\s+)*([\w./:@-]+)", 1, ProcessCommandLine))),
    CommandSamples = make_set(ProcessCommandLine, 3),
    Accounts = make_set(AccountName, 5)
    by DeviceName, bin(TimeGenerated, 1h)
| where DeploymentCount >= 5
| sort by DeploymentCount desc
Hunting — SPL
spl
index=* sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
| where match(Image, "(docker|kubectl|podman)$")
| where match(CommandLine, "(run|create)\s")
| bin _time span=1h
| stats count AS deploy_count, dc(user) AS unique_users, values(CommandLine) AS command_samples by host, _time
| where deploy_count >= 5
| sort - deploy_count

Atomic Red Team Tests

Test 1 Deploy Privileged Container with Host Filesystem Mount
linux

Simulates TeamTNT/Kinsing attack pattern by deploying a privileged container with the host root filesystem mounted, enabling full host access from within the container. This is the canonical container escape setup.

Command

bash
docker run -d --privileged --name atomic-test-t1610-priv -v /:/host alpine:latest sh -c 'sleep 60' && echo 'Container deployed. Verifying host filesystem access:' && docker exec atomic-test-t1610-priv ls /host/etc/passwd

Cleanup

bash
docker stop atomic-test-t1610-priv && docker rm atomic-test-t1610-priv

Expected Telemetry

Sysmon EventCode=1: Image=docker, CommandLine contains '--privileged' and '-v /:/host'. Follow-up EventCode=1 for 'docker exec' accessing /host/etc/passwd. Linux auditd EXECVE record for docker invocation.

Expected Detection

Alert fires with RiskScore >= 80 (--privileged = 40 + host mount = 40). ContainerImage='alpine:latest'. SuspiciousFlags=[PRIVILEGED][HOST_MOUNT].

Test 2 Deploy Container with Host Network and PID Namespace
linux

Deploys a container sharing the host network stack and PID namespace, enabling the container to see and interact with all host processes and network connections — a common technique for network sniffing and process injection.

Command

bash
docker run -d --net=host --pid=host --name atomic-test-t1610-ns alpine:latest sh -c 'sleep 60' && echo 'Container deployed. Verifying host process visibility:' && docker exec atomic-test-t1610-ns ps aux | head -20

Cleanup

bash
docker stop atomic-test-t1610-ns && docker rm atomic-test-t1610-ns

Expected Telemetry

Sysmon EventCode=1: CommandLine contains '--net=host --pid=host'. DeviceNetworkEvents will show container traffic attributed to host network interface rather than docker0 bridge. Docker daemon log records container creation with HostConfig.NetworkMode=host.

Expected Detection

Alert fires with RiskScore >= 45 (host_net=20 + host_pid=25). SuspiciousFlags=[HOST_NET][HOST_PID]. Severity=high.

Test 3 Deploy Privileged Pod via kubectl with hostPath Mount
linux

Simulates Peirates-style Kubernetes attack by deploying a privileged pod that mounts the node's root filesystem, enabling node escape. Uses kubectl apply with an inline YAML manifest defining a privileged container.

Command

bash
cat <<'EOF' > /tmp/atomic-t1610-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: atomic-test-t1610-k8s
  namespace: default
spec:
  containers:
  - name: pwned
    image: alpine:latest
    command: ["sh", "-c", "sleep 60"]
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /
EOF
kubectl apply -f /tmp/atomic-t1610-pod.yaml && kubectl wait --for=condition=Ready pod/atomic-test-t1610-k8s --timeout=60s && kubectl exec atomic-test-t1610-k8s -- ls /host/etc/

Cleanup

bash
kubectl delete pod atomic-test-t1610-k8s --grace-period=0 --force && rm /tmp/atomic-t1610-pod.yaml

Expected Telemetry

Sysmon EventCode=1: Image=kubectl, CommandLine='kubectl apply -f /tmp/atomic-t1610-pod.yaml'. Kubernetes API server audit log: CREATE verb on pods resource by current user with pod spec containing securityContext.privileged=true and hostPath volume. Second EventCode=1 for 'kubectl exec' access.

Expected Detection

Alert fires on kubectl apply command. Follow-up Kubernetes audit detection should fire on API server audit log showing privileged pod creation. KubeAudit or Falco rule 'Launch Privileged Container' should also fire.

Related Detections