Tentative d’exploit Postfix

Postfix est un serveur de messagerie électronique open source créée par Wietse Venema en 1998 au sein de l’entreprise IBM, dans le but d’être une alternative au très répandu Sendmail. Une de ses particularités est qu’il est beaucoup plus simple à administrer que Sendmail et est exclusivement SMTP. En janvier 2012, une étude faite par E-Soft, Inc. nous informe que 23% des serveurs mails visibles sur internet utilisent Postfix.

C’est pourquoi une faille logicielle sur ce serveur serait catastrophique en cas d’exploit plus ou moins simple, et surtout non corrigé. Postfix est développé par une équipe très expérimentée qui s’intéresse de près aux questions de sécurité. Le code fut écrit à partir de rien, ce qui le rend entièrement compréhensible par ses développeurs dans le but de parer toutes attaques; en particulier le rendre résistant aux buffers overflow qui sévissent dans les autres serveurs mails. On peut d’ailleurs le remarquer grâce à ce graphique répertoriant les failles de sécurité de Postfix de 2003 à 2013 :

On voit qu’il n’y a eu que 4 failles depuis 2003, où la plus critique le 10 mai 2011 est sensible à une injection de code arbitraire mais n’a jamais pu être exploité.
Le logiciel est d’autant plus sécurisé que chaque processus de Postfix à un ensemble de tâches prédéfini, ce qui permet de réduire leur champ d’action pour limiter les dégâts en cas d’attaque ou de bug.

Postfix ne possède qu’un nombre limité de fonctions, c’est pourquoi il s’appuie sur des extensions tierces pour le reste. Dans de nombreux cas, rajouter les protocoles IMAP, POP3 ou des protocoles d’authentification SASL supplémentaires et posible grâce aux logiciels Dovecot ou Cyrus, et se greffent facilement sur Postfix.

Postfix n’implémente pas SASL de lui-même, ce qui veut dire que les fichiers de configuration
vont appartenir à Postfix et à un logiciel tiers (dans notre cas Cyrus SASL).
Je vais vous montrer l’exploit que j’ai tenté au travers de cette partie, sur les versions ci-dessous :

- OS : Ubuntu 10.04 (04/2010)
- Postfix : 2.8.1 (22/02/2011)
- Cyrus SASL: 2.1.23 (04/10/2014)
- CVE: 2011–1720 (13/05/2011)

EXPLOITATION ET ANALYSE

1. Reproduction de l’erreur

Cette faille a été répertorié par le site CVE Details le 13 mai 2011 sous le nom de CVE-2011–1720 et a été découverte par Thomas Jarosch de Intra2net AG. Les serveurs vulnérables à cette faille sont ceux ayant une version de Postfix inférieur à la 2.5.13, 2.6.x avant 2.6.10, 2.7.x avant 2.7.4 et 2.8.x avant 2.8.3, quand l’authentification SASL de Cyrus est active.

Le serveur Postfix a une corruption de mémoire quand les mécaniques d’authentification SASL de Cyrus, autres que PLAIN et LOGIN, sont utilisées. Dans notre cas avec les versions de Postfix et Cyrus utilisés, ce sont les méthodes CRAM-MD5, DIGEST-MD5 et NTLM qui sont affectés par ce bug.

La corruption de mémoire résulte en erreur de segmentation et interrompt le service. De plus, cette action peut être faite à distance sans méthode d’authentification. Ci-dessous, voici un exemple d’utilisation de ce bug pour obtenir un arrêt brutal du serveur distant. Dans un des cas on voit bien le message d’erreur “Connection close by foreign host.” qui nous informe qu’il y a eu une erreur de segmentation. Dans la version patchée nous pouvons voir ce qui aurait dû apparaître.

Crash du serveur en version 2.8.1
Bug corrigé en 2.8.3

2. Pourquoi cette erreur de segmentation ?

Le serveur Postfix créé une nouvelle session à chaque connexion SMTP si l’authentification SASL est activée. Le serveur va utiliser cette session jusqu’à ce que la connexion soit rompue. Selon un commentaire dans smtpd_sasl_proto.c, un serveur ne doit pas réutiliser une session après un échec d’authentification. À la place, un serveur doit créer une nouvelle session en fonction du mécanisme d’authentification utilisé avant que celle-ci ne soit sélectionnée par l’utilisateur.

Cependant, le serveur SMTP échoue lors de la création d’une nouvelle session après un échec et conserve l’ancienne. Cette session est en particulier une structure de données nommée context_t propre à chaque méthode d’authentification :

