metasploit-framework/modules/auxiliary/scanner/h323/h323_version.rb

567 lines
15 KiB
Ruby

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'H.323 Version Scanner',
'Description' => 'Detect H.323 Version.',
'Author' => 'hdm',
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(1720),
], self.class)
end
def run_host(ip)
remote_display = nil
remote_product_id = nil
remote_version_id = nil
remote_vendor_id = nil
remote_protocol = nil
begin
# Wrap this in a timeout to prevent dead services from
# hanging this thread.
Timeout.timeout( call_timeout) do
connect
caller_name = "SYSTEM\x00"
h323_id = Rex::Text.rand_text_alpha(3)
vendor_id = Rex::Text.rand_text_alpha(32)
caller_host = Rex::Socket.source_address( ip )
caller_port = rand( 32768 ) + 30000
callee_host = rhost
callee_port = rport
conf_guid = Rex::Text.rand_text(16)
call_guid = Rex::Text.rand_text(16)
pkt_setup = h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
res = sock.put(pkt_setup) rescue nil
if not res
disconnect
return
end
cnt = 0
while( true )
info = read_packet
break if not info
# The remote side of the call disconnected us
break if info[:type] == @@H323_STATUS_RELEASE_COMPLETE
remote_display = info[40].strip if info[40]
remote_product_id = info[:product_id].strip if info[:product_id]
remote_version_id = info[:version_id].strip if info[:version_id]
remote_protocol = info[:protocol_version].strip if info[:protocol_version]
if info[:vendor_id] and [nil, "Unknown"].include?( remote_vendor_id )
remote_vendor_id = info[:vendor_id].strip
end
# Diagnostics
# print_status("Host: #{rhost}:#{rport} => #{info.inspect}")
# The remote side of the call was connected (kill it)
break if info[:type] == @@H323_STATUS_CONNECT
# Exit if we already received 5 packets from the server
break if (cnt +=1) > 5
end
# Make sure the call was shut down cleanly
pkt_release = h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
sock.put(pkt_release) rescue nil
# End timeout block
end
rescue ::Timeout::Error
rescue ::Interrupt
raise $!
rescue ::Rex::ConnectionError, ::IOError, ::Errno::ECONNRESET, ::Errno::ENOPROTOOPT
rescue ::Exception
print_error("#{rhost}:#{rport} #{$!.class} #{$!} #{$!.backtrace}")
ensure
disconnect
end
if remote_vendor_id
remote_product_id = remote_product_id.to_s.gsub(/[^\x20-\x7e]/, '')
remote_version_id = remote_version_id.to_s.gsub(/[^\x20-\x7e]/, '')
banner = "Protocol: #{ remote_protocol } VendorID: #{ remote_vendor_id } "
if remote_version_id and remote_version_id.length > 0
banner << "VersionID: #{ remote_version_id } "
end
if remote_product_id and remote_product_id.length > 0
banner << "ProductID: #{ remote_product_id } "
end
if remote_display and remote_display.length > 0
remote_display = remote_display.to_s.gsub(/[^\x20-\x7e]/, '')
banner << "DisplayName: #{ remote_display }"
end
print_status("#{rhost}:#{rport} #{banner}")
report_service(:host => rhost, :port => rport, :name => "h323", :info => banner)
end
end
def read_packet
begin
::Timeout.timeout( read_timeout ) do
ver = sock.read(2)
return if not (ver and ver == "\x03\x00")
bin = sock.read(2)
return if not bin
len = [ bin.unpack("n")[0] - 4, 0 ].max
return if len == 0
bin = sock.read(len)
return if not bin
f_desc, cref_len = bin.unpack("CC")
cref_val = bin[2, cref_len]
f_type = bin[2 + cref_len, 1].unpack("C")[0]
return { :type => f_type, :call_ref => cref_val }.merge( read_ies(f_type, bin[ 2 + cref_len + 1, bin.length] ) )
end
rescue ::Timeout::Error
end
nil
end
def read_ies(mtype, data)
r = { }
i = 0
while( i < (data.length - 1) )
ie_type = data[i, 1].unpack("C")[0]
break if not ie_type
ie_len = 0
ie_data = ""
case ie_type
when @@H225_IE_USER_USER
ie_len = data[i+1, 2].unpack("n")[0]
break if not ie_len
ie_data = data[i+3, ie_len]
break if not ie_data
i = i + 3 + ie_len
else
ie_len = data[i+1, 1].unpack("C")[0]
break if not ie_len
ie_data = data[i+2, ie_len]
break if not ie_data
i = i + 2 + ie_len
end
r[ ie_type ] = ie_data
if ie_type == @@H225_IE_USER_USER
r.merge!( ( read_user_user(mtype, ie_data) rescue {} ) )
end
end
r
end
# This provides a weak method of decoding USER-USER PDUs. These are
# actually PER-encoded ASN.1, but we take a few shortcuts since PER
# encoding is such a pain.
def read_user_user(mtype, data)
r = {}
# Identify the embedded version (2/3/4/5/6 commonly found)
i = data.index("\x00\x08\x91\x4a\x00")
return r if not i
# Store the protocol version
pver = data[i + 5, 1].unpack("C")[0]
r[:protocol_version] = pver.to_s
# Bump the index over the version
i+= 6
# print_line( Rex::Text.to_hex_dump( data[i, 32] ) )
# Set a placeholder VendorID so this system will be reported
r[:vendor_id] = "Unknown"
# We use the version offset to identify the destination block location
# This changes slightly based on the type of packet we receive
case mtype
when @@H323_STATUS_ALERTING, @@H323_STATUS_PROCEEDING
if pver == 2 and data[i, 2] == "\x20\x00"
r[ :vendor_id ] = "0x%.8x" % ( data[i + 2, 4].unpack("N")[0] rescue 0 )
return r
end
# Find the offset to the VendorID
if data[i + 1, 1] != "\xc0"
i+= 7
end
# Stop processing if we can't identify a VendorID
return r if data[i + 1, 1] != "\xc0"
# Otherwise just add 2 to the offset of the version
i += 2
when @@H323_STATUS_CONNECT
# Bail early in some corner cases
return r if data[i, 1] == "\x00"
# Find the offset to the VendorID
if data[i + 1, 1] != "\xc0"
i+= 7
end
# Stop processing if we can't identify a VendorID
return r if data[i + 1, 1] != "\xc0"
i += 2
return r
else
return r
end
# Extract the manufacturer ID
r[ :vendor_id ] = "0x%.8x" % ( data[i, 4].unpack("N")[0] rescue 0 )
i+= 4
# No Product ID / Version ID in versions less than 3 (unless special cased above)
return r if pver < 3
# Get the product_id length (-1)
product_id_length = data[i, 1].unpack("C")[0] + 1
i+= 1
# Extract the product ID
r[ :product_id ] = data[i, product_id_length]
i+= product_id_length
# Get the version ID length (-1)
version_id_length = data[i, 1].unpack("C")[0] + 1
i+= 1
# Extract the version ID
r[ :version_id ] = data[i, version_id_length]
# Thats it for now
r
end
def read_timeout
10
end
def call_timeout
30
end
@@H225_IE_BEARER_CAP = 0x04
@@H225_IE_DISPLAY = 0x28
@@H225_IE_USER_USER = 0x7e # Yes, really User-user
@@H323_STATUS_ALERTING = 0x01
@@H323_STATUS_PROCEEDING = 0x02
@@H323_STATUS_SETUP = 0x05
@@H323_STATUS_SETUP_ACK = 0x0D
@@H323_STATUS_CONNECT = 0x07
@@H323_STATUS_RELEASE_COMPLETE = 0x5a
@@H323_STATUS_FACILITY = 0x62
def encap_tpkt(ver,data)
[ ver, 0, data.length + 4 ].pack("CCn") + data
end
def encap_q225(desc, cref_value, msg_type, data)
[ desc, cref_value.length, cref_value, msg_type].pack("CCA*C") + data
end
def encap_q225_standard(msg_type, data)
encap_q225(0x08, [0x733f].pack("n"), msg_type, data)
end
def encap_q225_setup(data)
encap_q225_standard(0x05, data)
end
def encap_q225_release(data)
encap_q225_standard(0x5a, data)
end
def create_ie_byte(ie_type, data)
[ie_type, data.length].pack("CC") + data
end
def create_ie_short(ie_type, data)
[ie_type, data.length].pack("Cn") + data
end
def create_ie_bearer_capability(cap = 0x00038893)
create_ie_byte( @@H225_IE_BEARER_CAP, [cap].pack("N")[0,3] )
end
def create_ie_display(name = "DEBUG\x00")
create_ie_byte( @@H225_IE_DISPLAY, name )
end
def create_ie_user_user(data)
create_ie_short( @@H225_IE_USER_USER, data )
end
#
# This is ugly. Doing it properly requires a PER capable ASN.1 encoder, which is overkill for this task
#
def create_user_info(h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
buff = "\x05" # Protocol descriminator: X.208/X.209 coded user information
buff << "\x20\xa8\x06\x00\x08\x91\x4a\x00\x06\x01\x40\x02"
# H323-ID
buff << h323_id.unpack("C*").pack("n*")
buff << "\x22\xc0\x09\x00\x00\x3d\x02\x00\x00\x00\x21"
# VENDOR: 32 + 2 null bytes
buff << [vendor_id].pack("Z32") + "\x00\x00"
buff << "\x00"
# Remote IP + Remote Port
buff << ( ::Rex::Socket.addr_aton( callee_host ) + [ callee_port.to_i ].pack("n") )
buff << "\x00"
# Conference GUID
buff << conf_guid
buff << "\x00\xc5\x1d\x80\x04\x07\x00"
# Local IP + Port
buff << ( ::Rex::Socket.addr_aton( caller_host ) + [ caller_port.to_i ].pack("n") )
buff << "\x11\x00"
# Call GUID
buff << call_guid
buff <<
"\x82\x49\x10\x47\x40\x00\x00\x06\x04\x01\x00\x4c\x10\xb5" +
"\x00\x00\x26\x25\x73\x70\x65\x65\x78\x20\x73\x72\x3d\x31" +
"\x36\x30\x30\x30\x3b\x6d\x6f\x64\x65\x3d\x36\x3b\x76\x62" +
"\x72\x3d\x6f\x66\x66\x3b\x63\x6e\x67\x3d\x6f\x66\x66\x80" +
"\x12\x1c\x40\x01\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc6\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x90\x3c\x00\x00\x64\x0c\x10\xb5\x00\x00\x26\x25" +
"\x73\x70\x65\x65\x78\x20\x73\x72\x3d\x31\x36\x30\x30\x30" +
"\x3b\x6d\x6f\x64\x65\x3d\x36\x3b\x76\x62\x72\x3d\x6f\x66" +
"\x66\x3b\x63\x6e\x67\x3d\x6f\x66\x66\x80\x0b\x0d\x40\x01" +
"\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x48\x31\x40\x00\x00\x06\x04\x01\x00\x4c\x10\x09" +
"\x00\x00\x3d\x0f\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20" +
"\x57\x69\x64\x65\x36\x80\x12\x1c\x40\x01\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc6\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\xa0\x26\x00\x00\x65\x0c\x10\x09\x00\x00\x3d\x0f" +
"\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x57\x69\x64\x65" +
"\x36\x80\x0b\x0d\x40\x01\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x50\x1d\x40\x00\x00\x06\x04\x01\x00\x4c\x60\x13" +
"\x80\x11\x1c\x00\x01\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc6\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x13\x00\x00\x66\x0c\x60\x13\x80\x0b\x0d\x00\x01" +
"\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x00\x1d\x40\x00\x00\x06\x04\x01\x00\x4c\x20\x13" +
"\x80\x11\x1c\x00\x01\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc6\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x13\x00\x00\x67\x0c\x20\x13\x80\x0b\x0d\x00\x01" +
"\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc7\x00\x23\x40\x00\x00\x06\x04\x01\x00\x48\x78\x00" +
"\x4a\xff\x00\x80\x01\x00\x80\x11\x1c\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc8\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x19\x00\x00\x68\x08\x78\x00\x4a\xff\x00\x80\x01" +
"\x00\x80\x0b\x0d\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x00\x22\x40\x00\x00\x06\x04\x01\x00\x48\x68\x4a" +
"\xff\x00\x80\x01\x00\x80\x11\x1c\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc8\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x18\x00\x00\x69\x08\x68\x4a\xff\x00\x80\x01\x00" +
"\x80\x0b\x0d\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x00\x22\x40\x00\x00\x06\x04\x01\x00\x48\x70\x4a" +
"\xff\x00\x80\x01\x00\x80\x11\x1c\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc8\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x18\x00\x00\x6a\x08\x70\x4a\xff\x00\x80\x01\x00" +
"\x80\x0b\x0d\x00\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x00\x2c\x40\x00\x00\x06\x04\x01\x00\x48\xee\x00" +
"\x00\x20\x9f\xff\x20\x50\x40\x01\x00\x80\x17\x1c\x20\x02" +
"\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc8\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x80\x04\x48\x08\x8d\x44\x22\x00\x00\x6b\x08\xee" +
"\x00\x00\x20\x9f\xff\x20\x50\x40\x01\x00\x80\x11\x0d\x20" +
"\x02\x00" +
Rex::Socket.addr_aton( caller_host ) +
"\x13\xc9\x40\x00\x04\x48\x08\x8d\x44\x01\x00\x01\x00\x01" +
"\x00\x01\x00\x80\xfa\x02\x80\xef\x02\x70\x01\x06\x00\x08" +
"\x81\x75\x00\x0d\x80\x1a\x80\x01\xf4\x00\x01\x00\x00\x01" +
"\x00\x00\x01\x00\x04\x02\x05\x00\x48\x08\x8d\x44\x06\x60" +
"\x01\x00\x01\x80\x0b\x80\x00\x00\x20\x20\xb5\x00\x00\x26" +
"\x25\x73\x70\x65\x65\x78\x20\x73\x72\x3d\x31\x36\x30\x30" +
"\x30\x3b\x6d\x6f\x64\x65\x3d\x36\x3b\x76\x62\x72\x3d\x6f" +
"\x66\x66\x3b\x63\x6e\x67\x3d\x6f\x66\x66\x80\x00\x01\x20" +
"\x20\x09\x00\x00\x3d\x0f\x53\x70\x65\x65\x78\x20\x62\x73" +
"\x34\x20\x57\x69\x64\x65\x36\x80\x00\x02\x20\xc0\xef\x80" +
"\x00\x03\x20\x40\xef\x80\x00\x04\x08\xf0\x00\x4a\xff\x00" +
"\x80\x01\x00\x80\x00\x05\x08\xd0\x4a\xff\x00\x80\x01\x00" +
"\x80\x00\x06\x08\xe0\x4a\xff\x00\x80\x01\x00\x80\x00\x07" +
"\x09\xdc\x00\x00\x40\x9f\xff\x20\x50\x40\x01\x00\x80\x00" +
"\x08\x83\x01\x50\x80\x00\x09\x83\x01\x10\x80\x00\x0a\x83" +
"\x01\x40\x80\x00\x0b\x8a\x0c\x14\x0a\x30\x2d\x31\x36\x2c" +
"\x33\x32\x2c\x33\x36\x00\x80\x01\x03\x03\x00\x00\x00\x01" +
"\x00\x02\x00\x03\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00" +
"\x00\x08\x02\x00\x09\x00\x0a\x00\x0b\x07\x01\x00\x32\x80" +
"\x96\x61\x41\x02\x80\x01\x80"
buff
end
def create_user_release_info(call_guid)
"\x05" +
"\x25\x80\x06\x00\x08\x91\x4a\x00\x05\x01\x11\x00" +
call_guid +
"\x02\x80\x01\x00"
end
def h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
encap_tpkt(3,
encap_q225_release(
create_ie_display(caller_name) +
create_ie_user_user(
create_user_release_info(call_guid )
)
)
)
end
def h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
encap_tpkt(3,
encap_q225_setup(
create_ie_bearer_capability() +
create_ie_display(caller_name) +
create_ie_user_user(
create_user_info( h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid )
)
)
)
end
end