Metasploit : Afficher la politique de mots de passe

Il n’est plus nécessaire de présenter Metasploit. Véritable trousse à outils du pentester, les nombreux modules présents permettent d’effectuer un nombre d’opérations au sein d’un seul et même outil.

Lors du pentest d’une infrastructure Windows, nous avons eu besoin de récupérer la politique de mots de passe afin de savoir combien d’essais de login peuvent être tentés avant de bloquer le compte. En cherchant parmi les modules disponibles, rien de tel n’existe, il a fallu passer par d’autres outils pour obtenir ces informations.

L’idée de développer un module pour Metasploit permettant de récupérer cette politique a donc fait son chemin, et nous avons commencé par analyser le fonctionnement des modules interrogeant un contrôleur de domaine afin de comprendre leur fonctionnement.

En analysant le module smb_enumusers, un morceau de code à retenu notre attention :


# Password information
stub = dhandle + [0x01].pack('v')
dcerpc.call(8, stub)
resp = dcerpc.last_response ? dcerpc.last_response.stub_data : nil
if(resp and resp[-4,4].unpack('V')[0] == 0)
mlen,hlen = resp[8,4].unpack('vv')
domains[domain][:pass_min] = mlen
domains[domain][:pass_min_history] = hlen
end
[...]
# Lockout Threshold
stub = dhandle + [12].pack('v')
dcerpc.call(8, stub)
resp = dcerpc.last_response ? dcerpc.last_response.stub_data : nil
if(resp and resp[-4,4].unpack('V')[0] == 0)
lduration = resp[8,8]
lwindow = resp[16,8]
lthresh = resp[24, 2].unpack('v')[0]
domains[domain][:lockout_threshold] = lthresh
domains[domain][:lockout_duration] = Rex::Proto::SMB::Utils.time_smb_to_unix(*(lduration.unpack('V2')))
domains[domain][:lockout_window] = Rex::Proto::SMB::Utils.time_smb_to_unix(*(lwindow.unpack('V2')))
end

Apparemment, ce module devrait déjà récupérer et afficher la politique de mots de passe en utilisant des appels RPC, mais ne le fait pas. Pour comprendre pourquoi, lançons Wireshark et observons ce qui est transmis entre le client et le contrôleur de domaine :

Tiens tiens, les premières requêtes sont correctement traitées par le serveur, mais certaines requêtes ne sont pas autorisées et le serveur renvoie un STATUS_ACCESS_DENIED… Pour en savoir plus, regardons le contenu d’une de ces requêtes :

Comme on peut le voir, la requête envoyée est de type SamrQueryInformationDomain (Type 8 ) et de sous-type 12 (SamrDomainLockoutInformation). En cherchant dans MSDN, on tombe sur cet article qui décrit le fonctionnement de cette requête :

http://msdn.microsoft.com/en-us/library/cc245779(PROT.13).aspx

En lisant la documentation liée, on tombe sur ceci :

Upon receiving this message, the server MUST process the data from the message subject to the following constraints:

  1. DomainHandle MUST have the required access specified in section 3.1.2.1. Otherwise, the server MUST return STATUS_ACCESS_DENIED.

OK, maintenant, nous savons que le problème provient sûrement d’un manque de droits réclamés lors de l’obtention du DomainHandle. Ce DomainHandle est obtenu lors de l’appel à la fonction SamrOpenDomain, qui doit être appelée avant tout appel aux fonctions concernant un domaine. Une nouvelle recherche dans MSDN nous indique (http://msdn.microsoft.com/en-us/library/cc245748(PROT.10).aspx) que la fonction SamrOpenDomain prend en second paramètre un champ DesiredAccess Le format de ce paramètre est défini ici : http://msdn.microsoft.com/en-us/library/cc245522(v=PROT.10).aspx

La constante qui nous intéresse est DOMAIN_READ_PASSWORD_PARAMETERS. La valeur de cette constante est donnée pour 0x00000001.

Comment vérifier si ce droit est correctement demandé lors de l’appel à SamrOpenDomain ? Wireshark va nous aider :

La valeur de DesiredAccess est positionnée à 0x00000304, comme prévu, l’accès qui nous intéresse n’a donc pas été demandé par le module et c’est donc bien pour cela que la politique de mots de passe n’est pas récupérée. En observant à nouveau le module Metasploit, on retrouve facilement l’endroit ou est définie cette valeur :


# Open
stub =
phandle +
NDR.long(0x00000304) +
NDR.long(4) +
[1,4,0].pack('CvC') +
domains[domain][:sid_raw]

Modifier cette valeur à 0x00000305 (0x00000304 (valeur initiale) + 0x00000001 (DOMAIN_READ_PASSWORD_PARAMETERS)) permet de corriger ce bug et ainsi de faire fonctionner le module de manière correcte :

msf auxiliary(smb_enumusers) > exploit
[*] XX.XX.XX.XX DOMAIN [ Administrator, Guest, krbtgt ] ( LockoutTries=5 PasswordMin=7 )

Ce bug ainsi que sa résolution ont été soumis à l’équipe de développement de Metasploit et a ainsi été corrigé.