nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * coWPAtty - Brute-force dictionary attack against WPA-PSK. |
||
3 | * |
||
4 | * Copyright (c) 2004-2009, Joshua Wright <jwright@hasborg.com> |
||
5 | * |
||
6 | * $Id: cowpatty.c 264 2009-07-03 15:15:50Z jwright $ |
||
7 | * |
||
8 | * This program is free software; you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License version 2 as |
||
10 | * published by the Free Software Foundation. See COPYING for more |
||
11 | * details. |
||
12 | * |
||
13 | * coWPAtty is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | */ |
||
18 | |||
19 | /* |
||
20 | * Significant code is graciously taken from the following: |
||
21 | * wpa_supplicant by Jouni Malinen. This tool would have been MUCH more |
||
22 | * difficult for me if not for this code. Thanks Jouni. |
||
23 | */ |
||
24 | |||
25 | /* |
||
26 | * Right off the bat, this code isn't very useful. The PBKDF2 function makes |
||
27 | * 4096 SHA-1 passes for each passphrase, which takes quite a bit of time. On |
||
28 | * my Pentium II development system, I'm getting ~2.5 passphrases/second. |
||
29 | * I've done my best to optimize the PBKDF2 function, but it's still pretty |
||
30 | * slow. |
||
31 | */ |
||
32 | |||
33 | #define PROGNAME "cowpatty" |
||
34 | #define VER "4.6" |
||
35 | #define MAXPASSPHRASE 256 |
||
36 | #define DOT1X_LLCTYPE "\x88\x8e" |
||
37 | |||
38 | #include <stdlib.h> |
||
39 | #include <stdio.h> |
||
40 | #include <string.h> |
||
41 | #include <sys/stat.h> |
||
42 | #include <unistd.h> |
||
43 | #include <pcap.h> |
||
44 | #include <signal.h> |
||
45 | #include <sys/types.h> |
||
46 | #include <fcntl.h> |
||
47 | |||
48 | #include "cowpatty.h" |
||
49 | #include "common.h" |
||
50 | #include "utils.h" |
||
51 | #include "sha1.h" |
||
52 | #include "md5.h" |
||
53 | #include "radiotap.h" |
||
54 | |||
55 | /* Globals */ |
||
56 | pcap_t *p = NULL; |
||
57 | unsigned char *packet; |
||
58 | struct pcap_pkthdr *h; |
||
59 | char errbuf[PCAP_ERRBUF_SIZE]; |
||
60 | int sig = 0; /* Used for handling signals */ |
||
61 | char *words; |
||
62 | /* temporary storage for the password from the hash record, instead of |
||
63 | malloc/free for each entry. */ |
||
64 | char password_buf[65]; |
||
65 | unsigned long wordstested = 0; |
||
66 | |||
67 | /* Prototypes */ |
||
68 | void wpa_pmk_to_ptk(u8 * pmk, u8 * addr1, u8 * addr2, |
||
69 | u8 * nonce1, u8 * nonce2, u8 * ptk, size_t ptk_len); |
||
70 | void hexdump(unsigned char *data, int len); |
||
71 | void usage(char *message); |
||
72 | void testopts(struct user_opt *opt); |
||
73 | void cleanup(); |
||
74 | void parseopts(struct user_opt *opt, int argc, char **argv); |
||
75 | void closepcap(struct capture_data *capdata); |
||
76 | void handle_dot1x(struct crack_data *cdata, struct capture_data *capdata, |
||
77 | struct user_opt *opt); |
||
78 | void dump_all_fields(struct crack_data cdata, struct user_opt *opt); |
||
79 | void printstats(struct timeval start, struct timeval end, |
||
80 | unsigned long int wordcount); |
||
81 | int nextdictword(char *word, FILE * fp); |
||
82 | int nexthashrec(FILE * fp, struct hashdb_rec *rec); |
||
83 | |||
84 | void usage(char *message) |
||
85 | { |
||
86 | |||
87 | if (strlen(message) > 0) { |
||
88 | printf("%s: %s\n", PROGNAME, message); |
||
89 | } |
||
90 | |||
91 | printf("\nUsage: %s [options]\n", PROGNAME); |
||
92 | printf("\n" |
||
93 | "\t-f \tDictionary file\n" |
||
94 | "\t-d \tHash file (genpmk)\n" |
||
95 | "\t-r \tPacket capture file\n" |
||
96 | "\t-s \tNetwork SSID (enclose in quotes if SSID includes spaces)\n" |
||
97 | "\t-2 \tUse frames 1 and 2 or 2 and 3 for key attack (nonstrict mode)\n" |
||
98 | "\t-c \tCheck for valid 4-way frames, does not crack\n" |
||
99 | "\t-h \tPrint this help information and exit\n" |
||
100 | "\t-v \tPrint verbose information (more -v for more verbosity)\n" |
||
101 | "\t-V \tPrint program version and exit\n" "\n"); |
||
102 | } |
||
103 | |||
104 | void cleanup() |
||
105 | { |
||
106 | /* lame-o-meter++ */ |
||
107 | sig = 1; |
||
108 | } |
||
109 | |||
110 | void wpa_pmk_to_ptk(u8 * pmk, u8 * addr1, u8 * addr2, |
||
111 | u8 * nonce1, u8 * nonce2, u8 * ptk, size_t ptk_len) |
||
112 | { |
||
113 | u8 data[2 * ETH_ALEN + 2 * 32]; |
||
114 | |||
115 | memset(&data, 0, sizeof(data)); |
||
116 | |||
117 | /* PTK = PRF-X(PMK, "Pairwise key expansion", |
||
118 | * Min(AA, SA) || Max(AA, SA) || |
||
119 | * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ |
||
120 | |||
121 | if (memcmp(addr1, addr2, ETH_ALEN) < 0) { |
||
122 | memcpy(data, addr1, ETH_ALEN); |
||
123 | memcpy(data + ETH_ALEN, addr2, ETH_ALEN); |
||
124 | } else { |
||
125 | memcpy(data, addr2, ETH_ALEN); |
||
126 | memcpy(data + ETH_ALEN, addr1, ETH_ALEN); |
||
127 | } |
||
128 | |||
129 | if (memcmp(nonce1, nonce2, 32) < 0) { |
||
130 | memcpy(data + 2 * ETH_ALEN, nonce1, 32); |
||
131 | memcpy(data + 2 * ETH_ALEN + 32, nonce2, 32); |
||
132 | } else { |
||
133 | memcpy(data + 2 * ETH_ALEN, nonce2, 32); |
||
134 | memcpy(data + 2 * ETH_ALEN + 32, nonce1, 32); |
||
135 | } |
||
136 | |||
137 | sha1_prf(pmk, 32, "Pairwise key expansion", data, sizeof(data), |
||
138 | ptk, ptk_len); |
||
139 | } |
||
140 | |||
141 | void hexdump(unsigned char *data, int len) |
||
142 | { |
||
143 | int i; |
||
144 | for (i = 0; i < len; i++) { |
||
145 | printf("%02x ", data[i]); |
||
146 | } |
||
147 | } |
||
148 | |||
149 | void parseopts(struct user_opt *opt, int argc, char **argv) |
||
150 | { |
||
151 | |||
152 | int c; |
||
153 | |||
154 | while ((c = getopt(argc, argv, "f:r:s:d:c2nhvV")) != EOF) { |
||
155 | switch (c) { |
||
156 | case 'f': |
||
157 | strncpy(opt->dictfile, optarg, sizeof(opt->dictfile)); |
||
158 | break; |
||
159 | case 'r': |
||
160 | strncpy(opt->pcapfile, optarg, sizeof(opt->pcapfile)); |
||
161 | break; |
||
162 | case 's': |
||
163 | strncpy(opt->ssid, optarg, sizeof(opt->ssid)); |
||
164 | break; |
||
165 | case 'd': |
||
166 | strncpy(opt->hashfile, optarg, sizeof(opt->hashfile)); |
||
167 | break; |
||
168 | case 'n': |
||
169 | case '2': |
||
170 | opt->nonstrict++; |
||
171 | break; |
||
172 | case 'c': |
||
173 | opt->checkonly++; |
||
174 | break; |
||
175 | case 'h': |
||
176 | usage(""); |
||
177 | exit(0); |
||
178 | break; |
||
179 | case 'v': |
||
180 | opt->verbose++; |
||
181 | break; |
||
182 | case 'V': |
||
183 | printf |
||
184 | ("$Id: cowpatty.c 264 2009-07-03 15:15:50Z jwright $\n"); |
||
185 | exit(0); |
||
186 | break; |
||
187 | default: |
||
188 | usage(""); |
||
189 | exit(-1); |
||
190 | } |
||
191 | } |
||
192 | } |
||
193 | |||
194 | void testopts(struct user_opt *opt) |
||
195 | { |
||
196 | struct stat teststat; |
||
197 | |||
198 | /* Test for a pcap file */ |
||
199 | if (IsBlank(opt->pcapfile)) { |
||
200 | usage("Must supply a pcap file with -r"); |
||
201 | exit(-1); |
||
202 | } |
||
203 | |||
204 | if (opt->checkonly == 1) { |
||
205 | /* Special case where we only need pcap file */ |
||
206 | return; |
||
207 | } |
||
208 | |||
209 | /* test for required parameters */ |
||
210 | if (IsBlank(opt->dictfile) && IsBlank(opt->hashfile)) { |
||
211 | usage("Must supply a list of passphrases in a file with -f " |
||
212 | "or a hash file\n\t with -d. " |
||
213 | "Use \"-f -\" to accept words on stdin."); |
||
214 | exit(-1); |
||
215 | } |
||
216 | |||
217 | if (IsBlank(opt->ssid)) { |
||
218 | usage("Must supply the SSID for the network with -s"); |
||
219 | exit(-1); |
||
220 | } |
||
221 | |||
222 | /* Test that the files specified exist and are greater than 0 bytes */ |
||
223 | if (!IsBlank(opt->hashfile) && strncmp(opt->hashfile, "-", 1) != 0) { |
||
224 | if (stat(opt->hashfile, &teststat)) { |
||
225 | usage("Could not stat hashfile. Check file path."); |
||
226 | exit(-1); |
||
227 | } else if (teststat.st_size == 0) { |
||
228 | usage("Empty hashfile (0 bytes). Check file contents."); |
||
229 | exit(-1); |
||
230 | } |
||
231 | } |
||
232 | |||
233 | if (!IsBlank(opt->dictfile) && strncmp(opt->dictfile, "-", 1) != 0) { |
||
234 | if (stat(opt->dictfile, &teststat)) { |
||
235 | usage |
||
236 | ("Could not stat the dictionary file. Check file path."); |
||
237 | exit(-1); |
||
238 | } else if (teststat.st_size == 0) { |
||
239 | usage |
||
240 | ("Empty dictionary file (0 bytes). Check file contents."); |
||
241 | exit(-1); |
||
242 | } |
||
243 | } |
||
244 | |||
245 | if (stat(opt->pcapfile, &teststat) && strncmp(opt->hashfile, "-", 1) != 0) { |
||
246 | usage("Could not stat the pcap file. Check file path."); |
||
247 | exit(-1); |
||
248 | } else if (teststat.st_size == 0) { |
||
249 | usage("Empty pcap file (0 bytes). Check file contents."); |
||
250 | exit(-1); |
||
251 | } |
||
252 | } |
||
253 | |||
254 | int openpcap(struct capture_data *capdata) |
||
255 | { |
||
256 | |||
257 | /* Assume for now it's a libpcap file */ |
||
258 | p = pcap_open_offline(capdata->pcapfilename, errbuf); |
||
259 | if (p == NULL) { |
||
260 | perror("Unable to open capture file"); |
||
261 | return (-1); |
||
262 | } |
||
263 | |||
264 | /* Determine link type */ |
||
265 | capdata->pcaptype = pcap_datalink(p); |
||
266 | |||
267 | /* Determine offset to EAP frame based on link type */ |
||
268 | switch (capdata->pcaptype) { |
||
269 | case DLT_NULL: |
||
270 | case DLT_EN10MB: |
||
271 | case DLT_IEEE802_11: |
||
272 | case DLT_PRISM_HEADER: |
||
273 | case DLT_IEEE802_11_RADIO: |
||
274 | break; |
||
275 | default: |
||
276 | /* Unknown/unsupported pcap type */ |
||
277 | return (1); |
||
278 | } |
||
279 | |||
280 | return (0); |
||
281 | } |
||
282 | |||
283 | void closepcap(struct capture_data *capdata) |
||
284 | { |
||
285 | |||
286 | /* Assume it's a libpcap file for now */ |
||
287 | pcap_close(p); |
||
288 | } |
||
289 | |||
290 | /* Populates global *packet, returns status */ |
||
291 | int getpacket(struct capture_data *capdata) |
||
292 | { |
||
293 | /* Assume it's a libpcap file for now */ |
||
294 | int ret; |
||
295 | struct ieee80211_radiotap_header *rtaphdr; |
||
296 | int rtaphdrlen=0; |
||
297 | struct dot11hdr *dot11 = NULL; |
||
298 | |||
299 | /* Loop on pcap_next_ex until we get a packet we want, return from |
||
300 | * this while loop. This is kinda hack. |
||
301 | */ |
||
302 | while ((ret = pcap_next_ex(p, &h, (const u_char **)&packet)) |
||
303 | && ret > 0) { |
||
304 | |||
305 | /* Determine offset to EAP frame based on link type */ |
||
306 | switch (capdata->pcaptype) { |
||
307 | case DLT_NULL: |
||
308 | case DLT_EN10MB: |
||
309 | /* Standard ethernet header */ |
||
310 | capdata->dot1x_offset = 14; |
||
311 | capdata->l2type_offset = 12; |
||
312 | capdata->dstmac_offset = 0; |
||
313 | capdata->srcmac_offset = 6; |
||
314 | return(ret); |
||
315 | break; |
||
316 | |||
317 | case DLT_IEEE802_11: |
||
318 | /* If this is not a data packet, get the next frame */ |
||
319 | dot11 = (struct dot11hdr *)packet; |
||
320 | if (dot11->u1.fc.type != DOT11_FC_TYPE_DATA) { |
||
321 | continue; |
||
322 | } |
||
323 | |||
324 | capdata->dstmac_offset = 4; |
||
325 | capdata->srcmac_offset = 10; |
||
326 | |||
327 | /* differentiate QoS data and non-QoS data frames */ |
||
328 | if (dot11->u1.fc.subtype == DOT11_FC_SUBTYPE_QOSDATA) { |
||
329 | /* 26 bytes 802.11 header, 8 for 802.2 header */ |
||
330 | capdata->dot1x_offset = 34; |
||
331 | capdata->l2type_offset = 32; |
||
332 | } else if (dot11->u1.fc.subtype == DOT11_FC_SUBTYPE_DATA) { |
||
333 | /* 24 bytes 802.11 header, 8 for 802.2 header */ |
||
334 | capdata->dot1x_offset = 32; |
||
335 | capdata->l2type_offset = 30; |
||
336 | } else { |
||
337 | /* Not a data frame we support */ |
||
338 | continue; |
||
339 | } |
||
340 | return(ret); |
||
341 | break; |
||
342 | |||
343 | case DLT_PRISM_HEADER: |
||
344 | /* 802.11 frames with AVS header, AVS header is 144 |
||
345 | * bytes */ |
||
346 | |||
347 | /* If this is not a data packet, get the next frame */ |
||
348 | dot11 = ((struct dot11hdr *)(packet+144)); |
||
349 | if (dot11->u1.fc.type != DOT11_FC_TYPE_DATA) { |
||
350 | continue; |
||
351 | } |
||
352 | capdata->dstmac_offset = 4 + 144; |
||
353 | capdata->srcmac_offset = 10 + 144; |
||
354 | |||
355 | /* differentiate QoS data and non-QoS data frames */ |
||
356 | if (dot11->u1.fc.subtype == DOT11_FC_SUBTYPE_QOSDATA) { |
||
357 | capdata->dot1x_offset = 34 + 144; |
||
358 | capdata->l2type_offset = 32 + 144; |
||
359 | } else if (dot11->u1.fc.subtype == DOT11_FC_SUBTYPE_DATA) { |
||
360 | capdata->dot1x_offset = 32 + 144; |
||
361 | capdata->l2type_offset = 30 + 144; |
||
362 | } else { |
||
363 | /* Not a data frame we support */ |
||
364 | continue; |
||
365 | } |
||
366 | return(ret); |
||
367 | break; |
||
368 | |||
369 | case DLT_IEEE802_11_RADIO: |
||
370 | /* Radiotap header, need to calculate offset to payload |
||
371 | on a per-packet basis. |
||
372 | */ |
||
373 | rtaphdr = (struct ieee80211_radiotap_header *)packet; |
||
374 | /* rtap is LE */ |
||
375 | rtaphdrlen = le16_to_cpu(rtaphdr->it_len); |
||
376 | |||
377 | /* Sanity check on header length, 10 bytes is min |
||
378 | 802.11 len */ |
||
379 | if (rtaphdrlen > (h->len - 10)) { |
||
380 | return -2; /* Bad radiotap data */ |
||
381 | } |
||
382 | |||
383 | capdata->dstmac_offset = 4 + rtaphdrlen; |
||
384 | capdata->srcmac_offset = 10 + rtaphdrlen; |
||
385 | |||
386 | dot11 = ((struct dot11hdr *)(packet+rtaphdrlen)); |
||
387 | /* differentiate QoS data and non-QoS data frames */ |
||
388 | if (dot11->u1.fc.subtype == DOT11_FC_SUBTYPE_QOSDATA) { |
||
389 | capdata->dot1x_offset = 34 + rtaphdrlen; |
||
390 | capdata->l2type_offset = 32 + rtaphdrlen; |
||
391 | } else if (dot11->u1.fc.subtype == |
||
392 | DOT11_FC_SUBTYPE_DATA) { |
||
393 | capdata->dot1x_offset = 32 + rtaphdrlen; |
||
394 | capdata->l2type_offset = 30 + rtaphdrlen; |
||
395 | } else { |
||
396 | /* Not a data frame we support */ |
||
397 | continue; |
||
398 | } |
||
399 | return(ret); |
||
400 | break; |
||
401 | |||
402 | default: |
||
403 | /* Unknown/unsupported pcap type */ |
||
404 | return (1); |
||
405 | } |
||
406 | } |
||
407 | |||
408 | return (ret); |
||
409 | } |
||
410 | |||
411 | void handle_dot1x(struct crack_data *cdata, struct capture_data *capdata, |
||
412 | struct user_opt *opt) |
||
413 | { |
||
414 | struct ieee8021x *dot1xhdr; |
||
415 | struct wpa_eapol_key *eapolkeyhdr; |
||
416 | int eapolkeyoffset; |
||
417 | int key_info, index; |
||
418 | |||
419 | /* We're going after the last three frames in the 4-way handshake. |
||
420 | In the last frame of the TKIP exchange, the authenticator nonce is |
||
421 | omitted. In cases where there is a unicast and a multicast key |
||
422 | distributed, frame 4 will include the authenticator nonce. In some |
||
423 | cases however, there is no multicast key distribution, so frame 4 has |
||
424 | no authenticator nonce. For this reason, we need to capture information |
||
425 | from the 2nd, 3rd and 4th frames to accommodate cases where there is no |
||
426 | multicast key delivery. Suckage. |
||
427 | */ |
||
428 | |||
429 | dot1xhdr = (struct ieee8021x *)&packet[capdata->dot1x_offset]; |
||
430 | eapolkeyoffset = capdata->dot1x_offset + sizeof(struct ieee8021x); |
||
431 | eapolkeyhdr = (struct wpa_eapol_key *)&packet[eapolkeyoffset]; |
||
432 | |||
433 | /* Bitwise fields in the key_info field of the EAPOL-Key header */ |
||
434 | key_info = be_to_host16(eapolkeyhdr->key_info); |
||
435 | cdata->ver = key_info & WPA_KEY_INFO_TYPE_MASK; |
||
436 | index = key_info & WPA_KEY_INFO_KEY_INDEX_MASK; |
||
437 | |||
438 | if (opt->nonstrict == 0) { |
||
439 | |||
440 | /* Check for EAPOL version 1, type EAPOL-Key */ |
||
441 | if (dot1xhdr->version != 1 || dot1xhdr->type != 3) { |
||
442 | return; |
||
443 | } |
||
444 | |||
445 | } else { |
||
446 | |||
447 | /* Check for type EAPOL-Key */ |
||
448 | if (dot1xhdr->type != 3) { |
||
449 | return; |
||
450 | } |
||
451 | |||
452 | } |
||
453 | if (cdata->ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && |
||
454 | cdata->ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { |
||
455 | return; |
||
456 | } |
||
457 | |||
458 | if (cdata->ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { |
||
459 | /* Check for WPA key, and pairwise key type */ |
||
460 | if (eapolkeyhdr->type != 254 || |
||
461 | (key_info & WPA_KEY_INFO_KEY_TYPE) == 0) { |
||
462 | return; |
||
463 | } |
||
464 | } else if (cdata->ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { |
||
465 | if (eapolkeyhdr->type != 2 || |
||
466 | (key_info & WPA_KEY_INFO_KEY_TYPE) == 0) { |
||
467 | return; |
||
468 | } |
||
469 | } |
||
470 | |||
471 | if (opt->nonstrict == 0) { |
||
472 | |||
473 | /* Check for frame 2 of the 4-way handshake */ |
||
474 | if ((key_info & WPA_KEY_INFO_MIC) |
||
475 | && (key_info & WPA_KEY_INFO_ACK) == 0 |
||
476 | && (key_info & WPA_KEY_INFO_INSTALL) == 0 |
||
477 | && eapolkeyhdr->key_data_length > 0) { |
||
478 | |||
479 | /* All we need from this frame is the authenticator nonce */ |
||
480 | memcpy(cdata->snonce, eapolkeyhdr->key_nonce, |
||
481 | sizeof(cdata->snonce)); |
||
482 | cdata->snonceset = 1; |
||
483 | |||
484 | /* Check for frame 3 of the 4-way handshake */ |
||
485 | } else if ((key_info & WPA_KEY_INFO_MIC) |
||
486 | && (key_info & WPA_KEY_INFO_INSTALL) |
||
487 | && (key_info & WPA_KEY_INFO_ACK)) { |
||
488 | |||
489 | memcpy(cdata->spa, &packet[capdata->dstmac_offset], |
||
490 | sizeof(cdata->spa)); |
||
491 | memcpy(cdata->aa, &packet[capdata->srcmac_offset], |
||
492 | sizeof(cdata->aa)); |
||
493 | memcpy(cdata->anonce, eapolkeyhdr->key_nonce, |
||
494 | sizeof(cdata->anonce)); |
||
495 | cdata->aaset = 1; |
||
496 | cdata->spaset = 1; |
||
497 | cdata->anonceset = 1; |
||
498 | /* We save the replay counter value in the 3rd frame to match |
||
499 | against the 4th frame of the four-way handshake */ |
||
500 | memcpy(cdata->replay_counter, |
||
501 | eapolkeyhdr->replay_counter, 8); |
||
502 | |||
503 | /* Check for frame 4 of the four-way handshake */ |
||
504 | } else if ((key_info & WPA_KEY_INFO_MIC) |
||
505 | && (key_info & WPA_KEY_INFO_ACK) == 0 |
||
506 | && (key_info & WPA_KEY_INFO_INSTALL) == 0 |
||
507 | && (memcmp (cdata->replay_counter, |
||
508 | eapolkeyhdr->replay_counter, 8) == 0)) { |
||
509 | |||
510 | memcpy(cdata->keymic, eapolkeyhdr->key_mic, |
||
511 | sizeof(cdata->keymic)); |
||
512 | memcpy(cdata->eapolframe, &packet[capdata->dot1x_offset], |
||
513 | sizeof(cdata->eapolframe)); |
||
514 | cdata->keymicset = 1; |
||
515 | cdata->eapolframeset = 1; |
||
516 | } |
||
517 | } else { |
||
518 | |||
519 | /* Check for frame 1 of the 4-way handshake */ |
||
520 | if ((key_info & WPA_KEY_INFO_MIC) == 0 |
||
521 | && (key_info & WPA_KEY_INFO_ACK) |
||
522 | && (key_info & WPA_KEY_INFO_INSTALL) == 0 ) { |
||
523 | /* All we need from this frame is the authenticator nonce */ |
||
524 | memcpy(cdata->anonce, eapolkeyhdr->key_nonce, |
||
525 | sizeof(cdata->anonce)); |
||
526 | cdata->anonceset = 1; |
||
527 | |||
528 | /* Check for frame 2 of the 4-way handshake */ |
||
529 | } else if ((key_info & WPA_KEY_INFO_MIC) |
||
530 | && (key_info & WPA_KEY_INFO_INSTALL) == 0 |
||
531 | && (key_info & WPA_KEY_INFO_ACK) == 0 |
||
532 | && eapolkeyhdr->key_data_length > 0) { |
||
533 | |||
534 | cdata->eapolframe_size = ( packet[capdata->dot1x_offset + 2] << 8 ) |
||
535 | + packet[capdata->dot1x_offset + 3] + 4; |
||
536 | |||
537 | memcpy(cdata->spa, &packet[capdata->dstmac_offset], |
||
538 | sizeof(cdata->spa)); |
||
539 | cdata->spaset = 1; |
||
540 | |||
541 | memcpy(cdata->aa, &packet[capdata->srcmac_offset], |
||
542 | sizeof(cdata->aa)); |
||
543 | cdata->aaset = 1; |
||
544 | |||
545 | memcpy(cdata->snonce, eapolkeyhdr->key_nonce, |
||
546 | sizeof(cdata->snonce)); |
||
547 | cdata->snonceset = 1; |
||
548 | |||
549 | memcpy(cdata->keymic, eapolkeyhdr->key_mic, |
||
550 | sizeof(cdata->keymic)); |
||
551 | cdata->keymicset = 1; |
||
552 | |||
553 | memcpy(cdata->eapolframe, &packet[capdata->dot1x_offset], |
||
554 | cdata->eapolframe_size); |
||
555 | cdata->eapolframeset = 1; |
||
556 | |||
557 | |||
558 | /* Check for frame 3 of the 4-way handshake */ |
||
559 | } else if ((key_info & WPA_KEY_INFO_MIC) |
||
560 | && (key_info & WPA_KEY_INFO_ACK) |
||
561 | && (key_info & WPA_KEY_INFO_INSTALL)) { |
||
562 | /* All we need from this frame is the authenticator nonce */ |
||
563 | memcpy(cdata->anonce, eapolkeyhdr->key_nonce, |
||
564 | sizeof(cdata->anonce)); |
||
565 | cdata->anonceset = 1; |
||
566 | } |
||
567 | } |
||
568 | } |
||
569 | |||
570 | void dump_all_fields(struct crack_data cdata, struct user_opt *opt) |
||
571 | { |
||
572 | |||
573 | printf("AA is:"); |
||
574 | lamont_hdump(cdata.aa, 6); |
||
575 | printf("\n"); |
||
576 | |||
577 | printf("SPA is:"); |
||
578 | lamont_hdump(cdata.spa, 6); |
||
579 | printf("\n"); |
||
580 | |||
581 | printf("snonce is:"); |
||
582 | lamont_hdump(cdata.snonce, 32); |
||
583 | printf("\n"); |
||
584 | |||
585 | printf("anonce is:"); |
||
586 | lamont_hdump(cdata.anonce, 32); |
||
587 | printf("\n"); |
||
588 | |||
589 | printf("keymic is:"); |
||
590 | lamont_hdump(cdata.keymic, 16); |
||
591 | printf("\n"); |
||
592 | |||
593 | printf("eapolframe is:"); |
||
594 | if (opt->nonstrict == 0) { |
||
595 | lamont_hdump(cdata.eapolframe, 99); |
||
596 | } else { |
||
597 | lamont_hdump(cdata.eapolframe, 125); |
||
598 | } |
||
599 | |||
600 | printf("\n"); |
||
601 | |||
602 | } |
||
603 | |||
604 | void printstats(struct timeval start, struct timeval end, |
||
605 | unsigned long int wordcount) |
||
606 | { |
||
607 | |||
608 | float elapsed = 0; |
||
609 | |||
610 | if (end.tv_usec < start.tv_usec) { |
||
611 | end.tv_sec -= 1; |
||
612 | end.tv_usec += 1000000; |
||
613 | } |
||
614 | end.tv_sec -= start.tv_sec; |
||
615 | end.tv_usec -= start.tv_usec; |
||
616 | elapsed = end.tv_sec + end.tv_usec / 1000000.0; |
||
617 | |||
618 | printf("\n%lu passphrases tested in %.2f seconds: %.2f passphrases/" |
||
619 | "second\n", wordcount, elapsed, wordcount / elapsed); |
||
620 | } |
||
621 | |||
622 | int nexthashrec(FILE * fp, struct hashdb_rec *rec) |
||
623 | { |
||
624 | |||
625 | int recordlength, wordlen; |
||
626 | |||
627 | if (fread(&rec->rec_size, sizeof(rec->rec_size), 1, fp) != 1) { |
||
628 | |||
629 | perror("fread"); |
||
630 | return -1; |
||
631 | } |
||
632 | |||
633 | recordlength = abs(rec->rec_size); |
||
634 | wordlen = recordlength - (sizeof(rec->pmk) + sizeof(rec->rec_size)); |
||
635 | |||
636 | if (wordlen > 63 || wordlen < 8) { |
||
637 | fprintf(stderr, "Invalid word length: %d\n", wordlen); |
||
638 | return -1; |
||
639 | } |
||
640 | |||
641 | /* hackity, hack, hack, hack */ |
||
642 | rec->word = password_buf; |
||
643 | |||
644 | if (fread(rec->word, wordlen, 1, fp) != 1) { |
||
645 | perror("fread"); |
||
646 | return -1; |
||
647 | } |
||
648 | |||
649 | if (fread(rec->pmk, sizeof(rec->pmk), 1, fp) != 1) { |
||
650 | perror("fread"); |
||
651 | return -1; |
||
652 | } |
||
653 | |||
654 | return recordlength; |
||
655 | } |
||
656 | |||
657 | int nextdictword(char *word, FILE * fp) |
||
658 | { |
||
659 | |||
660 | if (fgets(word, MAXPASSLEN + 1, fp) == NULL) { |
||
661 | return (-1); |
||
662 | } |
||
663 | |||
664 | /* Remove newline */ |
||
665 | word[strlen(word) - 1] = '\0'; |
||
666 | |||
667 | if (feof(fp)) { |
||
668 | return (-1); |
||
669 | } |
||
670 | |||
671 | return (strlen(word)); |
||
672 | } |
||
673 | |||
674 | void hmac_hash(int ver, u8 *key, int hashlen, u8 *buf, int buflen, u8 *mic) |
||
675 | { |
||
676 | u8 hash[SHA1_MAC_LEN]; |
||
677 | |||
678 | if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { |
||
679 | hmac_md5(key, hashlen, buf, buflen, mic); |
||
680 | } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { |
||
681 | hmac_sha1(key, hashlen, buf, buflen, hash, NOCACHED); |
||
682 | memcpy(mic, hash, MD5_DIGEST_LENGTH); /* only 16 bytes, not 20 */ |
||
683 | } |
||
684 | } |
||
685 | |||
686 | int hashfile_attack(struct user_opt *opt, char *passphrase, |
||
687 | struct crack_data *cdata) |
||
688 | { |
||
689 | |||
690 | FILE *fp; |
||
691 | int reclen, wordlen; |
||
692 | u8 pmk[32]; |
||
693 | u8 ptk[64]; |
||
694 | u8 keymic[16]; |
||
695 | struct wpa_ptk *ptkset; |
||
696 | struct hashdb_rec rec; |
||
697 | struct hashdb_head hf_head; |
||
698 | char headerssid[33]; |
||
699 | |||
700 | /* Open the hash file */ |
||
701 | if (*opt->hashfile == '-') { |
||
702 | printf("Using STDIN for hashfile contents.\n"); |
||
703 | fp = stdin; |
||
704 | } else { |
||
705 | fp = fopen(opt->hashfile, "rb"); |
||
706 | if (fp == NULL) { |
||
707 | perror("fopen"); |
||
708 | return(-1); |
||
709 | } |
||
710 | } |
||
711 | |||
712 | /* Read the record header contents */ |
||
713 | if (fread(&hf_head, sizeof(hf_head), 1, fp) != 1) { |
||
714 | perror("fread"); |
||
715 | return(-1); |
||
716 | } |
||
717 | |||
718 | /* Ensure selected SSID matches what's stored in the header record */ |
||
719 | if (memcmp(hf_head.ssid, opt->ssid, hf_head.ssidlen) != 0) { |
||
720 | |||
721 | memcpy(&headerssid, hf_head.ssid, hf_head.ssidlen); |
||
722 | headerssid[hf_head.ssidlen] = 0; /* NULL terminate string */ |
||
723 | |||
724 | fprintf(stderr, "\nSSID in hashfile (\"%s\") does not match " |
||
725 | "SSID specified on the \n" |
||
726 | "command line (\"%s\"). You cannot " |
||
727 | "mix and match SSID's for this\nattack.\n\n", |
||
728 | headerssid, opt->ssid); |
||
729 | return(-1); |
||
730 | } |
||
731 | |||
732 | |||
733 | while (feof(fp) == 0 && sig == 0) { |
||
734 | |||
735 | /* Populate the hashdb_rec with the next record */ |
||
736 | reclen = nexthashrec(fp, &rec); |
||
737 | |||
738 | /* nexthashrec returns the length of the record, test to ensure |
||
739 | passphrase is greater than 8 characters */ |
||
740 | wordlen = rec.rec_size - |
||
741 | (sizeof(rec.pmk) + sizeof(rec.rec_size)); |
||
742 | if (wordlen < 8) { |
||
743 | printf("Found a record that was too short, this " |
||
744 | "shouldn't happen in practice!\n"); |
||
745 | return(-1); |
||
746 | } |
||
747 | |||
748 | /* Populate passphrase with the record contents */ |
||
749 | memcpy(passphrase, rec.word, wordlen); |
||
750 | |||
751 | /* NULL terminate passphrase string */ |
||
752 | passphrase[wordlen] = 0; |
||
753 | |||
754 | if (opt->verbose > 1) { |
||
755 | printf("Testing passphrase: %s\n", passphrase); |
||
756 | } |
||
757 | |||
758 | /* Increment the words tested counter */ |
||
759 | wordstested++; |
||
760 | |||
761 | /* Status display */ |
||
762 | if ((wordstested % 10000) == 0) { |
||
763 | printf("key no. %ld: %s\n", wordstested, passphrase); |
||
764 | fflush(stdout); |
||
765 | } |
||
766 | |||
767 | if (opt->verbose > 1) { |
||
768 | printf("Calculating PTK for \"%s\".\n", passphrase); |
||
769 | } |
||
770 | |||
771 | if (opt->verbose > 2) { |
||
772 | printf("PMK is"); |
||
773 | lamont_hdump(pmk, sizeof(pmk)); |
||
774 | } |
||
775 | |||
776 | if (opt->verbose > 1) { |
||
777 | printf("Calculating PTK with collected data and " |
||
778 | "PMK.\n"); |
||
779 | } |
||
780 | |||
781 | wpa_pmk_to_ptk(rec.pmk, cdata->aa, cdata->spa, cdata->anonce, |
||
782 | cdata->snonce, ptk, sizeof(ptk)); |
||
783 | |||
784 | if (opt->verbose > 2) { |
||
785 | printf("Calculated PTK for \"%s\" is", passphrase); |
||
786 | lamont_hdump(ptk, sizeof(ptk)); |
||
787 | } |
||
788 | |||
789 | ptkset = (struct wpa_ptk *)ptk; |
||
790 | |||
791 | if (opt->verbose > 1) { |
||
792 | printf("Calculating hmac-MD5 Key MIC for this " |
||
793 | "frame.\n"); |
||
794 | } |
||
795 | |||
796 | if (opt->nonstrict == 0) { |
||
797 | hmac_hash(cdata->ver, ptkset->mic_key, 16, cdata->eapolframe, |
||
798 | sizeof(cdata->eapolframe), keymic); |
||
799 | } else { |
||
800 | hmac_hash(cdata->ver, ptkset->mic_key, 16, cdata->eapolframe, |
||
801 | cdata->eapolframe_size, keymic); |
||
802 | } |
||
803 | |||
804 | if (opt->verbose > 2) { |
||
805 | printf("Calculated MIC with \"%s\" is", passphrase); |
||
806 | lamont_hdump(keymic, sizeof(keymic)); |
||
807 | } |
||
808 | |||
809 | if (memcmp(&cdata->keymic, &keymic, sizeof(keymic)) == 0) { |
||
810 | return 0; |
||
811 | } else { |
||
812 | continue; |
||
813 | } |
||
814 | } |
||
815 | |||
816 | return 1; |
||
817 | } |
||
818 | |||
819 | int dictfile_attack(struct user_opt *opt, char *passphrase, |
||
820 | struct crack_data *cdata) |
||
821 | { |
||
822 | |||
823 | FILE *fp; |
||
824 | int fret; |
||
825 | u8 pmk[32]; |
||
826 | u8 ptk[64]; |
||
827 | u8 keymic[16]; |
||
828 | struct wpa_ptk *ptkset; |
||
829 | |||
830 | /* Open the dictionary file */ |
||
831 | if (*opt->dictfile == '-') { |
||
832 | printf("Using STDIN for words.\n"); |
||
833 | fp = stdin; |
||
834 | } else { |
||
835 | fp = fopen(opt->dictfile, "r"); |
||
836 | if (fp == NULL) { |
||
837 | perror("fopen"); |
||
838 | exit(-1); |
||
839 | } |
||
840 | } |
||
841 | |||
842 | |||
843 | while (feof(fp) == 0 && sig == 0) { |
||
844 | |||
845 | /* Populate "passphrase" with the next word */ |
||
846 | fret = nextdictword(passphrase, fp); |
||
847 | if (fret < 0) { |
||
848 | break; |
||
849 | } |
||
850 | |||
851 | if (opt->verbose > 1) { |
||
852 | printf("Testing passphrase: %s\n", passphrase); |
||
853 | } |
||
854 | |||
855 | /* |
||
856 | * Test length of word. IEEE 802.11i indicates the passphrase |
||
857 | * must be at least 8 characters in length, and no more than 63 |
||
858 | * characters in length. |
||
859 | */ |
||
860 | if (fret < 8 || fret > 63) { |
||
861 | if (opt->verbose) { |
||
862 | printf("Invalid passphrase length: %s (%u).\n", |
||
863 | passphrase, strlen(passphrase)); |
||
864 | } |
||
865 | continue; |
||
866 | } else { |
||
867 | /* This word is good, increment the words tested |
||
868 | counter */ |
||
869 | wordstested++; |
||
870 | } |
||
871 | |||
872 | /* Status display */ |
||
873 | if ((wordstested % 1000) == 0) { |
||
874 | printf("key no. %ld: %s\n", wordstested, passphrase); |
||
875 | fflush(stdout); |
||
876 | } |
||
877 | |||
878 | if (opt->verbose > 1) { |
||
879 | printf("Calculating PMK for \"%s\".\n", passphrase); |
||
880 | } |
||
881 | |||
882 | pbkdf2_sha1(passphrase, opt->ssid, strlen(opt->ssid), 4096, |
||
883 | pmk, sizeof(pmk), USECACHED); |
||
884 | |||
885 | if (opt->verbose > 2) { |
||
886 | printf("PMK is"); |
||
887 | lamont_hdump(pmk, sizeof(pmk)); |
||
888 | } |
||
889 | |||
890 | if (opt->verbose > 1) { |
||
891 | printf("Calculating PTK with collected data and " |
||
892 | "PMK.\n"); |
||
893 | } |
||
894 | |||
895 | wpa_pmk_to_ptk(pmk, cdata->aa, cdata->spa, cdata->anonce, |
||
896 | cdata->snonce, ptk, sizeof(ptk)); |
||
897 | |||
898 | if (opt->verbose > 2) { |
||
899 | printf("Calculated PTK for \"%s\" is", passphrase); |
||
900 | lamont_hdump(ptk, sizeof(ptk)); |
||
901 | } |
||
902 | |||
903 | ptkset = (struct wpa_ptk *)ptk; |
||
904 | |||
905 | if (opt->verbose > 1) { |
||
906 | printf("Calculating hmac-MD5 Key MIC for this " |
||
907 | "frame.\n"); |
||
908 | } |
||
909 | |||
910 | if (opt->nonstrict == 0) { |
||
911 | hmac_hash(cdata->ver, ptkset->mic_key, 16, cdata->eapolframe, |
||
912 | sizeof(cdata->eapolframe), keymic); |
||
913 | } else { |
||
914 | hmac_hash(cdata->ver, ptkset->mic_key, 16, cdata->eapolframe, |
||
915 | cdata->eapolframe_size, keymic); |
||
916 | } |
||
917 | |||
918 | if (opt->verbose > 2) { |
||
919 | printf("Calculated MIC with \"%s\" is", passphrase); |
||
920 | lamont_hdump(keymic, sizeof(keymic)); |
||
921 | } |
||
922 | |||
923 | if (memcmp(&cdata->keymic, &keymic, sizeof(keymic)) == 0) { |
||
924 | return 0; |
||
925 | } else { |
||
926 | continue; |
||
927 | } |
||
928 | } |
||
929 | |||
930 | return 1; |
||
931 | } |
||
932 | |||
933 | int main(int argc, char **argv) |
||
934 | { |
||
935 | struct user_opt opt; |
||
936 | struct crack_data cdata; |
||
937 | struct capture_data capdata; |
||
938 | struct wpa_eapol_key *eapkeypacket; |
||
939 | u8 eapolkey_nomic[99]; |
||
940 | struct timeval start, end; |
||
941 | int ret; |
||
942 | char passphrase[MAXPASSLEN + 1]; |
||
943 | |||
944 | printf("%s %s - WPA-PSK dictionary attack. <jwright@hasborg.com>\n", |
||
945 | PROGNAME, VER); |
||
946 | |||
947 | memset(&opt, 0, sizeof(struct user_opt)); |
||
948 | memset(&capdata, 0, sizeof(struct capture_data)); |
||
949 | memset(&cdata, 0, sizeof(struct crack_data)); |
||
950 | memset(&eapolkey_nomic, 0, sizeof(eapolkey_nomic)); |
||
951 | |||
952 | /* Collect and test command-line arguments */ |
||
953 | parseopts(&opt, argc, argv); |
||
954 | testopts(&opt); |
||
955 | printf("\n"); |
||
956 | |||
957 | /* Populate capdata struct */ |
||
958 | strncpy(capdata.pcapfilename, opt.pcapfile, |
||
959 | sizeof(capdata.pcapfilename)); |
||
960 | if (openpcap(&capdata) != 0) { |
||
961 | printf("Unsupported or unrecognized pcap file.\n"); |
||
962 | exit(-1); |
||
963 | } |
||
964 | |||
965 | /* populates global *packet */ |
||
966 | while (getpacket(&capdata) > 0) { |
||
967 | if (opt.verbose > 2) { |
||
968 | lamont_hdump(packet, h->len); |
||
969 | } |
||
970 | /* test packet for data that we are looking for */ |
||
971 | if (memcmp(&packet[capdata.l2type_offset], DOT1X_LLCTYPE, 2) == |
||
972 | |||
973 | capdata.l2type_offset + sizeof(struct wpa_eapol_key))) { |
||
974 | /* It's a dot1x frame, process it */ |
||
975 | handle_dot1x(&cdata, &capdata, &opt); |
||
976 | if (cdata.aaset && cdata.spaset && cdata.snonceset && |
||
977 | cdata.anonceset && cdata.keymicset |
||
978 | && cdata.eapolframeset) { |
||
979 | /* We've collected everything we need. */ |
||
980 | break; |
||
981 | } |
||
982 | } |
||
983 | } |
||
984 | |||
985 | closepcap(&capdata); |
||
986 | |||
987 | if (!(cdata.aaset && cdata.spaset && cdata.snonceset && |
||
988 | cdata.anonceset && cdata.keymicset && cdata.eapolframeset)) { |
||
989 | printf("End of pcap capture file, incomplete four-way handshake " |
||
990 | "exchange. Try using a\ndifferent capture.\n"); |
||
991 | exit(-1); |
||
992 | } else { |
||
993 | if (cdata.ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { |
||
994 | printf("Collected all necessary data to mount crack" |
||
995 | " against WPA2/PSK passphrase.\n"); |
||
996 | } else if (cdata.ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { |
||
997 | printf("Collected all necessary data to mount crack" |
||
998 | " against WPA/PSK passphrase.\n"); |
||
999 | } |
||
1000 | } |
||
1001 | |||
1002 | if (opt.verbose > 1) { |
||
1003 | dump_all_fields(cdata, &opt); |
||
1004 | } |
||
1005 | |||
1006 | if (opt.checkonly) { |
||
1007 | /* Don't attack the PSK, just return non-error return code */ |
||
1008 | return 0; |
||
1009 | } |
||
1010 | |||
1011 | /* Zero mic and length data for hmac-md5 calculation */ |
||
1012 | eapkeypacket = |
||
1013 | (struct wpa_eapol_key *)&cdata.eapolframe[EAPDOT1XOFFSET]; |
||
1014 | memset(&eapkeypacket->key_mic, 0, sizeof(eapkeypacket->key_mic)); |
||
1015 | if (opt.nonstrict == 0) { |
||
1016 | eapkeypacket->key_data_length = 0; |
||
1017 | } |
||
1018 | |||
1019 | printf("Starting dictionary attack. Please be patient.\n"); |
||
1020 | fflush(stdout); |
||
1021 | |||
1022 | signal(SIGINT, cleanup); |
||
1023 | signal(SIGTERM, cleanup); |
||
1024 | signal(SIGQUIT, cleanup); |
||
1025 | |||
1026 | gettimeofday(&start, 0); |
||
1027 | |||
1028 | if (!IsBlank(opt.hashfile)) { |
||
1029 | ret = hashfile_attack(&opt, passphrase, &cdata); |
||
1030 | } else if (!IsBlank(opt.dictfile)) { |
||
1031 | ret = dictfile_attack(&opt, passphrase, &cdata); |
||
1032 | } else { |
||
1033 | usage("Must specify dictfile or hashfile (-f or -d)"); |
||
1034 | exit(-1); |
||
1035 | } |
||
1036 | |||
1037 | if (ret == 0) { |
||
1038 | printf("\nThe PSK is \"%s\".\n", passphrase); |
||
1039 | gettimeofday(&end, 0); |
||
1040 | printstats(start, end, wordstested); |
||
1041 | return 0; |
||
1042 | } else { |
||
1043 | printf("Unable to identify the PSK from the dictionary file. " |
||
1044 | "Try expanding your\npassphrase list, and double-check" |
||
1045 | " the SSID. Sorry it didn't work out.\n"); |
||
1046 | gettimeofday(&end, 0); |
||
1047 | printstats(start, end, wordstested); |
||
1048 | return 1; |
||
1049 | } |
||
1050 | |||
1051 | return 1; |
||
1052 | |||
1053 | } |