Structure de context_t pour CRAM-MD5 puis DIGEST-MD5

Comme on peut le voir, la structure de CRAM-MD5 est bien plus courte que celle de DIGEST-MD5 (je ne l’ai pas mis en entière car très longue). De ce fait, si la structure de CRAM-MD5 est appelée dans DIGEST-MD5 et qu’une requête demande la valeur de reauth (ligne 6), le programme va pointer vers une adresse qui n’est pas allouée ou qui n’a pas été prévue à cet effet.

Maintenant que la faille a bien été comprise, essayons de voir ce qui se passe dans le code lorsqu’une méthode d’authentification CRAM-MD5 est avortée puis suivi par un DIGEST-MD5.

3. Debug de la faille

Quand le serveur Postfix reçoit “AUTH CRAM-MD5”, la méthode CRAM-MD5 est appelée et initialise une structure de données context_t, puis génère un challenge au client pour vérifier son identité. Quand le client envoie “*”, la structure de données reste attachée à la session active.

Postfix échoue à créer une nouvelle session quand le client envoie la requête “AUTH DIGEST-MD5”. Cette méthode va utiliser le mauvais contexte et arrive l’erreur de segmentation.
Quand on debug avec gdb l’exécution de ces commandes, et qu’on s’attarde à ce qui provoque l’erreur de segmentation, on remarque que la structure contexte utilisée par DIGEST-MD5 est la suivante :

Valeurs de context_t pour DIGEST-MD5

Une fois de plus, intéressons-nous à la valeur de reauth. Si on se souvient de la structure de context_t, on remarque que reauth est censé être un pointeur vers une adresse, et dans le cas echéant la valeur est de -1 (0xffffffff). reauth est une structure de données qui possède une variable nommée timeout , celle-ci sera appelée par une fonction dans digestmd5.c à la ligne 2659. Comme pointer vers l’adresse 0xffffffff est impossible, il y aura une erreur de segmentation.

4. Injection de code

Pour exploiter cette faille, l’idée serait de modifier cette structure de données context_t pour orienter le programme vers ce que l’on veut exécuter. Lorsque l’on observe l’orientation du programme, on remarque que c’est la valeur 0x2 du contexte qui guide une partie de l’exécution et qui mène à l’erreur de segmentation par valeur du timeout. Aucun input n’est proposé à l’attaquant.

Une solution pourrait être de modifier les valeurs du contexte dans un premier temps, pour amener l’erreur de segmentation vers un autre endroit. La ligne exacte étant :

if (text->reauth->timeout && …)

Ce qui est inexploitable à mon avis car c’est un test qui cherche si le timeout est différent de zéro, aucun script ou code ne peut être exécuté.

Le seul endroit où il me semble que l’on peut interagir avec les données du programme serait à la place du “*” qui ferme le challenge de CRAM-MD5. Mais après de nombreux essais, et en ayant localisé l’endroit dans le buffer où a été écrit ce que j’injecte, la distance avec le contexte est fixe. Je n’ai donc pas réussi à écraser de données, ni de changer le contexte.

Erreur de segmentations sur le même modèle

L’erreur de segmentation est à chaque fois dans un appel impossible à exploiter si l’on ne peut pas modifier la valeur du contexte. La clé de cet exploit réside donc certainement dans la modification de la structure de données context_t en y injectant des valeurs sur mesure.

5. Patch appliqué

Dans le patch qui corrige la faille, on peut voir que seul un fichier a été modifié. C’est dans smtpd_sasl_proto.c ligne 187 qu’une portion de code a été rajouté. En effet, la session ne se libérait pas dans les versions précédentes car l’allocation mémoire ne se libérait pas correctement.
Pour pallier ce problème cette nouvelle portion de code envoie la structure context_t dans une fonction qui désalloue la mémoire à chaque élément de la structure de données.
Tous les pointeurs de fonctions seront donc réinitialisés et permettront au programme interne du serveur à se terminer correctement.

CONCLUSION

Nous avons pu voir que cette faille de Postfix est loin d’être trivial à exploiter. D’autres tests peuvent être lancés comme savoir si on peut donner une valeur valide au timeout en modifiant l’adresse de reauth directement dans gdb. Si oui, quelles sont les commandes exécutées par la suite et comment les modifier elles aussi pour injecter des commandes.
Malgré cet échec, il est intéressant de voir que Postfix est une fois de plus une valeur sûre. Cet exploit est pour moi à la fois inexploitable et peu dépendant de Postfix.

Les détails de mes codes sont disponibles ici : https://github.com/nbeguier/postfix_exploit