Thursday, July 14, 2011

Identifying and Querying HID VertX Controllers On The Network - VertX_Query.py

At the most basic level, HID VertX Access Controllers work in proximity card access systems and take the data provided by the reader (usually presented via the Weigand Protocol) and format it so a backend system can process the data. The backend system will look up the card ID provided and see if it has access to the particular door its requesting access from. The backend system makes that decision then sends it back over to the controller which then opens the door, or keeps it locked.

You can guess why these systems could be important to protect on a network. Here are a couple ways to identify them and a tool i wrote to help pull some data from them.

1. Standard Port Scan for TCP 4050: The first way is to do a quick sweep for any hosts with a service on TCP 4050.


root@bt:/VertX# nmap -sS -p 4050 --open 192.168.1.0/24

Starting Nmap 5.51 ( http://nmap.org ) at 2011-07-14 14:54 EDT
Nmap scan report for 192.168.1.50
Host is up (0.00090s latency).
PORT STATE SERVICE
4050/tcp open unknown
MAC Address: 00:06:8E:FF:FF:FF (HID)

Nmap done: 256 IP addresses (22 hosts up) scanned in 2.97 seconds


2. Check OUIs: If you're on the same network, or can otherwise look at the system's MAC address, HID has its own OUI(s) allocated to them.

3. Telnet Banner: Obvious, but not always enabled.

root@bt:/# telnet 192.168.1.50
Trying 192.168.1.50...
Connected to 192.168.1.50.
Escape character is '^]'.

Axis Developer Board LX release 2.2.0
Linux 2.4.26 on a cris (0)

VertXController login:


4. VertX Tool Box Discovery: HID has a Windows Client called the Discovery Client.

5. VertX_Query.py: This is a simple tool I wrote to identify VertX Controllers on a local network, and perform some other basic functions.

If you're within the same broadcast domain you can send out a broadcast, otherwise just define a suspected IP and it'll pull any information it can:

root@bt:/VertX# ./VertX_Query.py -h 255.255.255.255 -m 01
VertX_Query.py - HID VertX Discovery and Query Tool
by brad antoniewicz
--------------------------------------

[+] Got Response
Type: VertXController - V2000
Version: 2.2.7.18
IP Address: 192.168.1.50
MAC Address: 00:06:8E:FF:FF:FF



The tool also can make the Comm LED blink on/off by defining a 02 and 03 message type.

Here's the help:


root@bt:/VertX# ./VertX_Query.py
VertX_Query.py - HID VertX Discovery and Query Tool
by brad antoniewicz
--------------------------------------

Options:
-h host
-p port (default 4070)
-v verbose
-m Message Type code

Supported Message Types:
01 Discover
02 command_blink_on
03 command_blink_off


Here's the code:

root@bt:/VertX# cat VertX_Query.py
#!/usr/bin/env python
#
# VertX_Query.py - HID VertX Discovery and Query Tool
# by brad antoniewicz
#


import struct
import socket
import binascii
import getopt
import sys
import random
from socket import *

def usage():
        help = "Options:\n"
        help += "\t-h <host>\t host\n"
        help += "\t-p <port>\t port (default 4070)\n"
        help += "\t-v \t\t verbose\n"
        help += "\t-m <code> \t Message Type code\n"
        help += "\nSupported Message Types:\n"
        help += "\t01 \t Discover\n"
        help += "\t02 \t command_blink_on\n"
        help += "\t03 \t command_blink_off\n"
        return help

def process_resp(recv_data,msg_type):
        # ('discovered;090;MAC_ADDR;VertXController;IP_ADDR;2;V2000;2.2.7.18;02/27/2007;', ('10.112.18.50', 4070))
        mac=0
        if msg_type == "01":
                data = recv_data[0].split(';')
                print "\tType:\t\t",data[3],"-",data[6]
                print "\tVersion:\t",data[7]
                print "\tIP Address:\t",data[4]
                print "\tMAC Address:\t",data[2]
                mac = data[2]
        return mac

def get_mac(host,port):
        msg_to_send = buildmsg("01",0,host,port)
        if msg_to_send:
                mac = sendmsg(host,port,binascii.unhexlify(msg_to_send),0,0,"01")
        return mac

def buildmsg(msg_type,verbose,host,port):
        # Basic Connect
        if msg_type == "01" :
                # discover;013;
                msg = "646973636f7665723b3031333b"
        elif msg_type == "02":
                # command_blink_on;042;MAC_ADDR;30;
                print "[+] Querying Host first to pull MAC address"
                msg = "command_blink_on;042;"
                msg += get_mac(host,port)
                msg += ";30;"
                msg = binascii.hexlify(msg)
                print "[+] Sending command_blink_on"

        elif msg_type == "03":
                # command_blink_off;042;MAC_ADDR;1;
                print "[+] Querying host first to pull MAC address"
                msg = "command_blink_off;042;"
                msg += get_mac(host,port)
                msg += ";1;"
                msg = binascii.hexlify(msg)
                print "[+] Sending command_blink_off"
        else :
                print "[!] Message Type", msg_type, "unsupported!"
                print "[!] Unsupported message types can only be used with -f\n"
                print "[!] Quiting..."
                return 0

        ## Get the length of our msg
        total_length = len(binascii.unhexlify(msg))

        datahex=msg

        if verbose:
                print '[Verbose] Message before Send:\nH(', total_length, '):', datahex, '\t | \tA:', binascii.unhexlify(datahex)

        return datahex


def sendmsg(host,port,hex_msg,verbose,count,msg_type):

        udp=0

        if ((msg_type == "01") or (msg_type == "02") or (msg_type == "03")):
                if verbose:
                        print '[Verbose] Message type indicates UDP'
                udp=1

        if udp:
                s = socket(AF_INET,SOCK_DGRAM)
                if ( host == "255.255.255.255"):
                        if verbose:
                                print '[Verbose] Destination is Broadcast'
                        s.bind(('',0))
                        s.setsockopt(SOL_SOCKET, SO_BROADCAST,1)
        else:
                s = socket.socket()

        s.settimeout(2)

        recv_data = 0

        if verbose:
                print '[+] Target',host,':',port
                print "[+] Sending msg with length",len(hex_msg)

        if udp:
                s.sendto(hex_msg,(host,port))
                error=0
        else:
                s.connect((host,port))
                error = s.sendall(hex_msg)

        if error:
                print "[!] Error:",error
        else:
                try:
                        if udp:
                                 recv_data = s.recvfrom(1024);
                        else:
                                recv_data = s.recv(1024);

                except:
                        if count:
                                print "[",count,":ALERT] Client timed out or didnt respond!"
                        else:
                                print "[ALERT] Client timed out or didnt respond!"

                if recv_data:
                        if verbose:
                                print '[+] Client Resp:'
                                print '\t ascii:\t',recv_data
                                if not udp:
                                        print '\t hex:\t',binascii.hexlify(recv_data)
                        else:
                                print "[+] Got Response"
                        mac = process_resp(recv_data,msg_type)
                else:
                        if count:
                                print "[",count,"] No recv_data"
                        else:
                                print "[+] No recv_data"

        if not udp:
                s.shutdown(2)

        s.close()
        if mac:
                return mac


def main():
        print "VertX_Query.py - HID VertX Discovery and Query Tool"
        print "by brad antoniewicz"
        print "--------------------------------------\n"

        try:
                opts, args = getopt.getopt(sys.argv[1:], "h:p:m:vfs:at:",[])

        except getopt.GetoptError:
                print usage()
                return
        port = 4070
        host = cmdcode = verbose = fuzz = hex_str = dumbfuzz = ftype = vals = 0

        for o, a in opts:
                if o == "-h":
                        host = a
                if o == "-p":
                        port = int(a)
                if o == "-m":
                        cmdcode = a
                if o == "-v":
                        verbose = 1
                if o == "-f":
                        fuzz = 1
                if o == "-s":
                        hex_str = a
                if o == "-a":
                        vals = 1
                if o == "-t":
                        ftype = a
        if (host == 0) or (cmdcode == 0):
                print usage()
                return

        if verbose and cmdcode:
                print "[Verbose] Command code: ",cmdcode

        if fuzz:
                fuzz_msg(host,port,cmdcode,hex_str,verbose,ftype,vals)
        elif dumbfuzz:
                dumbfuzzer(verbose)
        else:
                msg_to_send = buildmsg(cmdcode,verbose,host,port)
                if msg_to_send:
                        sendmsg(host,port,binascii.unhexlify(msg_to_send),verbose,0,cmdcode)
main()

2 comments:

  1. How create our own .dll for vertx V1000.

    controller Accept msg format and accepted output with respect to input

    ReplyDelete