Skip to content

Retab/pr/2001 #1

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions lib/msf/core/exploit/dcerpc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'msf/core/exploit/dcerpc_epm'
require 'msf/core/exploit/dcerpc_mgmt'
require 'msf/core/exploit/dcerpc_lsa'
require 'msf/core/exploit/dcerpc_services'

module Msf

Expand Down Expand Up @@ -32,6 +33,7 @@ module Exploit::Remote::DCERPC
include Exploit::Remote::DCERPC_EPM
include Exploit::Remote::DCERPC_MGMT
include Exploit::Remote::DCERPC_LSA
include Exploit::Remote::DCERPC_SERVICES

def initialize(info = {})
super
Expand Down
229 changes: 229 additions & 0 deletions lib/msf/core/exploit/dcerpc_services.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# -*- coding: binary -*-
module Msf

###
# This module implements MSRPC functions that control creating, deleting, starting, stopping, and querying system services.
###
module Exploit::Remote::DCERPC_SERVICES

NDR = Rex::Encoder::NDR


# Calls OpenSCManagerW() to obtain a handle to the service control manager.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param rhost [String] the target host.
# @param access [Fixnum] the access flags requested.
#
# @return [String] the handle to the service control manager.
def dce_openscmanagerw(dcerpc, rhost, access = 0xF003F)
scm_handle = nil
scm_status = nil
stubdata =
NDR.uwstring("\\\\#{rhost}") +
NDR.long(0) +
NDR.long(access)
response = dcerpc.call(0x0f, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
scm_handle = dcerpc.last_response.stub_data[0,20]
scm_status = dcerpc.last_response.stub_data[20,4]
end

if scm_status.to_i != 0
scm_handle = nil
end
return scm_handle
end


# Calls CreateServiceW() to create a system service. Returns a handle to the service on success, or nil.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param scm_handle [String] the SCM handle (from dce_openscmanagerw()).
# @param service_name [String] the service name.
# @param display_name [String] the display name.
# @param binary_path [String] the path of the binary to run.
# @param opts [Hash] a hash containing the following keys and values:
# access [Fixnum] the access level (default is maximum).
# type [Fixnum] the type of service (default is interactive, own process).
# start [Fixnum] the start options (default is on demand).
# errors [Fixnum] the error options (default is ignore).
# load_order_group [Fixnum] the load order group.
# dependencies [Fixnum] the dependencies of the service.
# service_start [Fixnum]
# password1 [Fixnum]
# password2 [Fixnum]
# password3 [Fixnum]
# password4 [Fixnum]
#
# @return [String] a handle to the created service.
def dce_createservicew(dcerpc, scm_handle, service_name, display_name, binary_path, opts)
default_opts = {
:access => 0x0F01FF, # Maximum access.
:type => 0x00000110, # Interactive, own process.
:start => 0x00000003, # Start on demand.
:errors => 0x00000000,# Ignore errors.
:load_order_group => 0,
:dependencies => 0,
:service_start => 0,
:password1 => 0,
:password2 => 0,
:password3 => 0,
:password4 => 0
}.merge(opts)

svc_handle = nil
svc_status = nil
stubdata = scm_handle +
NDR.wstring(service_name) +
NDR.uwstring(display_name) +
NDR.long(default_opts[:access]) +
NDR.long(default_opts[:type]) +
NDR.long(default_opts[:start]) +
NDR.long(default_opts[:errors]) +
NDR.wstring(binary_path) +
NDR.long(default_opts[:load_order_group]) +
NDR.long(default_opts[:dependencies]) +
NDR.long(default_opts[:service_start]) +
NDR.long(default_opts[:password1]) +
NDR.long(default_opts[:password2]) +
NDR.long(default_opts[:password3]) +
NDR.long(default_opts[:password4])
response = dcerpc.call(0x0c, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
svc_handle = dcerpc.last_response.stub_data[4,20]
svc_status = dcerpc.last_response.stub_data[20,4]
end

if svc_status.to_i != 0
svc_handle = nil
end
return svc_handle
end

# Calls CloseHandle() to close a handle. Returns true on success, or false.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param handle [String] the handle to close.
#
# @return [Boolean] true if the handle was successfully closed, or false if not.
def dce_closehandle(dcerpc, handle)
ret = false
response = dcerpc.call(0x0, handle)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
if dcerpc.last_response.stub_data[20,4].to_i == 0
ret = true
end
end
return ret
end

# Calls OpenServiceW to obtain a handle to an existing service.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param scm_handle [String] the SCM handle (from dce_openscmanagerw()).
# @param service_name [String] the name of the service to open.
# @param access [Fixnum] the level of access requested (default is maximum).
#
# @return [String, nil] the handle of the service opened, or nil on failure.
def dce_openservicew(dcerpc, scm_handle, service_name, access = 0xF01FF)
svc_handle = nil
svc_status = nil
stubdata = scm_handle + NDR.wstring(service_name) + NDR.long(access)
response = dcerpc.call(0x10, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
svc_handle = dcerpc.last_response.stub_data[0,20]
svc_status = dcerpc.last_response.stub_data[20,4]
end

if svc_status.to_i != 0
svc_handle = nil
end
return svc_handle
end

# Calls StartService() on a handle to an existing service in order to start it. Returns true on success, or false.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param svc_handle [String] the handle of the service to start (from dce_openservicew()).
# @param magic1 [Fixnum] an unknown value.
# @param magic2 [Fixnum] another unknown value.
#
# @return [Boolean] true if the service was successfully started, false if it was not.
def dce_startservice(dcerpc, svc_handle, magic1 = 0, magic2 = 0)
ret = false
stubdata = svc_handle + NDR.long(magic1) + NDR.long(magic2)
response = dcerpc.call(0x13, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
if dcerpc.last_response.stub_data[0,4].to_i == 0
ret = true
end
end
return ret
end

# Stops a running service.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param svc_handle [String] the handle of the service to stop (from dce_openservicew()).
#
# @return [Boolean] true if the service was successfully stopped, false if it was not.
def dce_stopservice(dcerpc, svc_handle)
return dce_controlservice(dcerpc, svc_handle, 1)
end

# Controls an existing service.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param svc_handle [String] the handle of the service to control (from dce_openservicew()).
# @param operation [Fixnum] the operation number to perform (1 = stop service; others are unknown).
#
# @return [Boolean] true if the operation was successful, false if it was not.
def dce_controlservice(dcerpc, svc_handle, operation)
ret = false
response = dcerpc.call(0x01, svc_handle + NDR.long(operation))
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
if dcerpc.last_response.stub_data[28,4].to_i == 0
ret = true
end
end
return ret
end

# Calls DeleteService() to delete a service.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param svc_handle [String] the handle of the service to delete (from dce_openservicew()).
#
# @return [Boolean] true if the service was successfully deleted, false if it was not.
def dce_deleteservice(dcerpc, svc_handle)
ret = false
response = dcerpc.call(0x02, svc_handle)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
if dcerpc.last_response.stub_data[0,4].to_i == 0
ret = true
end
end
return ret
end

# Calls QueryServiceStatus() to query the status of a service.
#
# @param dcerpc [Rex::Proto::DCERPC::Client] the DCERPC client to use.
# @param svc_handle [String] the handle of the service to query (from dce_openservicew()).
#
# @return [Fixnum] Returns 0 if the query failed (i.e.: a state was returned that isn't implemented),
# 1 if the service is running, and 2 if the service is stopped.
def dce_queryservice(dcerpc, svc_handle)
ret = 0
response = dcerpc.call(0x06, svc_handle)
if response[0,9] == "\x10\x00\x00\x00\x04\x00\x00\x00\x01"
ret = 1
elsif response[0,9] == "\x10\x00\x00\x00\x01\x00\x00\x00\x00"
ret = 2
end
return ret
end

end
end
53 changes: 53 additions & 0 deletions lib/msf/core/exploit/smb/psexec_svc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: binary -*-
require 'digest'

module Msf

####
# This allows one to extract PSEXESVC.EXE from Microsoft Sysinternal's
# PsExec.exe.
####
module Exploit::Remote::SMB::PsexecSvc

# Returns the bytes for PSEXESVC.EXE on success, or nil on error.
#
# @param psexec_path [String] the local filesystem path to PsExec.exe
# @param verbose [Boolean] true if verbosity is desired, false if otherwise.
#
# @return [String] the bytes corresponding to PSEXESVC.EXE.
def extract_psexesvc(psexec_path, verbose = false)
read_offset = 0
bytes_to_read = 0
if verbose
print_status("Calculating SHA-256 hash of #{psexec_path}...")
end
hash = Digest::SHA256.file(psexec_path).hexdigest
# If we were given a path to v1.98 (the latest as of the
# time of this writing), then we set the read offset and
# file size accordingly. Otherwise, we fail. Future
# versions of PsExec can be handled by adding the new
# hash, offset, and size to this code.
if hash == 'f8dbabdfa03068130c277ce49c60e35c029ff29d9e3c74c362521f3fb02670d5'
read_offset = 193288
bytes_to_read = 181064
else
if verbose
print_error("Hash is not correct!\nExpected: f8dbabdfa03068130c277ce49c60e35c029ff29d9e3c74c362521f3fb02670d5\nActual: #{hash}\nEnsure that you have PsExec v1.98.")
end
return nil
end

if verbose
print_status("File hash verified. Extracting PSEXESVC.EXE code from #{psexec_path}...")
end
# Extract the PSEXESVC.EXE code from PsExec.exe.
hPsExec = File.open(psexec_path, 'rb')
hPsExec.seek(read_offset)
psexesvc = hPsExec.read(bytes_to_read)
hPsExec.close

return psexesvc
end

end
end
33 changes: 33 additions & 0 deletions lib/rex/proto/smb/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def smb_defaults(packet)
packet.v['TreeID'] = self.last_tree_id.to_i
packet.v['UserID'] = self.auth_user_id.to_i
packet.v['ProcessID'] = self.process_id.to_i
self.multiplex_id = (self.multiplex_id + 16) % 65536
end


Expand Down Expand Up @@ -1291,6 +1292,38 @@ def write(file_id = self.last_file_id, offset = 0, data = '', do_recv = true)
return ack
end

def write_raw(file_id, flags1, flags2, wordcount, andx_command, andx_offset, offset, write_mode, remaining, data_len_high, data_len_low, data_offset, high_offset, byte_count, data, do_recv)

pkt = CONST::SMB_WRITE_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB'])

pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
pkt['Payload']['SMB'].v['Flags1'] = flags1
pkt['Payload']['SMB'].v['Flags2'] = flags2

pkt['Payload']['SMB'].v['WordCount'] = wordcount

pkt['Payload'].v['AndX'] = andx_command
pkt['Payload'].v['AndXOffset'] = andx_offset
pkt['Payload'].v['FileID'] = file_id
pkt['Payload'].v['Offset'] = offset
pkt['Payload'].v['Reserved2'] = -1
pkt['Payload'].v['WriteMode'] = write_mode
pkt['Payload'].v['Remaining'] = remaining
pkt['Payload'].v['DataLenHigh'] = data_len_high
pkt['Payload'].v['DataLenLow'] = data_len_low
pkt['Payload'].v['DataOffset'] = data_offset
pkt['Payload'].v['HighOffset'] = high_offset
pkt['Payload'].v['ByteCount'] = byte_count

pkt['Payload'].v['Payload'] = data

ret = self.smb_send(pkt.to_s)
return ret if not do_recv

ack = self.smb_recv_parse(CONST::SMB_COM_WRITE_ANDX)
return ack
end

# Reads data from an open file handle
def read(file_id = self.last_file_id, offset = 0, data_length = 64000, do_recv = true)
Expand Down
Loading