Bind shell, Reverse shell et Forward shell - Partie 3

De nos jours, les exécutions de code à distance ou RCE (Remote Code Execution) font partie des attaques web les plus redoutées. Les risques qui en découlent sont critiques, car ce type de vulnérabilité permet en somme une prise de contrôle du système et d’être présent au sein du réseau de la victime selon le contexte du serveur : dédié, mutualisé, interne etc.).

Au sein de cet article, il sera expliqué :

• Ce qu’est un interpréteur de commandes interactif et pourquoi un attaquant nécessite ou préfère son utilisation lorsqu’il dispose d’une vulnérabilité de type RCE.

• S’en suivra une analyse de deux manières d’obtenir un interpréteur en ligne de commandes interactif avec une RCE et des options existantes pour contraindre son obtention à un attaquant.

• Enfin, il sera expliqué une méthode spécifique appelée Forward shell qui peut être mise en place lorsque les deux autres font défaut.

  1. Bind Shell
  2. Reverse Shell
  3. Forward Shell

Le Forward shell

Concept du Named pipe

Avant de présenter cette dernière méthode, il est nécessaire d’expliquer le concept de Named Pipe.
Les Pipes permettent à différents processus d’interagir.

Prenons la commande suivante : head -5 <file> | grep “s”

Lorsque l’interpréteur recevra cette commande, il s’arrêtera avant le premier caractère pipe (|) et redirigera la sortie de l’instruction head -5 en tant qu’entrée pour l’instruction grep. Ainsi, parmi les 5 premières lignes du fichier, seules celles contenant le caractère « s » seront affichées par la console.

Les processus head et grep n’ont pas été conçus pour travailler ensemble, mais il est pourtant possible de les utiliser ensemble à l’aide des Pipes.

L’exemple présenté est un Unamed Pipe. Dans ce cas-là, le Pipe n’existe qu’au sein du noyau et ne peut pas être accessible par l’interpréteur de commandes.
À l’inverse, les Named Pipes sont des fichiers qui sont directement accessibles comme n’importe quel autre fichier. Ils sont créés avec la commande suivante :

mkfifo <name of named pipe file>

Créons un Named Pipe et écrivons une chaine de caractères à l’intérieur afin de mieux comprendre son utilité :

Création d’un Named Pipe

Le processus echo est ici bloqué, car le Named Pipe va attendre que l’entrée et la sortie soient connectées. En lisant le contenu du Named Pipe créé, l’entrée et la sortie seront connectées et le processus echo ne sera plus bloqué. La chaine de caractères sera alors affichée en lisant le contenu du fichier :

Création d’un Named Pipe

En sachant désormais ce qu’est un Named Pipe, il devient possible de réfléchir à la réalisation d’un interpréteur de commandes interactif sur la base de la commande suivante :

Commande permettant de créer la base d’un interpréteur de commandes basique interactif

Ainsi, le Named pipe /tmp/input agira comme un interpréteur de commandes et leur sortie pourra être consultée via le fichier /tmp/output :

Création de la base d’un interpréteur de commandes interactif avec
des Named Pipes

De plus, l’interpréteur de commandes est interactif, car les commandes s’exécutent toutes au sein d’un même fichier et donc, au sein du même interpréteur de commandes :

Création de la base d’un interpréteur de commandes interactif avec
des Named Pipes

Étant donné que l’interpréteur réalisé en local sur notre machine attaquante à l’aide de Named Pipes peut être interactif, il peut également l’être via une session HTTP où chaque commande entrée via la RCE sera envoyée au fichier Named Pipe input. Puisque le résultat de chaque commande nous est retourné dans le cadre de notre exemple, il est possible de se baser sur celle-ci pour la lecture du fichier output.

Il est désormais temps de voir comment il est possible d’implémenter cette méthode via une RCE en reprenant notre exemple.

Cette méthode est appelée Forward shell et a été découverte par IppSec. Il a par la même occasion réalisé un outil permettant de réaliser un interpréteur de commandes interactif via cette méthode :

https://github.com/IppSec/forward-shell

L’exploitation du Forward shell

