PostgreSQL RCE via COPY ... PROGRAM (CVE-2019-9193)
Desde la versión 9.3, PostgreSQL incluye la cláusula COPY ... FROM/TO PROGRAM: permite que una sentencia COPY lea su entrada de la salida de un programa, o escriba su salida hacia la entrada de otro. Ese programa se ejecuta con el usuario del sistema bajo el que corre el servidor (normalmente postgres). Quien pueda ejecutar COPY ... PROGRAM consigue, en la práctica, ejecución de comandos en el host.
Por qué importa (y el matiz del CVE). La técnica se cataloga como CVE-2019-9193, pero PostgreSQL la considera una funcionalidad por diseño, no un fallo: COPY ... PROGRAM está restringida a cuentas con rol de superusuario (o, desde la 11, a miembros de pg_execute_server_program). Es decir, no es un salto de privilegios desde un usuario sin permisos: hace falta autenticarse como superusuario primero. El caso típico de laboratorio es justo ese, credenciales débiles o por defecto (postgres:postgres) que dan acceso de superusuario; a partir de ahí la "vulnerabilidad" es simplemente usar una función legítima para ejecutar comandos.
La receta. Manualmente, se crea una tabla temporal y se vuelca en ella la salida de un comando:
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;El SELECT final muestra la salida de id ejecutado en el servidor. Para una reverse shell basta con poner el comando correspondiente en el PROGRAM. El exploit público 50847.py (exploit-db) automatiza todo el ciclo (crear la tabla con un nombre aleatorio, lanzar el comando y limpiar) sobre una base de datos indicada con -d:
python3 50847.py -i <ip> -p <port> -d template1 -c "<comando>"Cómo detectarlo. Con log_statement='all' (o pgaudit), la firma es inequívoca en el log de PostgreSQL: una sentencia COPY ... FROM/TO PROGRAM, que ninguna aplicación legítima usa, casi siempre precedida de un CREATE TABLE temporal de nombre aleatorio. En el host, el proceso postgres lanzando sh/bash (o el comando indicado) como hijo es la confirmación (auditd/Sysmon for Linux sobre execve con ppid=postgres), algo que el motor no hace en operación normal. Un login de superusuario desde una IP externa (a menudo postgres:postgres) es el indicio previo.
Defensa. No exponer PostgreSQL a la red sin necesidad y nunca con credenciales por defecto. Conceder el rol de superusuario solo a las cuentas imprescindibles: una cuenta de aplicación con privilegios mínimos no puede usar COPY ... PROGRAM. Relacionado con inyección de comandos y, como vector de salto posterior, con abuso de binarios SUID.
Máquinas donde se aplica
- NibblesProving Grounds Practice