Brad Geesaman
+ Your AuthorsArchive @bradgeesaman Co-founder and Chief Architect at Helping organizations assess the security posture of their AWS, GCP, and Kubernetes environments. He/him Dec. 21, 2019 5 min read

Shout out to @honkci (@jeefy and @MrBobbyTables) for allowing this Goose to be horrible in the garden last night. I wanted to do a quick writeup of my approach and thought processes so that others may benefit:

1) The command syntax appears to be "/honkctl" on a line by itself followed by mostly standard kubectl commands. So, "/honkctl get pods" is translated to "kubectl get pods" on the target cluster.

2) From some of the lead-up tweets, we know that the target cluster is running .

3) When the game started, the first command "/honkctl get pods --all-namespaces" was a sort of litmus test to see a) is my command syntax correct, b) how fast does the bot respond to my tweet, and c) do I have some basic permissions in the cluster?

4) About 10 seconds later, I got back:
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:lake:goose" cannot list resource "pods" in API group "" at the cluster scope

5) From this RBAC error, I now know that I am interacting with the cluster using the "goose" service account in the "lake" namespace, and that I cannot list pods in all namespaces.

6) My next question was, "Does the goose service account have any RBAC permissions?" Thanks to @mauilion and @iancoldwater, this is answerable with a "/honkctl auth can-i --list"

7) Again, I got back:

Resources Non-Resource URLs Resource Names Verbs  [] [] [create delete deletecollection get list patch update watch] 

8) Nice! This means I have the ability to create namespace-scoped RoleBindings (presumably in the lake namespace). So, that means I should be able to bind the built-in "admin" ClusterRole to the lake namespace:

9) "/honkctl create rolebinding admin --clusterrole=admin --serviceaccount=lake:goose -n lake" got back a "  created"

10) HONK! Now, I can see what I'm allowed to create. Using Twitter as my input mechanism, I have to fit everything into a tweet. Github public gists to the rescue. I wanted to see if I could create a root-user pod with a hostpath mount from an arbitrary image path:

11) "/honkctl apply -f "

12) A few seconds later, I got back: "pod/nginx created" HONK! Know that admission control is letting quite a bit through here, I've got a lot of freedom to operate. The limiting factors at this point were that "/honkctl exec" was blocked and I didn't have an interactive shell.

13) Knowing that egress traffic was likely allowed, I opted to try a reverse shell. Since I can run arbitrary pods, I can have that pod send me back a shell. I spun up a GCP instance with TCP/80 inbound. I SSHed in and installed netcat. Then I ran "nc -l -p 80"

14) I created another gist and ran "/honkctl apply -f " and this time told it to spin in a "while" loop trying to "shovel me a shell" to my attacking VM on port 80.

15) "pod/nginx2 created" HONK! Over in my SSH session, nothing changed, but now when I ran "ls" I was greeted with the directory listing of my nginx2 pod! Best part was, if I Ctrl-C out of the netcat session, my pod would immediately retry to connect to me.

16) Next command I ran was "chroot /rootfs /bin/bash" followed by "ls". HONK! I was now root on the underlying control plane node running this cluster!

17) I looked at "/etc/shadow" for any hashes to crack:

18) I thought "What am I doing?" This is a Kind cluster! The cluster credentials are in "/etc/kubernetes" and a valid, fully-privileged kubeconfig is in "admin.conf"

19) From there, I could freely run "kubectl" commands as "cluster-admin" if I set the env var for KUBECONFIG to point to that file. HONK!

20) And with the cluster certificates and keys, I could undermine the interactions with all components.

21) And then, all of a sudden, my shell stopped working. Other naughty geese had run amok and my pod was tossed in the lake. @honkci worked tirelessly to recover the cluster.

22) Knowing that my reverse shell pod was running in the "lake" namespace where everyone on Twitter had "namespace admin", I make a slight modification to my gist to run in kube-system instead.

23) When the cluster came back up, I quickly re-added the "admin" role binding, and reran my reverse shell pod in the "lake" namespace.

24) With my shell back on my GCP VM, I got back into the node as root and ran "kubectl apply -f "

25) Now, my reverse shell pod was running outside of the purview of most of the other participants. I used "/honkctl delete pod nginx2" to clean up a bit, and I was greeted with a shell sent by my kube-system pod:

26) HONK! Unfortunately, that's all I had time for and had to sign off. When @honkci ran out of Twitter API quota, they took things to Slack where complete and utter chaos ensued. Dozens of horrible geese HONKing and doing wonderfully awful things to each other.

27) A great big HONKing Thank you to @honkci (@jeefy and @MrBobbyTables) for putting on such a cool little event last night. I only had a short window to participate, but it was great fun! Hope to see more things like this in the future!

You can follow @bradgeesaman.


Tip: mention @threader_app on a Twitter thread with the keyword “compile” to get a link to it.

Enjoy Threader? Sign up.

Since you’re here...

... we’re asking visitors like you to make a contribution to support this independent project. In these uncertain times, access to information is vital. Threader gets 1,000,000+ visits a month and our iOS Twitter client was featured as an App of the Day by Apple. Your financial support will help two developers to keep working on this app. Everyone’s contribution, big or small, is so valuable. Support Threader by becoming premium or by donating on PayPal. Thank you.

Follow Threader