Le but ici va être de créer un script. Ce script reposera sur les 3 fonctions suivantes :

  • La première permet la création d’un interpréteur de commandes interactif basique avec un Named
  • Pipe sur le système en reprenant la commande présentée précédemment.
  • La seconde fonction doit permettre d’insérer des commandes qui seront envoyées au Named Pipe.
  • Enfin, la dernière doit permettre de récupérer la sortie de la commande.

Celles-ci peuvent être résumées au sein du schéma suivant :

Schéma des 3 fonctions générales au sein du script

Ces 3 fonctions ont un but différent, mais elles requièrent toutes d’envoyer une commande via la RCE identifiée. Ainsi, une fonction sera consacrée à l’envoi de commandes pour la première et dernière fonction. La fonction permettant d’insérer les commandes directement au sein du Named Pipe via la RCE se suffira ainsi à elle-même.

Afin de construire ces fonctions, il est possible d’examiner la requête permettant d’exécuter du code à l’aide d’un proxy HTTP tel que Burp Suite, mitmproxy ou Zap dans le but de pouvoir la retranscrire au sein d’un script :

Requête permettant d’exploiter la RCE interceptée via l’outil Burp Suite

L’exploitation de la RCE se fait donc à via l’envoi d’une requête POST et de la variable ip contenant notre charge utile.
Nous devons donc réaliser les 4 fonctions suivantes :

1. send_cmd : Processus d’envoi de commandes simple via la RCE
2. create_shell : Création de l’interpréteur de commandes basique
3. write_cmd : Processus d’envoi de commandes au sein du Named Pipe via la RCE
4. read_cmd : Lecture de la sortie de la dernière commande exécutée

Expliquons alors le code de chaque fonction composant notre script :

1. Send_cmd

Fonction send_cmd

Cette fonction prend en paramètre la commande que l’on souhaite envoyer. Elle permet ainsi d’envoyer une requête POST avec la charge utile ; {$cmd} permettant d’exécuter du code sur le serveur. L’ajout du paramètre timeout est très important, car lors de la création du Named Pipe, le serveur ne répond pas et la requête réalisée ne se termine jamais malgré l’exécution de la commande.

2. Create_shell

Fonction create_shell

Cette fonction sert simplement à envoyer la commande permettant de créer l’interpréteur de commandes basique à l’aide de la fonction présentée ci-dessus. L’ajout d’une exception est nécessaire, car comme évoqué précédemment, le retour de la fonction send_cmd lors de la création du Named Pipe sera un timeout forcé de la requête.

3. Write_cmd

Fonction write_cmd

Cette fonction reprend le même principe que la première en prenant en paramètre la commande pour l’envoyer via une requête POST. Cependant, au lieu de simplement exécuter la commande reçue, la fonction permet de l’écrire au sein du Named Pipe à l’aide du programme echo.

4. Read_cmd

Fonction read_cmd

La dernière fonction permet d’envoyer la commande pour lire le fichier output en utilisant le binaire cat. Cette instruction est ensuite envoyée à la fonction send_cmd. Elle retourne alors le résultat de la réponse HTTP retournée par le serveur contenant le résultat de la lecture du fichier output.
Toutes les fonctions étant présentées, il convient désormais de présenter le main du script.

Ce document est la propriété du cabinet XMCO. Toute reproduction est strictement interdite.

Le main du script


La première instruction est de créer l’interpréteur de commandes basique en appelant la fonction
create_shell (cf. 1).

Ensuite, en raison de l’utilisation du même fichier pour stocker le résultat de l’exécution des commandes envoyées, il est nécessaire de définir une instruction afin de vider ce fichier (cf. 2). Cela permet de n’afficher que l’exécution de la dernière commande envoyée.

Puis, l’interpréteur de commandes obtenu est rendu totalement interactif avec l’envoi de la commande python (cf. 3). Une boucle est par la suite définie pour permettre à l’utilisateur d’envoyer des commandes via un invité de commandes basique (cf. 4). Celles-ci sont envoyées à la fonction write_cmd (cf. 5) et leur résultat est affiché grâce à la fonction read_cmd (cf. 6).

