Overblog Suivre ce blog
Editer l'article Administration Créer mon blog

Faille formulaire mail(); php

par Morgan

publié dans securité

 

Il n'est pas rare de voir sur internet trainer ce genre de formulaire :

 

 formulaire.png

Ce code est largement ouvert au spamming email !

 

Beaucoup de webmaster on tendance a appeler leurs formulaires de contact "contact.php", "formulaire.php" de quoi facilité la recherche de formulaire de contact via google (inurl:contact.php).

 

 

Ce code va permettre au méchants hackers d'envoyer à répétition des emails, totalement personnalisés, via VOTRE serveur mail.


Quand on envoie un mail en PHP, le destinataire reçoit un mail dont l'ip de l'expéditeur est celui du site depuis lequel la fonction mail() a été appelée. Le site fait donc usage de proxy SMTP.

 

Rappel : La fonction mail fonctionne comme ceci : mail([DESTINATAIRE],[SUJET],[MESSAGE],[HEADERS]);

 


On peut détourner la fonction en injectant du code dans le champ expéditeur (From:). On choisira From: comme cible, car il apparait après les en-tête Subject: et To:, il fait parti des headers ajoutés manuellement au mail et n'est pas protégé par mail() et la RFC.


Notez bien que même si le champ destinataire est fixé, le fait de pouvoir remplir le champ expéditeur, suffit pour une injection.



On ne peut donc pas choisir le destinataire via le premier argument de la fonction mail(), vu qu'il est définit dans le script, par contre on peut définir le sujet, le message, et l'expéditeur.


Rappelons le format d'envoi de mail texte( non MIME ) en PHP, pour une fonction de type mail($destinataire,$sujet,$message,$headers); selon la RFC 2822 :

To: $destinataire
Subject: $sujet
$headers

$message

Donc quand on fait un appel à la fonction mail("webmaster@monsite.com","Salut","Salut,\n voici un pti message pour toi :)","From: quelquun@unsite.com"); on envoie :

To: webmaster@monsite.com
Subject: Salut
From: quelquun@unsite.com

Salut,
voici un pti message pour toi :)

Nous allons modifier/ajouter d'autres headers que le "From:".


Injecter des headers (en-têtes), permet de détourner la fonction principale du script, et envoyer un email anonyme à quelqu'un d'autre que le webmaster du site ( dans notre exemple )

C'est possible en utilisant par exemple l'header "Cc" ( Carbon Copy ), uu mieux encore, le header "Bcc" (Blind Carbon Copy) qui envoie lui une copie conforme sans qu'aucun autre destinataire ne soit au courant.

 

La RFC spécifie que pour définir un nouvel header, il faut passer à la ligne, et cela est rendu possible grâce au caractère <LF> (Line Feed), dont la valeur hexadécimale est 0x0A.

 

Ainsi, en reprenant l'exemple de script PHP ci-dessus, si je donne comme valeur :


From: (expediteur) -> "myemail@anonyme.com%0ACc:email1@site1.com%0ABcc:email2@site2.com,email3@site3.com"
Subject: (sujet) -> "Ceci est mon sujet"
(message) -> "Un message"

 

 

alors l'email envoyé sera de la forme :

 

To: webmaster@monsite.com
Subject: Ceci est mon sujet
From: myemail@anonymous.com
Cc:email1@site1.com
Bcc:email2@site2.com,email3@site3.com

Un message

et on aura non seulement bel et bien injecté des headers, vu que le seul prévu par le webmaster était "From", mais on a en plus envoyé l'email à trois personne de notre choix alors qu'on était pas sensé pouvoir choisir de destinataire.
On a utilisé les headers "Cc" et "Bcc" pour envoyer notre mail à qui on voulait, mais il est de même possible de redéfinir le header "To:", la nouvelle valeur viendra juste s'ajouter à l'ancienne, comme si on avait séparé les deux mails par des virgules.

Ainsi : donnons à l'expéditeur la valeur : "myemail@anonyme.com%0ATo:boiteemail@domaine.com" :

To: webmaster@monsite.com
Subject: Ceci est mon sujet
From: myemail@anonyme.com
To:boiteemail@domaine.com

Un message

La répétition du "To" ne posera pas de problème, et l'email sera envoyé à webmaster@monsite.com ET boiteemail@domaine.com .
Idem pour le sujet, on peut redéfinir le header 'Subject:' , le sujet sera alors la concaténation de tous les sujets définis par cet en-tête.

Pour le cors du message, pas d'en-tête spécifique, si on passe à la ligne avec un LF sans avoir définit d'en-tête , alors le corps du message commence.

Les corps de message seront concaténés, mais il est possible de faire carrément disparaitre un corps de message déja fixé dans le code PHP.

Changeons d'exemple, imaginons que nous ne puissions pas écrire de corps de message, mais que seuls To: et From: nous sont accessibles.

Dans ce cas, l'injection d'un en-tête "content-type=multipart/mixed" va nous permettre de faire disparaitre le corps du message initialement fixé.
Il va aussi nous permettre d'attacher des pièces jointes, ou d'écrire du HTML.
Je vous renvoie cette fois ci à la RFC2045, qui définit les normes concernant les emails MIME ( Multipurpose Internet Mail Extensions ).
Vous pouvez aussi vous renseigner sur le site GNU

Si on donne, via le formulaire web, une valeur à l'expediteur (From:) comme celle-ci : "onemail@onedomain.com%0AContent-Type:multipart/mixed;%20boundary=myBoundary;%0A--myBoundary%0AContent-Type:text/html%0A%0A<u>Un message souligné</u>%0A--myBoundary--"
L'email peut ressembler par exemple à ceci :

To: monami@sondomaine.com
Subject: Ce sujet est fixé par le webmaster du site, dans son formulaire
From: onemail@onedomaine.com
Content-Type: multipart/mixed; boundary="MyBoundary"
--MyBoundary
Content-Type: text/html

<u>Un message souligné</u>
--MyBoundary--

Voici ici le texte que le webmaster avait initialement fixé, dans son formulaire

Selon la RFC, la valeur de "boundary=" peut etre n'importe quoi. Le boundary est le séparateur qui va séparer les types contenu dans le multipart/mixed.
Avec --leBoundaryChoisit , on définit le début d'un nouveau type. C'est le boundary d'ouverture
Avec --leBoundaryChoisit-- , on définit la fin du type en cours. C'est le boundary de fermeture

Et le fait est que tout ce qui se trouve après un boundary de fermeture, est tout simplement ignoré. Dans notre cas "Voici ici le texte que le webmaster avait initialement fixé, dans son formulaire" se trouve après --MyBoundary-- , et est donc purement ignoré, et non affiché.

Nous avons donc envoyé à quelqu'un, un email au format html, alors que le webmaster s'attendait à un message fixe. Seul le sujet nous trahi ici. Mais celui ci n'est pas effacable totallement, il est juste complétable. Il faut savoir par contre que certains webmails n'afficheront que le dernier "Subject:" rencontré, celui injecté.

Au niveau des solutions, il y en a plusieurs :

  • Utiliser une classe non vulnérable à ce type d'attaque
  • Utiliser des patchs, comme le patch Suhosin
  • Le mod Security d'Apache peut filtrer le contenu de $_POST et $_GET à la recherche de chaines telles que "Cc:" ou "Bcc:"
  • Un filtrage php à base de preg_replace()

Pour plus d'infos, vous pouvez lire http://www.mailinjection.com
Un article parallèle sur les injections mail();

 

Merci à julienpauli pour sont article

 

Commenter cet article