Skip to content

Commit 8e7f12e

Browse files
author
Tod Beardsley
committed
Land rapid7#3085, service_control support
This depends on rapid7/meterpreter#77 to function
2 parents 04b5d71 + 1d70411 commit 8e7f12e

File tree

3 files changed

+120
-35
lines changed

3 files changed

+120
-35
lines changed

lib/rex/post/meterpreter/extensions/extapi/service/service.rb

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,48 +15,86 @@ module Service
1515
###
1616
class Service
1717

18+
SERVICE_OP_START = 1
19+
SERVICE_OP_PAUSE = 2
20+
SERVICE_OP_RESUME = 3
21+
SERVICE_OP_STOP = 4
22+
SERVICE_OP_RESTART = 5
23+
1824
def initialize(client)
1925
@client = client
2026
end
2127

28+
#
2229
# Enumerate all the services on the target.
30+
#
2331
def enumerate
2432
request = Packet.create_request('extapi_service_enum')
2533
response = client.send_request(request)
2634

2735
services = []
2836

29-
response.each(TLV_TYPE_EXT_SERVICE_ENUM_GROUP) { |s|
37+
response.each(TLV_TYPE_EXT_SERVICE_ENUM_GROUP) do |s|
3038
services << {
3139
:name => s.get_tlv_value(TLV_TYPE_EXT_SERVICE_ENUM_NAME),
3240
:display => s.get_tlv_value(TLV_TYPE_EXT_SERVICE_ENUM_DISPLAYNAME),
3341
:pid => s.get_tlv_value(TLV_TYPE_EXT_SERVICE_ENUM_PID),
3442
:status => s.get_tlv_value(TLV_TYPE_EXT_SERVICE_ENUM_STATUS),
3543
:interactive => s.get_tlv_value(TLV_TYPE_EXT_SERVICE_ENUM_INTERACTIVE)
3644
}
37-
}
45+
end
3846

39-
return services.sort_by { |s| s[:name].upcase }
47+
services.sort_by { |s| s[:name].upcase }
4048
end
4149

50+
#
4251
# Query some detailed parameters about a particular service.
52+
#
4353
def query(service_name)
4454
request = Packet.create_request('extapi_service_query')
4555
request.add_tlv(TLV_TYPE_EXT_SERVICE_ENUM_NAME, service_name)
4656

4757
response = client.send_request(request)
4858

49-
detail = {
59+
{
5060
:starttype => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_STARTTYPE),
5161
:display => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_DISPLAYNAME),
5262
:startname => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_STARTNAME),
5363
:path => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_PATH),
5464
:logroup => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_LOADORDERGROUP),
5565
:interactive => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_INTERACTIVE),
56-
:dacl => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_DACL)
66+
:dacl => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_DACL),
67+
:status => response.get_tlv_value(TLV_TYPE_EXT_SERVICE_QUERY_STATUS)
5768
}
69+
end
70+
71+
#
72+
# Control a single service
73+
#
74+
def control(service_name, op)
75+
if op.is_a? String
76+
case op.strip.downcase
77+
when "start"
78+
op = SERVICE_OP_START
79+
when "pause"
80+
op = SERVICE_OP_PAUSE
81+
when "resume"
82+
op = SERVICE_OP_RESUME
83+
when "stop"
84+
op = SERVICE_OP_STOP
85+
when "restart"
86+
op = SERVICE_OP_RESTART
87+
end
88+
end
89+
90+
unless (op.is_a? Integer) && op >= SERVICE_OP_START && op <= SERVICE_OP_RESTART
91+
raise ArgumentError, "Invalid operation: #{op}"
92+
end
5893

59-
return detail
94+
request = Packet.create_request('extapi_service_control')
95+
request.add_tlv(TLV_TYPE_EXT_SERVICE_CTRL_NAME, service_name)
96+
request.add_tlv(TLV_TYPE_EXT_SERVICE_CTRL_OP, op)
97+
client.send_request(request)
6098
end
6199

62100
attr_accessor :client

lib/rex/post/meterpreter/extensions/extapi/tlv.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ module Extapi
2727
TLV_TYPE_EXT_SERVICE_QUERY_LOADORDERGROUP = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 24)
2828
TLV_TYPE_EXT_SERVICE_QUERY_INTERACTIVE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 25)
2929
TLV_TYPE_EXT_SERVICE_QUERY_DACL = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 26)
30+
TLV_TYPE_EXT_SERVICE_QUERY_STATUS = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 27)
31+
32+
TLV_TYPE_EXT_SERVICE_CTRL_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 28)
33+
TLV_TYPE_EXT_SERVICE_CTRL_OP = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 29)
3034

3135
TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35)
3236

lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/service.rb

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ class Console::CommandDispatcher::Extapi::Service
2222
#
2323
def commands
2424
{
25-
"service_enum" => "Enumerate all registered Windows services",
26-
"service_query" => "Query more detail about a specific Windows service"
25+
"service_enum" => "Enumerate all registered Windows services",
26+
"service_query" => "Query more detail about a specific Windows service",
27+
"service_control" => "Control a single service (start/pause/resume/stop/restart)"
2728
}
2829
end
2930