Du fait que l’on se base sur la réponse HTTP du serveur pour afficher la sortie de la commande, une recherche de chaine de caractères basée sur une expression régulière (regex) est définie afin de n’afficher que le résultat de la commande et non la réponse HTTP entière (cf. 7). Une fois la réponse affichée, le fichier output est vidé (cf. 8).

Il faut savoir que la manière d’exploiter une RCE variera selon les cas et les fonctions permettant d’envoyer une commande seront donc à modifier selon le contexte de la vulnérabilité. De la même manière, l’affichage de la réponse d’exécution de la commande ne sera pas affichée de la même manière et l’expression régulière devra ainsi être modifiée.

Le script exécuté permet ainsi d’obtenir un interpréteur de commandes basique interactif uniquement via une session HTTP :

L’interpréteur de commandes obtenu après l’exécution du
script est interactif

Le tableau suivant résume les différentes valeurs du paramètre ip pour les différentes requêtes HTTP envoyées dans l’ordre :


Avec le script interactif obtenu, il a été permis d’observer que pour notre exemple, l’utilisateur www-da-ta peut exécuter le binaire php avec des droits root.


Ainsi, il est possible d’élever les privilèges de l’utilisateur simplement avec la commande suivante :

Élévation de privilèges sur le serveur compromis

Avec l’interpréteur de commandes interactif, il a donc été possible d’élever les privilèges de l’utilisateur actuel afin de compromettre totalement le serveur. En restant sur l’invit de commandes web non interactif, il n’aurait pas été possible de voir les privilèges sudo.

Méthode de contournement

Empêcher la création de Named Pipe à un attaquant permettrait de prévenir l’exploitation de cette méthode. Cela s’avère cependant très difficile à réaliser en pratique. En effet, ceci signifierait restreindre l’exécution du binaire mkfifo aux utilisateurs root par exemple.

Cette méthode de contournement ne fera malheureusement que ralentir un attaquant, car celui-ci pourra charger son propre binaire mkfifo sur le serveur compromis avec des droits adaptés. Il est en effet possible de charger des binaires sur des serveurs en les convertissant en Base64 au préalable.

Cette méthode d’obtention d’interpréteur de commandes interactif à distance est donc impossible à restreindre.

Cependant, comme expliqué durant cet article, le serveur n’affichera pas toujours le résultat de la commande exécutée au sein de sa réponse HTTP lors de l’exploitation d’une RCE.

Un attaquant pourrait contourner ce problème en redirigeant la réponse de la commande non pas dans un fichier /tmp/output, mais au sein d’un fichier présent dans le répertoire du serveur web accessible par l’utilisateur.

Cependant, cela signifie devoir connaitre le chemin exact de ce répertoire. Et sans réponse du serveur web, seules des assomptions peuvent être faites. En effet, le chemin et le nom du répertoire web par défaut peuvent être modifiés rendant particulièrement difficile l’exploitation de cette méthode de Forward shell si aucune réponse de ses commandes n’est affichée à l’attaquant.

Du fait que cette méthode nécessite de pouvoir écrire sur le système vulnérable, une méthode de contournement possible serait d’exécuter les différents services du serveur avec un utilisateur disposant de droits restreints. Si celui-ci ne peut pas écrire sur le système de fichiers de la machine, le Forward shell ne pourrait pas être obtenu par un attaquant.

Tableau récapitulatif

Voici un tableau récapitulatif des différentes méthodes permettant d’obtenir un interpréteur de commandes interactif lorsqu’une vulnérabilité de type RCE est identifiée sur une application web :

Un WAF peut également être placé devant les serveurs web afin de bloquer des charges utiles ou des caractères couramment utilisés par les attaquants. Des sondes de type IPS peuvent aussi être placées sur le réseau dans le même effet. Ce n’est cependant qu’une mesure complémentaire de durcissement et non une solution de remédiation.

XMCO

Découvrir d'autres articles

  • Pentest

    Des dés aux données : de l’importance des nombres aléatoires (partie 2/2)

    Lire l'article
  • Pentest

    Des dés aux données : de l’importance des nombres aléatoires (partie 1/2)

    Lire l'article
  • Pentest

    Tour d’horizon des attaques et vulnérabilités sur le gestionnaire de mots de passe KeePass 

    Lire l'article