/* * The IKE Scanner (ike-scan) is Copyright (C) 2003-2007 Roy Hills, * NTA Monitor Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library, and distribute linked combinations including the two. * * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. * * If this license is unacceptable to you, I may be willing to negotiate * alternative licenses (contact ike-scan@nta-monitor.com). * * You are encouraged to send comments, improvements or suggestions to * me at ike-scan@nta-monitor.com. * * $Id: psk-crack.c 9919 2007-01-22 22:52:36Z rsh $ * * psk-crack.c -- IKE Aggressive Mode Pre-Shared Key cracker for ike-scan * * Author: Roy Hills * Date: 8 July 2004 * * Usage: * psk-crack [options] * */ #include "psk-crack.h" #include "hash_functions.h" static const char rcsid[] = "$Id: psk-crack.c 9919 2007-01-22 22:52:36Z rsh $"; /* RCS ID for ident(1) */ static const char *default_charset = "0123456789abcdefghijklmnopqrstuvwxyz"; /* default bruteforce charset */ static psk_entry *psk_list; /* List of PSK parameters */ int main (int argc, char *argv[]) { const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"bruteforce", required_argument, 0, 'B'}, {"charset", required_argument, 0, 'c'}, {"dictionary", required_argument, 0, 'd'}, {"norteluser", required_argument, 0, 'u'}, {0, 0, 0, 0} }; const char *short_options = "hvVB:c:d:u:"; int arg; int options_index=0; int verbose=0; unsigned brute_len=0; /* Bruteforce len. 0=dictionary attack (default) */ const char *charset = NULL; char dict_file_name[MAXLINE]; /* Dictionary file name */ char *nortel_user = NULL; /* For cracking Nortel Contivity passwords only */ FILE *dictionary_file=NULL; /* Dictionary file */ IKE_UINT64 iterations=0; struct timeval start_time; /* Program start time */ struct timeval end_time; /* Program end time */ struct timeval elapsed_time; /* Elapsed time as timeval */ double elapsed_seconds; /* Elapsed time in seconds */ int psk_idx; /* Index into psk list */ unsigned psk_count; /* Number of PSK entries in the list */ unsigned psk_uncracked; /* Number of uncracked PSK entries */ unsigned char *hash_r; char line[MAXLINE]; dict_file_name[0] = '\0'; /* Initialise to empty string */ /* * Process options and arguments. */ while ((arg=getopt_long_only(argc, argv, short_options, long_options, &options_index)) != -1) { switch (arg) { case 'h': /* --help */ psk_crack_usage(EXIT_SUCCESS); break; /* NOTREACHED */ case 'v': /* --verbose */ verbose++; break; case 'V': /* --version */ fprintf(stderr, "psk-crack (%s)\n\n", PACKAGE_STRING); fprintf(stderr, "Copyright (C) 2003-2007 Roy Hills, NTA Monitor Ltd.\n"); fprintf(stderr, "ike-scan comes with NO WARRANTY to the extent permitted by law.\n"); fprintf(stderr, "You may redistribute copies of ike-scan under the terms of the GNU\n"); fprintf(stderr, "General Public License.\n"); fprintf(stderr, "For more information about these matters, see the file named COPYING.\n"); fprintf(stderr, "\n"); /* We use rcsid here to prevent it being optimised away */ fprintf(stderr, "%s\n", rcsid); error_use_rcsid(); utils_use_rcsid(); wrappers_use_rcsid(); exit(EXIT_SUCCESS); break; /* NOTREACHED */ case 'B': /* --bruteforce */ brute_len=Strtoul(optarg, 10); break; case 'c': /* --charset */ charset=make_message("%s", optarg); break; case 'd': /* --dictionary */ strncpy(dict_file_name, optarg, MAXLINE); brute_len = 0; break; case 'u': /* --norteluser */ nortel_user = make_message("%s", optarg); break; default: /* Unknown option */ psk_crack_usage(EXIT_FAILURE); break; /* NOTREACHED */ } } /* End While */ /* * Check that we've got exactly one argument. */ if ((argc - optind) != 1) { psk_crack_usage(EXIT_FAILURE); } /* * Display the starting message. */ printf("Starting psk-crack [%s] (http://www.nta-monitor.com/tools/ike-scan/)\n", PACKAGE_STRING); /* * If the character set has not been specified, use the default one. */ if (!charset) charset = default_charset; /* * Load the PSK entries from the data file. */ psk_count = load_psk_params(argv[optind], nortel_user); if (verbose) printf("Loaded %u PSK entries from %s\n", psk_count, argv[optind]); if (psk_count < 1) err_msg("ERROR: No pre-shared keys to crack"); /* * Open dictionary file if required. */ if (!brute_len) /* If not bruteforcing */ dictionary_file = open_dict_file(dict_file_name); /* * Get program start time for statistics displayed on completion. */ if (brute_len) { printf("Running in brute-force cracking mode\n"); } else { printf("Running in dictionary cracking mode\n"); } Gettimeofday(&start_time); /* * Cracking loop. */ psk_uncracked = psk_count; if (brute_len) { /* Brute force cracking */ IKE_UINT64 max; unsigned base; unsigned i; IKE_UINT64 loop; IKE_UINT64 val; unsigned digit; base = strlen(charset); max = base; for (i=1; i 1) printf("Trying key \"%s\"\n", line); iterations++; for (psk_idx=0; psk_idx 1) printf("Trying key \"%s\"\n", line); iterations++; for (psk_idx=0; psk_idxskeyid_data = skeyid_data; pe->skeyid_data_len = skeyid_data_len; pe->hash_r_data = hash_r_data; pe->hash_r_data_len = hash_r_data_len; pe->hash_r = hex2data(hash_r_hex, &pe->hash_r_len); hash_r_hex_len = strlen(hash_r_hex) + 1; /* includes terminating null */ pe->hash_r_hex = Malloc(hash_r_hex_len); strncpy(pe->hash_r_hex, hash_r_hex, hash_r_hex_len); pe->nortel_user = nortel_user; /* * Determine hash type based on the length of the hash, and * store this in the current psk list entry. */ if (pe->hash_r_len == MD5_HASH_LEN) { pe->hash_type=HASH_TYPE_MD5; pe->hash_name=make_message("MD5"); } else if (pe->hash_r_len == SHA1_HASH_LEN) { pe->hash_type=HASH_TYPE_SHA1; pe->hash_name=make_message("SHA1"); } else { err_msg("Cannot determine hash type from %u byte HASH_R", pe->hash_r_len); } pe->live = 1; } /* End While fgets() */ /* * Close the data file, and return the number of PSK entries * read into the list. */ fclose(data_file); return count; } /* * compute_hash -- Compute the hash given a candidate password * * Inputs: * * psk_params Pointer to PSK params structure * password The candidate password * * Returns: * * Pointer to the computed hash. * * This function calculates a hash given the PSK parameters and * a candidate password. * * The standard process used to calculate the hash is detailed in * RFC 2409. The hash used by Nortel Contivity systems use a different, * proprietary, method. * * In all cases, the calculation of the hash is a two-stage process: * * a) Calculate SKEYID using some of the PSK parameters and the password; * b) Calculate HASH_R using SKEYID and the other PSK parameters. * */ static inline unsigned char * compute_hash (const psk_entry *psk_params, const char *password) { size_t password_len = strlen(password); unsigned char skeyid[SHA1_HASH_LEN]; static unsigned char hash_r[SHA1_HASH_LEN]; /* * Calculate SKEYID */ if (psk_params->nortel_user == NULL) { /* RFC 2409 SKEYID calculation */ if (psk_params->hash_type == HASH_TYPE_MD5) { hmac_md5(psk_params->skeyid_data, psk_params->skeyid_data_len, (const unsigned char *) password, password_len, skeyid); } else { /* SHA1 */ hmac_sha1(psk_params->skeyid_data, psk_params->skeyid_data_len, (const unsigned char *) password, password_len, skeyid); } } else { /* Nortel proprietary SKEYID calculation */ unsigned char nortel_psk[SHA1_HASH_LEN]; unsigned char nortel_pwd_hash[SHA1_HASH_LEN]; SHA1((const unsigned char *) password, password_len, nortel_pwd_hash); hmac_sha1((const unsigned char *)psk_params->nortel_user, strlen(psk_params->nortel_user), nortel_pwd_hash, SHA1_HASH_LEN, nortel_psk); if (psk_params->hash_type == HASH_TYPE_MD5) { hmac_md5(psk_params->skeyid_data, psk_params->skeyid_data_len, nortel_psk, SHA1_HASH_LEN, skeyid); } else { /* SHA1 */ hmac_sha1(psk_params->skeyid_data, psk_params->skeyid_data_len, nortel_psk, SHA1_HASH_LEN, skeyid); } } /* * Calculate HASH_R */ if (psk_params->hash_type == HASH_TYPE_MD5) { hmac_md5(psk_params->hash_r_data, psk_params->hash_r_data_len, skeyid, psk_params->hash_r_len, hash_r); } else { /* SHA1 */ hmac_sha1(psk_params->hash_r_data, psk_params->hash_r_data_len, skeyid, psk_params->hash_r_len, hash_r); } return hash_r; } /* * open_dict_file -- Open the dictionary file * * Inputs: * * dict_file_name The dictionary file name, or NULL for default. * * Returns: * * The file descriptor of the dictionary file. */ static FILE * open_dict_file(const char *dict_file_name) { char *fn; FILE *fp; #ifdef __CYGWIN__ char fnbuf[MAXLINE]; int fnbuf_siz; int i; #endif if (dict_file_name[0] == '\0') { /* Dictionary file not specified */ #ifdef __CYGWIN__ if ((fnbuf_siz=GetModuleFileName(GetModuleHandle(0), fnbuf, MAXLINE)) == 0) { err_msg("ERROR: Call to GetModuleFileName failed"); } for (i=fnbuf_siz-1; i>=0 && fnbuf[i] != '/' && fnbuf[i] != '\\'; i--) ; if (i >= 0) { fnbuf[i] = '\0'; } fn = make_message("%s\\%s", fnbuf, DICT_FILE); #else fn = make_message("%s/%s", IKEDATADIR, DICT_FILE); #endif } else { /* Dictionary filename was specified */ fn = make_message("%s", dict_file_name); } if ((strcmp(fn, "-")) == 0) { /* Filename "-" means stdin */ fp = stdin; } else { if ((fp = fopen(fn, "r")) == NULL) { err_sys("error opening dictionary file %s", fn); } } free(fn); return fp; } /* * psk_crack_usage -- display usage message and exit * * Inputs: * * status Status value to pass to exit() * * Returns: * * None (this function never returns). */ static void psk_crack_usage(int status) { fprintf(stderr, "Usage: psk-crack [options] \n"); fprintf(stderr, "\n"); fprintf(stderr, " is a file containing the parameters for the pre-shared\n"); fprintf(stderr, "key cracking process in the format generated by ike-scan with the --pskcrack\n"); fprintf(stderr, "(-P) option. This file can contain one or more entries. For multiple entries,\n"); fprintf(stderr, "each one must be on a separate line.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Two SKEYID computation methods are supported: the standard method for pre-\n"); fprintf(stderr, "shared keys as described in RFC 2409, and the proprietary method used by\n"); fprintf(stderr, "Nortel Contivity / VPN Router systems. The standard method is used by default,\n"); fprintf(stderr, "and the Nortel method can be selected with the --norteluser option.\n"); fprintf(stderr, "\n"); fprintf(stderr, "The program can crack either MD5 or SHA1-based hashes. The type of hash is\n"); fprintf(stderr, "automatically determined from the length of the hash (16 bytes for MD5 or\n"); fprintf(stderr, "20 bytes for SHA1). Each entry in the is handled\n"); fprintf(stderr, "separately, so it is possible to crack a mixture of MD5 and SHA1 hashes.\n"); fprintf(stderr, "\n"); fprintf(stderr, "By default, psk-crack will perform dictionary cracking using the default\n"); fprintf(stderr, "dictionary. The dictionary can be changed with the --dictionary (-d) option,\n"); fprintf(stderr, "or brute-force cracking can be selected with the --bruteforce (-B) option.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, "\n--help or -h\t\tDisplay this usage message and exit.\n"); fprintf(stderr, "\n--version or -V\t\tDisplay program version and exit.\n"); fprintf(stderr, "\n--verbose or -v\t\tDisplay verbose progress messages.\n"); fprintf(stderr, "\t\t\tUse more than once for increased verbosity.\n"); fprintf(stderr, "\n--dictionary= or -d Set dictionary file to \n"); #ifdef __CYGWIN__ fprintf(stderr, "\t\t\tdefault=%s in psk-crack.exe dir.\n", DICT_FILE); #else fprintf(stderr, "\t\t\tdefault=%s/%s.\n", IKEDATADIR, DICT_FILE); #endif fprintf(stderr, "\t\t\tUse \"-\" for standard input.\n"); fprintf(stderr, "\n--norteluser= or -u Specify username for Nortel Contivity PSK cracking.\n"); fprintf(stderr, "\t\t\tThis option is required when cracking pre-shared keys\n"); fprintf(stderr, "\t\t\ton Nortel Contivity / VPN Router systems. These\n"); fprintf(stderr, "\t\t\tsystems use a proprietary method to calculate the hash\n"); fprintf(stderr, "\t\t\tthat includes a hash of the username.\n"); fprintf(stderr, "\t\t\tThis option is only needed when cracking Nortel format\n"); fprintf(stderr, "\t\t\thashes, and should not be used for standard format\n"); fprintf(stderr, "\t\t\thashes.\n"); fprintf(stderr, "\t\t\tWhen this option is used, all the PSK entries in the\n"); fprintf(stderr, "\t\t\tpsk parameters file are assumed to be in Nortel format\n"); fprintf(stderr, "\t\t\tusing the supplied username. There is currently no way\n"); fprintf(stderr, "\t\t\tto crack a mixture of Nortel and standard format PSK\n"); fprintf(stderr, "\t\t\tentries, or Nortel entries with different usernames in\n"); fprintf(stderr, "\t\t\ta single psk-crack run.\n"); fprintf(stderr, "\n--bruteforce= or -B Select bruteforce cracking up to characters.\n"); fprintf(stderr, "\n--charset= or -c \tSet bruteforce character set to \n"); fprintf(stderr, "\t\t\tDefault is \"%s\"\n", default_charset); fprintf(stderr, "\n"); fprintf(stderr, "Report bugs or send suggestions to %s\n", PACKAGE_BUGREPORT); fprintf(stderr, "See the ike-scan homepage at http://www.nta-monitor.com/tools/ike-scan/\n"); exit(status); }