Múltiples vulnerabilidades (no-fix) M4 PDF plugin para PrestaShop

Volvemos con más CVEs, en este caso una inyección de HTML y un directorio transversal, que afectan al plugin M4 PDF de PrestaShop. Estas vulnerabilidades no han sido ni parece que vayan a ser solucionadas por el fabricante, siendo este plugin utilizado de forma bastante extendida. Los identificadores de los CVEs que el INCIBE nos ha asignado son los siguientes: CVE-2022-45447 y CVE-2022-45448.

Antes de comenzar con la entrada, me gustaría mencionar a los compañeros del equipo de Seguridad Ofensiva de Grupo CIES – Alisec, David Álvarez y Sergio Corral que, junto a mí (Francisco Díaz-Pache), volvimos a realizar el trabajo en equipo de descubrimiento, documentación y reporte de las vulnerabilidades.

En cuanto a las vulnerabilidades, lo primero que se debe tener en cuenta es que están presentes cuando se utiliza el plugin M4 PDF en una web con PrestaShop. Es sencillo encontrar webs que utilizan este plugin mediante búsquedas de Google y el siguiente dork:

"inurl:/m4pdf/pdf.php"

Inyección HTML – CVE-2022-45448

El recurso /m4pdf/pdf.php utiliza plantillas para crear documentos dinámicamente. Si la plantilla no existe, la aplicación devolverá un documento con un mensaje que dice “Template not found: “. Ese mensaje, al igual que cualquier documento generado por la aplicación, sigue el formato mpdf, que es HTML/CSS más algunas etiquetas más para cosas como saltos de página. Si el usuario ingresa un documento HTML/CSS válido como valor del parámetro, se puede crear un documento arbitrario, ya que a pesar de estar pensado para devolver un documento PDF es posible especificar que devuelva un documento HTML mediante los siguientes parámetros:

  • nav
  • template
  • html

El parámetro nav puede tener uno de los siguientes valores: [customer, order, orderdetail, orderslipdetail, orderreturndetail, product, category, supplyorder].

El parámetro template es donde se envía el payload.

El parámetro HTML puede ser 0 o 1, si es 0 se genera un documento PDF, si es 1 genera un documento HTML.

Este payload devuelve un archivo JSON con algunos valores, siendo “msg” el nombre del archivo del documento generado. Si un usuario accede a “/pdf.php?f=<msg>” el usuario podrá descargar el documento. Esos enlaces son de un solo uso, y una vez que se descarga el documento, se elimina. Esto es una limitación (porque el atacante tendrá que generar un enlace para cada usuario), pero también es una forma de verificar si el enlace fue utilizado.

También es posible utilizar el mismo vector de ataque para comprobar que un fichero exista en el servidor, cambiando el parámetro “template” por una ruta a un fichero (../../../../../../../etc/passwd). Si el fichero existe, devolverá un error diciendo que el formato de la plantilla es incorrecto. Si el usuario ejecutando la aplicación no tiene permisos de lectura sobre el fichero, dará otro error expresando esto mismo.

Directorio transversal – CVE-2022-45447

Esta vulnerabilidad es mucho más simple de ejecutar, pero es mucho más peligrosa, ya que es posible obtener información muy sensible de la aplicación y del sistema. El parámetro “f”, de “file”, es utilizado para descargar el fichero generado anteriormente (como se vio en la sección anterior). Sin embargo, si se cambia el valor de este parámetro por una dirección relativa (../../../../../../../etc/passwd), es posible descargar cualquier fichero del sistema.

Parcheo y pruebas de concepto

Ya que el desarrollador no piensa arreglar estos fallos, no vamos a dar PoCs de exploits de las vulnerabilidades. Sin embargo, son vulnerabilidades con una explotación trivial, así que animamos a cualquiera que use este plugin a que directamente lo deje de usar. En caso de necesitarlo imperativamente, dejamos una solución propuesta a ambas vulnerabilidades, que cada uno puede aplicar bajo su propio riesgo (no están probadas y, muy posiblemente, puedan existir bypasses a las mismas, por lo que han de testearse tras su implementación).

Solución a la inyección de HTML

Viendo el código fuente, fichero llamado “M4Object.php”, se ve que la excepción generada es devuelta en el documento, sin ningún tipo de saneo:

catch (Exception $e)
{
   	$out = '<p>'.$e->getMessage().'</p>';
}

Se podría comentar esta línea, o dejarla como <p>An error has occured\<p>, eliminando ningún detalle que pueda proporcionar la excepción.

Solución al directorio transversal

De nuevo, viendo el código en el fichero “pdf.php”:

if (Tools::getValue('f'))
{
	$file = 'cache/'.(string)Tools::getValue('f');
	if (file_exists($file))
	{
              
		[...]

		while (@ob_end_clean());
		flush();
		readfile($file);
		unlink($file);
	}
	else
		echo 'File not exists';
}

Se comprueba que no se procesa el valor del parámetro f en ningún momento. Ya que en condiciones normales el recurso nunca esperará nada diferente al identificador generado, se puede añadir un filtro para eliminar todos los caracteres que no sean letras o números:

$expresion_regular = '[^\w-]+/u';
$file = preg_replace($expresion_regular, '', (string)Tools::getValue('f'));
$file = 'cache/'.$file;

Esto es todo por hoy, esperemos volver pronto con más CVEs, ya que todavía nos queda alguno en recámara.