[couverture de Linux Magazine 115]

Perles de Mongueurs (43)

Article publié dans Linux Magazine 115, avril 2009.

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

La perle de ce mois-ci a été rédigée par Michael Scherer (misc).

Jouer des tours pendables avec HTTP::Proxy, Image::Magick et Netfilter

HTTP::Proxy est un outil merveilleux. Simple module permettant de faire des proxys HTTP, il autorise des modifications et des améliorations à la volée des pages web. Par exemple, il peut servir à remplacer automatiquement les gros mots par de prudes astérisques, ou supprimer les publicités ou le JavaScript sans peine à grands coups d'expressions rationnelles.

Bien sûr, on peut aussi s'en servir pour modifier de façon plus ludique les pages, et tout ceci à l'insu de vos utilisateurs. Par exemple, on peut modifier les images à la volée, et grâce à Netfilter, sans avoir à modifier la configuration des clients. L'idée n'est pas de moi, elle vient de Peter Stevens, qui explique comment faire pareil avec Squid et mogrify sur http://www.ex-parrot.com/pete/upside-down-ternet.html.

Tout d'abord, regardons le code du proxy en lui-même :

    #!/usr/bin/perl
    use strict;
    use warnings;

    use HTTP::Proxy;
    use HTTP::Proxy::BodyFilter::simple;
    use HTTP::Proxy::BodyFilter::complete;
    use Proc::Daemon;
    use Image::Magick;

    my $proxy = HTTP::Proxy->new(
        port => 3128,
        host => '0.0.0.0',
    );

    Proc::Daemon::Init();

    $proxy->push_filter(
        path     => qr/\.(jpg|png|gif)$/,
        mime     => qr|^image/|,
        response => HTTP::Proxy::BodyFilter::complete->new,
        response => HTTP::Proxy::BodyFilter::simple->new(
            filter => sub {
                my ( $self, $dataref, $message, $protocol, $buffer ) = @_;
                return if $buffer;

                my $image = Image::Magick->new();
                $image->BlobToImage($$dataref);

                $image->Blur( radius => 1, channel => "All", sigma => 1 );

                $image->Rotate(180);

                $$dataref = $image->ImageToBlob();
            }
        )
    );

    $proxy->start;

On commence par créer l'objet $proxy, sur le port TCP 3128, en écoute sur toutes les interfaces (par défaut, le proxy écoute sur l'interface localhost, pour des raisons de sécurité). Afin de ne pas bloquer la console, on transforme le script en un démon bien éduqué, qui libère le tty et rends la main, via l'appel à Proc::Daemon::Init(). Une autre solution, si on s'intéresse aux logs éventuelles, est de lancer le proxy dans une session screen.

Ensuite, on ajoute deux filtres sur les réponses HTTP correspondant à des images.

Le premier filtre utilise HTTP::Proxy::BodyFilter::complete, afin de stocker tout le contenu de l'image dans un buffer en mémoire. En effet, HTTP::Proxy travaille par morceau de requêtes, à la volée, afin de ne pas consommer trop de mémoire et de ne pas ralentir trop la navigation.

Comme expliqué dans la documentation, les filtres suivants doivent vérifier le contenu de la variable $buffer pour détecter la dernière requête, celle qui contient toutes les données dans $$dataref. $buffer est une référence à une chaîne dans laquelle un filtre peut décider de laisser des données qu'il traitera la prochaine fois qu'il sera appelé. Quand $buffer est undef, c'est le dernier tour, le filtre doit traiter les données. Ici, le filtre complete remplit son $$buffer au fur et à mesure, et lors du dernier tour, le copie intégralement dans son $$dataref, qui sera ensuite passé à notre second filtre.

Ensuite, grâce à Image::Magick, un module Perl de manipulation d'image basé sur le logiciel du même nom, le deuxième filtre va appliquer la modification de notre choix à l'image. Dans notre exemple, nous allons tourner l'image à 180°, et rajouter un peu de flou gaussien pour un meilleur effet.

Et enfin, nous démarrons la boucle principale du serveur.

Pour le tester, il suffit de configurer notre navigateur pour utiliser http://serveur:3128/ comme proxy.

Maintenant, la seconde partie consiste à forcer l'utilisation du proxy par votre victime, via l'utilisation de Netfilter. Il est également possible d'utiliser pf, ipf ou votre pare-feu favori bien sûr.

Le secret de fonctionnement d'un proxy transparent, c'est qu'au niveau HTTP, depuis la version 1.1 du protocole (RFC 2616), une requête vers un proxy ou vers le serveur final, c'est la même chose. Grâce au champ Host de l'entête, le proxy peut déterminer la destination et le site web à visiter par lui même. Il suffit donc de rediriger tout le trafic web vers le port 3128 de notre proxy, via une règle Netfilter.

Dans le cas d'une architecture simple, avec le proxy tournant directement sur la passerelle, sous un système linux récent, la commande pour rediriger le flux web de la machine 1.2.3.4 est la suivante :

    iptables -t nat -I PREROUTING -s 1.2.3.4 -p tcp --dport 80 -j REDIRECT --to-ports 3128

Et voilà, vous pouvez maintenant faire le test, ça marche sans modifier votre navigateur.

Néanmoins, faites attention.

Les divers tests que j'ai menés montre que HTTP::Proxy n'est pas configuré par défaut pour avoir des dizaines d'utilisateurs simultanés, et le traitement d'image bloque souvent des processus, ce qui aboutit à des coupures du proxy, qui s'arrête si il dépasse la limite par défaut de 12 processus simultanés. Et plus de 12 images en simultané, c'est courant, surtout quand on commence à aller voir un album photo.

De plus, le traitement en mémoire des images n'a pas de limite, ce qui peut aboutir à une surconsommation de la ram.

Enfin, n'oubliez pas, les blagues les plus drôles sont souvent les plus courtes.

Le changement de la limite des processus de façon propre est laissé à titre d'exercice au lecteur, tout comme la protection contre l'épuisement des ressources.

À vous !

Envoyez vos perles à perles@mongueurs.net, elles seront peut-être publiées dans un prochain numéro de Linux Magazine.

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]