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/lib/rex/proto/dcerpc/svcctl/client.rb
Views: 11766
1
# -*- coding: binary -*-
2
module Rex
3
4
###
5
# This module implements MSRPC functions that control creating, deleting,
6
# starting, stopping, and querying system services.
7
###
8
module Proto::DCERPC::SVCCTL
9
10
require 'windows_error'
11
require 'windows_error/win32'
12
NDR = Rex::Encoder::NDR
13
14
15
class Client
16
17
include WindowsError::Win32
18
include Msf::Exploit::Windows_Constants
19
20
attr_accessor :dcerpc_client
21
22
def initialize(dcerpc_client)
23
self.dcerpc_client = dcerpc_client
24
end
25
26
# Returns the Windows Error Code in numeric format
27
#
28
# @param raw_error [String] the raw error code in binary format.
29
#
30
# @return [Integer] the Windows Error Code integer.
31
def error_code(raw_error)
32
raw_error.unpack('V').first
33
end
34
35
# Calls OpenSCManagerW() to obtain a handle to the service control manager.
36
#
37
# @param rhost [String] the target host.
38
# @param access [Integer] the access flags requested.
39
#
40
# @return [Array<String,Integer>] the handle to the service control manager or nil if
41
# the call is not successful and the Windows error code
42
def openscmanagerw(rhost, access = SC_MANAGER_ALL_ACCESS)
43
scm_handle = nil
44
scm_status = nil
45
stubdata =
46
NDR.uwstring("\\\\#{rhost}") +
47
NDR.long(0) +
48
NDR.long(access)
49
begin
50
response = dcerpc_client.call(OPEN_SC_MANAGER_W, stubdata)
51
if response
52
scm_status = error_code(response[20,4])
53
if scm_status == ERROR_SUCCESS
54
scm_handle = response[0,20]
55
end
56
end
57
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
58
elog('Error getting scm handle', error: e)
59
end
60
61
[scm_handle, scm_status]
62
end
63
64
# Calls CreateServiceW() to create a system service. Returns a handle to
65
# the service on success, or nil.
66
#
67
# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).
68
# @param service_name [String] the service name.
69
# @param display_name [String] the display name.
70
# @param binary_path [String] the path of the binary to run.
71
# @param opts [Hash] arguments for CreateServiceW()
72
# @option opts [Integer] :access (SERVICE_ALL_ACCESS) the access level.
73
# @option opts [Integer] :type (SERVICE_WIN32_OWN_PROCESS ||
74
# SERVICE_INTERACTIVE_PROCESS) the type of service.
75
# @option opts [Integer] :start (SERVICE_DEMAND_START) the start options.
76
# @option opts [Integer] :errors (SERVICE_ERROR_IGNORE) the error options.
77
# @option opts [Integer] :load_order_group (0) the load order group.
78
# @option opts [Integer] :dependencies (0) the dependencies of the service.
79
# @option opts [Integer] :service_start (0)
80
# @option opts [Integer] :password1 (0)
81
# @option opts [Integer] :password2 (0)
82
# @option opts [Integer] :password3 (0)
83
# @option opts [Integer] :password4 (0)
84
#
85
# @return [String, Integer] a handle to the created service, windows
86
# error code.
87
def createservicew(scm_handle, service_name, display_name, binary_path, opts)
88
default_opts = {
89
:access => SERVICE_ALL_ACCESS,
90
:type => SERVICE_WIN32_OWN_PROCESS || SERVICE_INTERACTIVE_PROCESS,
91
:start => SERVICE_DEMAND_START,
92
:errors => SERVICE_ERROR_IGNORE,
93
:load_order_group => 0,
94
:dependencies => 0,
95
:service_start => 0,
96
:password1 => 0,
97
:password2 => 0,
98
:password3 => 0,
99
:password4 => 0
100
}.merge(opts)
101
102
svc_handle = nil
103
svc_status = nil
104
stubdata = scm_handle +
105
NDR.wstring(service_name) +
106
NDR.uwstring(display_name) +
107
NDR.long(default_opts[:access]) +
108
NDR.long(default_opts[:type]) +
109
NDR.long(default_opts[:start]) +
110
NDR.long(default_opts[:errors]) +
111
NDR.wstring(binary_path) +
112
NDR.long(default_opts[:load_order_group]) +
113
NDR.long(default_opts[:dependencies]) +
114
NDR.long(default_opts[:service_start]) +
115
NDR.long(default_opts[:password1]) +
116
NDR.long(default_opts[:password2]) +
117
NDR.long(default_opts[:password3]) +
118
NDR.long(default_opts[:password4])
119
begin
120
response = dcerpc_client.call(CREATE_SERVICE_W, stubdata)
121
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
122
elog('Error creating service', error: e)
123
end
124
125
if response
126
svc_status = error_code(response[24,4])
127
if svc_status == ERROR_SUCCESS
128
svc_handle = response[4,20]
129
end
130
end
131
132
return svc_handle, svc_status
133
end
134
135
# Calls ChangeServiceConfig2() to change the service description.
136
#
137
# @param svc_handle [String] the service handle to change.
138
# @param service_description [String] the service description.
139
#
140
# @return [Integer] Windows error code
141
def changeservicedescription(svc_handle, service_description)
142
svc_status = nil
143
stubdata =
144
svc_handle +
145
NDR.long(SERVICE_CONFIG_DESCRIPTION) +
146
NDR.long(1) + # lpInfo -> *SERVICE_DESCRIPTION
147
NDR.long(0x0200) + # SERVICE_DESCRIPTION struct
148
NDR.long(0x04000200) +
149
NDR.wstring(service_description)
150
begin
151
response = dcerpc_client.call(CHANGE_SERVICE_CONFIG2_W, stubdata) # ChangeServiceConfig2
152
svc_status = error_code(response)
153
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
154
elog('Error changing service description', error: e)
155
end
156
157
svc_status
158
end
159
160
161
# Calls CloseHandle() to close a handle.
162
#
163
# @param handle [String] the handle to close.
164
#
165
# @return [Integer] Windows error code
166
def closehandle(handle)
167
svc_status = nil
168
begin
169
response = dcerpc_client.call(CLOSE_SERVICE_HANDLE, handle)
170
if response
171
svc_status = error_code(response)
172
end
173
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
174
elog('Error closing service handle', error: e)
175
end
176
177
svc_status
178
end
179
180
# Calls OpenServiceW to obtain a handle to an existing service.
181
#
182
# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).
183
# @param service_name [String] the name of the service to open.
184
# @param access [Integer] the level of access requested (default is maximum).
185
#
186
# @return [String, nil] the handle of the service opened, or nil on failure.
187
def openservicew(scm_handle, service_name, access = SERVICE_ALL_ACCESS)
188
svc_handle = nil
189
svc_status = nil
190
stubdata = scm_handle + NDR.wstring(service_name) + NDR.long(access)
191
begin
192
response = dcerpc_client.call(OPEN_SERVICE_W, stubdata)
193
if response
194
svc_status = error_code(response[20,4])
195
if svc_status == ERROR_SUCCESS
196
svc_handle = response[0,20]
197
end
198
end
199
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
200
elog('Error opening service handle', error: e)
201
end
202
203
svc_handle
204
end
205
206
# Calls StartService() on a handle to an existing service in order to start
207
# it. Returns true on success, or false.
208
#
209
# @param svc_handle [String] the handle of the service (from {#openservicew}).
210
# @param args [Array] an array of arguments to pass to the service (or nil)
211
#
212
# @return [Integer] Windows error code
213
def startservice(svc_handle, args=[])
214
svc_status = nil
215
216
if args.empty?
217
stubdata = svc_handle + NDR.long(0) + NDR.long(0)
218
else
219
# This is just an arbitrary "pointer" value, gonna match it to what the real version uses
220
id_value = 0x00000200
221
222
stubdata = svc_handle
223
stubdata += NDR.long(args.length) + NDR.long(id_value) + NDR.long(args.length)
224
225
# Encode an id value for each parameter
226
args.each do
227
id_value += 0x04000000
228
stubdata += NDR.long(id_value)
229
end
230
231
# Encode the values now
232
args.each do |arg|
233
# We can't use NDR.uwstring here, because we need the "id" values to come first
234
stubdata += NDR.long(arg.length + 1) + NDR.long(0) + NDR.long(arg.length + 1)
235
236
# Unicode string
237
stubdata += Rex::Text.to_unicode(arg + "\0")
238
239
# Padding
240
if((arg.length % 2) == 0)
241
stubdata += Rex::Text.to_unicode("\0")
242
end
243
end
244
end
245
246
begin
247
response = dcerpc_client.call(0x13, stubdata)
248
if response
249
svc_status = error_code(response)
250
end
251
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
252
elog('Error starting service', error: e)
253
end
254
255
svc_status
256
end
257
258
# Stops a running service.
259
#
260
# @param svc_handle [String] the handle of the service (from {#openservicew}).
261
#
262
# @return [Integer] Windows error code
263
def stopservice(svc_handle)
264
dce_controlservice(svc_handle, SERVICE_CONTROL_STOP)
265
end
266
267
# Controls an existing service.
268
#
269
# @param svc_handle [String] the handle of the service (from {#openservicew}).
270
# @param operation [Integer] the operation number to perform (1 = stop
271
# service; others are unknown).
272
#
273
# @return [Integer] Windows error code
274
def controlservice(svc_handle, operation)
275
svc_status = nil
276
begin
277
response = dcerpc_client.call(CONTROL_SERVICE, svc_handle + NDR.long(operation))
278
if response
279
svc_status = error_code(response[28,4])
280
end
281
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
282
elog('Error controlling service', error: e)
283
end
284
285
svc_status
286
end
287
288
# Calls DeleteService() to delete a service.
289
#
290
# @param svc_handle [String] the handle of the service (from {#openservicew}).
291
#
292
# @return [Integer] Windows error code
293
def deleteservice(svc_handle)
294
svc_status = nil
295
begin
296
response = dcerpc_client.call(DELETE_SERVICE, svc_handle)
297
if response
298
svc_status = error_code(response)
299
end
300
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
301
elog('Error deleting service', error: e)
302
end
303
304
svc_status
305
end
306
307
# Calls QueryServiceStatus() to query the status of a service.
308
#
309
# @param svc_handle [String] the handle of the service (from {#openservicew}).
310
#
311
# @return [Integer] Returns 0 if the query failed (i.e.: a state was returned
312
# that isn't implemented), 1 if the service is running, and
313
# 2 if the service is stopped.
314
def queryservice(svc_handle)
315
ret = 0
316
317
begin
318
response = dcerpc_client.call(QUERY_SERVICE_STATUS, svc_handle)
319
if response[0,9] == "\x10\x00\x00\x00\x04\x00\x00\x00\x01"
320
ret = 1
321
elsif response[0,9] == "\x10\x00\x00\x00\x01\x00\x00\x00\x00"
322
ret = 2
323
end
324
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
325
elog('Error deleting service', error: e)
326
end
327
328
ret
329
end
330
331
end
332
end
333
end
334
335
336