Vulnérabilités dans les lecteurs PDF : le cas de SumatraPDF

Fri 04 December 2009 by Christophe Devine

On pourrait penser, suite à de nombreuses failles affectant Acrobat Reader (voir par exemple l'avis CERTA-2009-AVI-445), qu'il est plus prudent d'utiliser un lecteur alternatif. Sous Windows, les principales alternatives sont Foxit et SumatraPDF, un lecteur de PDF léger et open-source. En pratique, la situation n'est pas aussi tranchée : Acrobat est plus répandu donc plus exposé, mais rien n'interdit de penser que les autres lecteurs présentent de même des vulnérabilités critiques.

Ainsi, suite à l'analyse manuelle du code source de SumatraPDF par l'auteur du présent billet, une nouvelle faille (CVE-2009-4117) a été identifiée dans les fonctions responsables du traitement des objects "Shading" de type 4 à 7. Le code vulnérable est présenté ci-dessous :

float c0[FZ_MAXCOLORS];
float c1[FZ_MAXCOLORS];
[...]
obj = fz_dictgets(shading, "Decode");
if (fz_isarray(obj))
{
    [...]
    for (i=0; i < fz_arraylen(obj) / 2; ++i) {
        c0[i] = fz_toreal(fz_arrayget(obj, i*2+4));
        c1[i] = fz_toreal(fz_arrayget(obj, i*2+5));
    }
}

On voit la fonction vulnérable, pdf_loadtype4shade(), utiliser sans vérification la taille du tableau "Decode" pour remplir les tableaux c0 et c1, alloués sur la pile. Un tableau trop grand donnera alors lieu à un dépassement de mémoire tampon et fournirait à l'attaquant le contrôle du flux d'exécution.

Il s'avère que SumatraPDF est compilé avec le drapeau /GS, qui ajoute aux fonctions potentiellement vulnérables la vérification d'un "cookie" pour détecter les dépassements de mémoire tampon sur la pile. En l'occurrence, la version 1.0 est compilée avec Visual Studio 2008. Pourtant, en décompilant le binaire on constate l'absence de cookie appliqué à la fonction pdf_loadtype4shade() : l'heuristique de Visual Studio a échoué en ne considérant pas les tableaux c0 et c1 comme étant "à risque".

L'exploitation proprement dite de cette vulnérabilité est de plus facilitée par l'absence de protections supplémentaires : ni DEP (protection contre l'exécution de données), ni ASLR (aléa sur les adresses de chargement) n'ont été activés dans les options de compilation. On retombe alors dans le cas classique ou l'adresse de retour est ré-écrite pour sauter vers un "shellcode" présent sur la pile. Pour autant quelques astuces sont requises lors de l'exploitation ; par exemple, le pointeur "obj" ne doit pas être modifié, sous peine de provoquer une exception. Le problème peut se contourner en ré-écrivant en premier lieu la variable i pour dépasser obj. D'autre part, le tableau Decode ne doit contenir que des nombres à virgule flottante convertis en float : les données écrites sur la pile doivent être converties en "float", en faisant attention à ne pas générer de nombres non représentables (NaN). Par exemple, 0x00001033 sera converti en 5.8111847e-42.

Le framework de manipulation de PDF Origami fournit la possibilité de créer simplement un fichier PDF qui déclenche la vulnérabilité (le code ci-dessous est un "proof-of-concept" et n'inclut pas la charge finale).

$: << "sources/parser"
require 'parser.rb'
include Origami

sploit = [ 1234 ] * 250

shader = Graphics::Pattern::Shading::FreeFormTriangleMesh.new
shader.ColorSpace = Graphics::Color::Space::DEVICE_RGB
shader.BitsPerCoordinate = 24
shader.BitsPerComponent = 16
shader.BitsPerFlag = 8
shader.Decode = sploit

page = Page.new.add_shading(:kikoo, shader)
page.Contents = ContentStream.new
page.Contents.paint_shading(:kikoo)
PDF.new.append_page(page).saveas('toto.pdf')

Comment se protéger contre ce type de vulnérabilités ? Après échanges avec l'auteur de SumatraPDF, les options de compilation de ce logiciel intègrent désormais les protections DEP est ASLR ; de plus, le DEP est maintenant activé de façon permanente via l'appel système NtSetInformationProcess(), ce qui est requis pour Windows XP (lequel ne prend pas en compte le drapeau NXCOMPAT). Ainsi, un hypothétique dépassement de mémoire tampon dans une version future sera considérablement plus difficile à exploiter de manière fiable.

Cette faille illustre le fait qu'il ne faut pas accorder une trop grande confiance aux protections supplémentaires tel que /GS ; cette option est certainement un plus en termes de sécurité, mais doit être renforcée par d'autres mesures techniques (DEP, ASLR, SAFESEH) pour être vraiment efficace. Remarquons de plus que ces protections sont sans effet contre d'autres classes de vulnérabilités, comme le filtrage des chemins d'accès locaux.

Par ailleurs, il est recommandé d'activer automatiquement le DEP pour l'ensemble des programmes, ce qui à l'heure actuelle ne pose plus de soucis particuliers de compatibilité. Dans "Paramètres systèmes avancées" sous Windows Vista/7 (ou bien Ordinateur -> clic droit -> propriétés sous XP), allez dans l'onglet Avancé, puis sélectionnez les paramètres de Performances, et dans l'onglet "Prévention de l'exécution des données" choisissez "Activer la prévention de l'exécution des données pour tous les programmes". Pour les utilisateurs de Acrobat Reader, il est conseillé de désactiver le JavaScript (menu Préférences -> JavaScript, décochez "Activer le JavaScript").