@@ -33,6 +34,32 @@ def commands
3334
def name
3435
"Extapi: Service Management"
3536
end
37+
38+
#
39+
# Initialize the instance
40+
#
41+
def initialize(shell)
42+
super
43+
44+
@status_map = {
45+
1 => "Stopped",
46+
2 => "Starting",
47+
3 => "Stopping",
48+
4 => "Running",
49+
5 => "Continuing",
50+
6 => "Pausing",
51+
7 => "Paused"
52+
}
53+
54+
@start_type_map = {
55+
0 => "Boot",
56+
1 => "System",
57+
2 => "Automatic",
58+
3 => "Manual",
59+
4 => "Disabled"
60+
}
61+
end
62+
3663
#
3764
# Options for the service_enum command.
3865
#
@@ -44,7 +71,7 @@ def name
4471
# Query a single service for more detail.
4572
#
4673
def cmd_service_enum(*args)
47-
@@service_enum_opts.parse(args) { |opt, idx, val|
74+
@@service_enum_opts.parse(args) do |opt, idx, val|
4875
case opt
4976
when "-h"
5077
print(
@@ -55,17 +82,7 @@ def cmd_service_enum(*args)
5582
"able to interact with the desktop.\n\n")
5683
return true
5784
end
58-
}
59-
60-
status_map = {
61-
1 => "Stopped",
62-
2 => "Starting",
63-
3 => "Stopping",
64-
4 => "Running",
65-
5 => "Continuing",
66-
6 => "Pausing",
67-
7 => "Paused"
68-
}
85+
end
6986

7087
services = client.extapi.service.enumerate
7188

@@ -78,14 +95,14 @@ def cmd_service_enum(*args)
7895
]
7996
)
8097

81-
services.each { |s|
98+
services.each do |s|
8299
table << [
83100
s[:pid],
84-
status_map[s[:status]],
101+
@status_map[s[:status]],
85102
s[:interactive] ? "Y" : "N",
86103
"#{s[:name].downcase} (#{s[:display]})"
87104
]
88-
}
105+
end
89106

90107
print_line
91108
print_line(table.to_s)
@@ -107,9 +124,9 @@ def cmd_service_enum(*args)
107124
# Query a single service for more detail.
108125
#
109126
def cmd_service_query(*args)
110-
args << "-h" if args.length == 0
127+
args.unshift("-h") if args.length != 1
111128

112-
@@service_query_opts.parse(args) { |opt, idx, val|
129+
@@service_query_opts.parse(args) do |opt, idx, val|
113130
case opt
114131
when "-h"
115132
print(
@@ -119,25 +136,18 @@ def cmd_service_query(*args)
119136
"binary path, DACL, load order group, start type and more.\n\n")
120137
return true
121138
end
122-
}
139+
end
123140

124141
service_name = args.shift
125142

126-
start_type_map = {
127-
0 => "Boot",
128-
1 => "System",
129-
2 => "Automatic",
130-
3 => "Manual",
131-
4 => "Disabled"
132-
}
133-
134143
detail = client.extapi.service.query(service_name)
135144

136145
print_line
137146
print_line("Name : #{service_name}")
138147
print_line("Display : #{detail[:display]}")
139148
print_line("Account : #{detail[:startname]}")
140-
print_line("Start Type : #{start_type_map[detail[:starttype]]}")
149+
print_line("Status : #{@status_map[detail[:status]]}")
150+
print_line("Start Type : #{@start_type_map[detail[:starttype]]}")
141151
print_line("Path : #{detail[:path]}")
142152
print_line("L.O. Group : #{detail[:logroup]}")
143153
print_line("Interactive : #{detail[:interactive] ? "Yes" : "No"}")
@@ -146,6 +156,39 @@ def cmd_service_query(*args)
146156

147157
end
148158

159+
#
160+
# Options for the service_control command.
161+
#
162+
@@service_control_opts = Rex::Parser::Arguments.new(
163+
"-h" => [ false, "Help banner" ]
164+
)
165+
166+
#
167+
# Query a single service for more detail.
168+
#
169+
def cmd_service_control(*args)
170+
args.unshift("-h") if args.length != 2
171+
172+
@@service_control_opts.parse(args) do |opt, idx, val|
173+
case opt
174+
when "-h"
175+
print(
176+
"\nUsage: service_control [-h] <servicename> <op>\n" +
177+
" <servicename> : The name of the service to control.\n" +
178+
" <op> : The operation to perform on the service.\n" +
179+
" Valid ops: start pause resume stop restart.\n\n")
180+
return true
181+
end
182+
end
183+
184+
service_name = args[0]
185+
op = args[1]
186+
187+
client.extapi.service.control(service_name, op)
188+
189+
print_good("Operation #{op} succeeded.")
190+
end
191+
149192
end
150193

151194
end

0 commit comments

Comments
 (0)