Introduction
During a DFIR engagement I worked on recently, I came across a technique that genuinely caught me off guard, not because it was a zero-day or sophisticated custom malware, but because of how cleanly the attacker chained two things together: (Container Escape T1611) and Tailscale as a C2 channel.
No custom malware. No CVE exploitation. Just Docker's own features, a legitimate VPN tool, and scripts. By the time we got the investigation handover, the attacker had persistent root-level access to a fleet of edge devices and had been sitting quietly inside the network for weeks.
This is the full technical breakdown of what I found, how I found it, and what it means for anyone running containerized infrastructure.
Environment Context
The target environment was a Portainer CE instance, a popular Docker management web UI. Running on a cloud VM and managing a large fleet of edge devices deployed across multiple physical locations. Portainer gives you a browser-based interface to deploy containers, manage stacks, execute commands inside containers, and push deployments to remote edge agents.
The attacker's entry point was a leaked credential belonging to a former employee whose account had never been deactivated. No brute force. No phishing. Direct Valid login into the Portainer web UI and with that, full admin access to the entire container fleet.
Phase 1 — The Initial Escape: systmd
The attacker's first move was to deploy a container through Portainer's native stack deployment. They named it systmd — deliberately mimicking the Linux systemd process name to blend into process listings.
The compose File :
version: '3'
services:
systmd:
image: ubuntu:20.04
container_name: systmd
privileged: true
pid: host
volumes:
- /:/host
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- /var/run/dbus:/var/run/dbus:rw
- /run/systemd/system:/run/systemd/system:rw
command: chroot /host /bin/bash
restart: alwaysBreaking this down:
privileged: true :-disables all kernel security mechanisms. A privileged container has near-unrestricted access to the host kernel.pid: host— shares the host's PID namespace. The container can see and interact with every process on the host./:/host— mounts the entire host root filesystem read-write inside the container at/host./run/systemd/system:/run/systemd/system:rw— direct write access to systemd service unit directories on the host.command: chroot /host /bin/bash— on startup, immediately chroots into the host filesystem. The container boundary is now gone.
This is MITRE ATT&CK T1611 — Escape to Host, executed without exploiting a single vulnerability. Docker allows this by design. The attacker simply used Portainer's web UI to deploy it, no terminal access to the host required.
Phase 2 — Evolution: host-shell and nsenter
The attacker didn't stop at systmd. Forensic artifacts from Portainer's compose directory showed them iterating their technique over time.
A new container named host-shell / hostshell_entrypoint appeared, switching from chroot to sleep infinity:
command: sleep infinityThis is operationally cleaner. Instead of dropping into an interactive shell on startup, sleep infinity keeps the container alive indefinitely. The attacker then executes commands on demand:
docker exec -it hostshell_entrypoint nsenter -t 1 -m -u -n -i /bin/bashThe nsenter command is the real technique here:
nsenter -t 1 -m -u -n -i /bin/bash-t 1— target PID 1, the host's init process-m— enter its mount namespace (host filesystem)-u— enter its UTS namespace (hostname)-n— enter its network namespace (host network stack)-i— enter its IPC namespace
The result is a shell running inside all of the host's namespaces simultaneously. It is not a container anymore. It is the host.
The attacker also baked this nsenter command into Portainer custom templates, letting them push this escape capability to any edge device in the fleet with a single click. The technique continued to evolve, showing the attacker actively refining their tooling as the operation progressed.
Phase 3 — Deploying Tailscale via Container
Here is where it gets interesting. The attacker didn't install Tailscale manually from a shell session. They containerized the Tailscale deployment itself — packaging the entire installation, authentication, and persistence setup into an Alpine Linux container that would escape to the host and do everything automatically.
The container used two critical mounts:
volumes:
- /:/host
- /var/run/docker.sock:/var/run/docker.sock Combined with the same nsenter -t 1 -m -u -n -i escape technique from Phase 2, the container's script broke out of its namespace, landed on the host OS, and executed the following — all without any manual interaction:
- Downloaded and installed Tailscale binaries directly onto the host OS
- Authenticated to the attacker's Tailscale network using a hardcoded auth key
- Enabled
--ssh --accept-routes— giving the attacker persistent SSH access over the Tailscale mesh - Created a systemd service for automatic restart on reboot
- Dropped a cron job running every 3 minutes to ensure Tailscale stayed connected
Once this ran, the attacker had a persistent, encrypted SSH tunnel into the host — completely independent of Portainer. They no longer needed the web UI. They had direct access.
Phase 4 — How I Actually Found It
While reviewing /var/log/ on a compromised device, one filename stood out immediately:
/var/log/tailscale-persistent.logThis was the output log of the attacker's persistence script — a self-created audit trail of their own activity. Reading it led directly to the script itself:
/usr/local/bin/tailscale-persistent.shThe script implemented dual-layer persistence — both mechanisms working together to ensure Tailscale would always survive a reboot or manual kill:
Cron job (every 3 minutes, as root):
*/3 * * * * /usr/local/bin/tailscale-persistent.sh >> /var/log/tailscale-persistent.log 2>&1Systemd service (written directly to the host via the mounted /run/systemd/system directory):
[Unit]
Description=Tailscale Persistent Service
After=network.target
[Service]
ExecStart=/usr/local/bin/tailscale-persistent.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetThe script checked if Tailscale was running, and if not. re-authenticated using the hardcoded auth key and reconnected. Every 3 minutes. Silently. Logging to that /var/log/ file.
Why Tailscale Specifically
This is a deliberate and smart choice by the attacker. Once Tailscale is installed on a compromised host, that device becomes a permanent node in the attacker's private network — accessible from anywhere, at any time
Encrypted tunnel over port 443 — Tailscale's control plane runs on controlplane.tailscale.com over HTTPS. From any network monitoring tool, this looks like normal cloud service traffic. There is nothing to block and nothing that looks suspicious.
Not flagged by security tools — Tailscale is a legitimate, signed binary from a reputable vendor. EDR tools won't alert on it. It's not in any threat intel feed as malicious.
SSH built in — tailscale ssh gives the attacker a persistent SSH channel to every enrolled device, completely bypassing firewall rules and network perimeter. Once Tailscale is installed, the attacker doesn't need Portainer anymore.
Survives IP changes and reboots — because Tailscale is a mesh VPN with its own coordination server, the attacker's connection to compromised devices doesn't depend on static IPs or open ports. The devices call home to Tailscale's infrastructure — the attacker just waits.
This is MITRE ATT&CK T1572 — Protocol Tunneling, and it's increasingly the technique of choice for attackers who want persistence that blends into legitimate traffic.
MITRE ATT&CK Mapping
MITRE ATT&CK mapping of the full attack chain from initial access via leaked credentials through container escape, Tailscale C2 installation, and persistence mechanisms observed during the engagement.
Detection Opportunities
Hunt for privileged containers with host mounts:
docker ps -q | xargs docker inspect \
--format '{{.Name}} Privileged={{.HostConfig.Privileged}} Mounts={{.HostConfig.Binds}}' \
| grep "Privileged=true"Hunt for Tailscale on Linux hosts:
# Binary present?
which tailscale 2>/dev/null
# Running as a service
systemctl list-units
# Cron persistence
crontab -l -u root 2>/dev/null Any host connecting to 100.64.0.0/10 (Tailscale's CGNAT space) that isn't an approved deployment warrants immediate investigation.
Key Takeaways
1. One undeactivated account = full fleet compromise. The entire chain started with a former employee's credentials. Offboarding is a security control — treat it like one.
2. Privileged containers are not containers — they are root shells. If your container management platform allows deploying --privileged containers with host filesystem mounts, you have effectively given that user root on the host. Enforce RBAC, restrict privileged deployments, require approval workflows.
3. Attackers are containerizing their own attack tools. The Tailscale deployment wasn't a manual install — it was packaged into a container for repeatable, scalable deployment across a fleet.
4. Legitimate tools are the blind spot in your detection stack. Tailscale, AnyDesk, TeamViewer, ngrok — your EDR has no reason to flag these. Baseline what's legitimately installed in your environment and alert on anything new appearing outside of change management.
Conclusion
What stayed with me from this engagement wasn't the sophistication — it was the simplicity. The attacker used a web UI, standard Docker features, a legitimate VPN product, and shell scripts. The entire operation was built from things that look completely normal in most environments.
That is precisely what makes this dangerous. Detection strategies that ask "is this malicious software?" will miss this completely. The better question is: "is this legitimate software running somewhere it shouldn't be, doing something it wasn't deployed to do?"
If you're running Portainer, Rancher, or any Docker management platform — audit your privileged containers today. Check for unexpected binaries in /usr/local/bin. It might be the most important 10 minutes you spend this week.
Hope you will find it useful, Thanks!
Harshit | Senior Information Security Analyst | DFIR Practitioner | eCIR | CHFI
Connect with me on LinkedIn: https://www.linkedin.com/in/harshit-bhardwaj-65388622a/