Код:
Пишем универсальный брутфорсер Методика взломов бывает разной. Обычно хакер долгое время собирает информацию о сервере, а лишь затем начинает свои атаки. Причем сам процесс вторжения длится совсем недолго. Но бывает, приходится проникать на сервак сложными и неблагодарными способами. Один из таких методов - брутфорсинг паролей. В чем сложность? Брутфорс, если рассматривать его на практике сетевых ресурсов, это не что иное, как простая попытка входа на определенный сервис удаленной машины. По ее результату программа-переборщик определяет, верный пароль или нет. Если аккаунт верный - происходит завершение работы и сохранение информации о последней попытке входа. В противном случае, брутфорсер берет следующую пару логин/пароль и продолжает перебор. Казалось бы, все просто, и никаких трудностей в методике быть не должно. На самом деле они есть, как и в любых других способах взлома. В нашем случае можно выделить три основных недостатка. 1. Логи. Каждая попытка входа на сервис в обязательном порядке записывается в специальный лог-файл (как правило, в /var/log/messages). Так уж устроены операционки *nix. В этих самых логах админ может найти логин и IP-адрес, с которого производился перебор, а также время захода. По определению брутфорсинга, одной такой записью дело не ограничивается, поэтому логи - самый большой недостаток этого метода. 2. Канал. Брутфорсер каждый раз соединяется с сервером, чтобы проверить правильность пароля. Этот процесс занимает довольно длительное время и зависит от пропускной способности между тобой и удаленной машиной. Поэтому запускать программу-переборщик на диалапе не имеет смысла (можно, но успешного результата дождутся твои внуки). Для этого метода заранее ищется шелл с хорошим каналом, на котором и запускается программа. 3. Защита. В последнее время админы серверов (особенно тех, которые предоставляют бесплатные сервисы) защищаются от подобных атак. Методы защиты бывают разными: от введения специальных цифр, изображенных на картинках, генерируемых специальными скриптами, до временных блокировок входа с IP-адреса либо с логина. Но на каждое действие есть противодействие, поэтому существуют и способы обхода такой защиты ;). Web-брутфорсинг: от теории к практике Теперь попробуем написать простенький брутфорсер, чтобы ты разобрался в его алгоритме. Как известно, бродя по вебу, часто приходится иметь дело с разного рода формочками, в которые заносятся данные. Нередко в этих данных содержится пароль пользователя, после обработки которого отображается результирующая страница. Ее наличие/отсутствие указывает на верность/ошибочность пароля. Только вот использовать web-браузер в качестве переборщика весьма неудобно, хотя иногда именно так и делают некоторые личности (намек на дебилов - прим. ред.). Мы пойдем другим путем! А именно, попробуем написать полностью автоматический брутфорсер, заточенный под определенный web-сервис. Я долго думал о выборе жертвы ;), но потом решил, что это будет популярный сервис www.livejournal.com. Если ты часто бываешь в инете, то наслышан об этом онлайн-журнале. В противном случае - зайди на сайт и почитай о проекте. Ни для кого не секрет, что lj (или просто ЖЖ) аккаунты можно получить, заплатив энную сумму создателям проекта либо выпросив за ящик пива ключ у знакомого, уже имеющего ЖЖ. Дело в том, что ключ активации выдается по истечении недели с того момента, как пользователь зарегистрировался на сайте. Получив пароль к заброшенной учетной записи, ты легко можешь поменять ее имя и личные данные. Но это уже второй вопрос ;). Важно понять, как же можно получить этот самый пароль. А получить его довольно просто. Заходим на главную страницу проекта, пытаемся залогиниться под пользователем, например, "vasya" (предварительно убедившись, что линк www.livejournal.com/~vasya содержит 1-2 записи, сделанных в 2000 году ;)). Естественно, сервер сообщит о неверности введенного пароля. Вернемся назад и откроем пагу в виде HTML-кода. После просмотра нужной формы станет ясно - для успешной аутентификации достаточно передать правильные поля user и password. Первое поле мы знаем, соответственно, наше "уравнение" содержит всего одно неизвестное, а учитывая тупость Васи, который не способен придумать нормальный пароль (как и подавляющее большинство ЖЖ-пользователей), у нас появляется шанс сломать аккаунт. Начало положено! Основная ошибка начинающих кодеров - поспешная реализация задачи без четкого понимания алгоритма. Любую задачу, пусть даже примитивную, следует разложить на n пунктов, после чего задуматься о способе реализации каждого из них. И лишь после этого приступить к процессу кодинга. Так будет и в нашем случае. Что мы имеем? Название полей формы (user и password), страницу, куда передаются данные, и метод передачи (POST). Попробуем послать POST-запрос. Телнетимся на 80 порт www.livejournal.com и пишем следующее: POST /login.bml HTTP/1.0 Host: www.livejournal.com Content-Type: application/x-www-form-urlencoded Content-Length: 24 Два раза Enter user=petya&password=1234& В ответе видим пагу, которая сообщает, что пользователь petya успешно залогинился на сервер (разумеется, у нас была заранее подготовлена рабочая учетная запись). Кодинг Теперь, когда мы располагаем полной информацией о форме, ее структуре и методе, можем начинать кодить. Дополнительно замечу: чтобы узнать результат перебора, совсем не обязательно грузить всю страницу-ответ. Достаточно послать HEAD-запрос, и при верном пароле скрипт создаст кукиз, в котором будет находиться имя пользователя. Следующая конструкция кода реализует чтение паролей из словаря и последовательный их перебор. Этот кусок кода, как и все программы из этой статьи, написан на Perl'е: Таблица1: Последовательный алгоритм переборщика open(file, "$words") || die print "$!\n"; while($pass=<file>) { # По каждому слову в файле chomp($pass); crack($pass); # Переходим на процедуру crack() } close (file); После реализации этого несложного алгоритма, мы получаем работоспособный брутфорсер. Полноценный вариант программы ты можешь найти под названием ljbrut1.pl на CD либо в архиве (ссылку на архив смотри во врезке). Работает все стабильно, но крайне медленно. Поэтому теперь займемся усовершенствованием нашего кода с применением потоков. Дело в том, что в однопоточном режиме в каждом проходе цикла создание сокета происходит всего один раз. Но что нам мешает наплодить 10 параллельных запросов в одном цикле, а затем за один раз обработать все ответы? В результате получаем скрипт, который буквально за несколько минут сможет перебрать полумегабайтный словарь. Реализовать алгоритм нам поможет перловая функция fork(), о прелестях которой я уже не раз писал. Несложная модификация кода быстро превращает наш скрипт в реактивный переборщик ;). Таблица2: Параллельный алгоритм переборщика while($pass=<file>) { # По каждому слову в файле chomp($pass); # Обрезаем символ \n push(@threads,$pass); # И заносим его в отдельный массив if (scalar @threads eq $threads-1) { for ($j=0;$j<=$threads-1;$j++) { if ($pid=fork()) { # Делаем fork() push(@forked, $pid); } else { crack($threads[$j]); # Переходим на процедуру crack exit; # Завершаем подпроцесс } } killall(); # Убиваем все порожденные процессы } } Если запустить модифицированный брутфорсер (ljbrut2.pl), то итоговое время перебора паролей уменьшится в несколько раз по сравнению с первой программой. Это объясняется тем, что происходит не последовательный перебор, а порождение нескольких независимых подпроцессов, каждый из которых соединяется с удаленным сервером. Переменная $threads, определяющая максимальное количество потоков, задается пользователем из командной строки либо может быть изменена в начале скрипта. Безопасность Я не зря акцентировал твое внимание на недостатках этого метода. Если админ обнаружит несколько тысяч одинаковых соединений в логах с твоего адреса, то он просто забанит твой ip. Но я не говорил, что при этом невозможно его защитить. Самый рабочий вариант - использовать подготовленный листинг рабочих прокси-серверов (найти их ты можешь, например, на http://proxycheck.spylog.ru). Для нашей безопасности просто возьмем случайный прокси из листа, через который законнектимся на ливжурнал. Если проксик дохлый - повторим операцию и заменим его на работоспособный. Таблица3: Вариант кода для работы с proxy-серверами if ($opt_p) { # Если есть опция -p командной строки open(proxy, $proxylist) || die "cant find proxylist\n"; @proxies=<proxy>; # Считываем прокси из файла close (proxy); # И закрываем } sub crack { my $pass=shift; # Выцепляем параметр $pass ($server, $port)=split(':',$proxies[rand(int(scalar @proxies))]) if ($opt_p); # Берем рандомную строку из массива @proxies $socket=IO::Socket::INET->new(PeerAddr => $server, PeerPort => $port, Proto => tcp) or crack($pass); # Создаем соединение с livejournal.com # либо рекурсивно вызываем процедуру ... } Теперь можно с уверенностью сказать, что брутфорс будет работать быстро и безопасно. По крайней мере, если не злоупотреблять им. Ведь прокси-сервера тоже могут писать логи... MIME-кодирование: подбор паролей к web-авторизации Если уж мы заговорили о Web-брутфорсах, то следует упомянуть и о стандартных методах авторизации. Ты наверняка сталкивался с небольшим окошечком в браузере, в котором запрашивалось имя пользователя и пароль. Это могла быть как страница статистики, так и обычная авторизация на squid-прокси сервер. Когда известно имя пользователя, пароль можно попробовать перебрать. Для этого надо написать небольшой брутфорсер. Естественно, алгоритм его работы будет слегка отличаться от вышеописанного. Если просмотреть трафик между клиентом и сервером, то в случае запроса директории, защищенной паролем, в ответе от сервера будет видна ошибка 401 (Unauthorized). А среди строк заголовка обнаружится следующая запись: WWW-Authenticate: Basic realm="Secret zone" После того, как это случится, браузер автоматически запросит логин и пароль, которые будут закодированы в MIME-хеш. Затем в стандартный заголовок запроса добавится строка, подобная этой: Authorization: Basic MjIyOjExMQ== Сервер проверит правильность пароля, и, если все в порядке, пропустит клиента к секретной зоне. В противном случае, если результат не изменится, то апач вернет ошибку 401. Реализовать брутфорсер, работающий по такому алгоритму, совсем несложно. Достаточно подключить перловый модуль Base64.pm (идет в поставке с перлом по умолчанию). В этом модуле существует функция encode_base64(). Она возвращает тот самый хеш, про который я говорил выше. Остается разобраться, по какому принципу генерируется MIME-запись. На самом деле, все просто: достаточно передать функции аргумент вида "login:password", и в результате кодирования мы получим нужный хеш. Если все это реализовать, то получим следующий код: Таблица4: MIME-кодирование open(words, $wordlist); while($pass=<words>) { # Перебираем каждую строку chomp($pass); $hash=encode_base64("$login:$pass"); # Кодируем login/password $status=crack($hash); # Переходим на crack() После небольшой модификации crack() (добавления одной строки в заголовок запроса и анализа ответа) брутфорс заработал как надо. Теперь, думаю, ты без проблем сможешь сам переделать брутфорсер под работу в многопотоковом режиме. Мораль сей басни Надеюсь, что предложенные мной алгоритмы и способы их реализации не показались тебе скучными, а, наоборот, научили тебя писать хорошие переборщики паролей. Помни, что материал дан только в ознакомительных целях, и вся ответственность по его использованию ложится на тебя. При возникновении вопросов по организации и работе брутфорсеров можешь писать мне - постараюсь ответить. Сливаем софт Набор рабочих брутфорсеров ты можешь найти на нашем CD, сайте (в X-релизах), а также на моей странице в архиве http://kamensk.net.ru/forb/1/x/bruteforcers.tar.gz. В нем находятся 4 файла: ljbrut1.pl - последовательный брутфорс ЖЖ-аккаунтов. ljbrut2.pl - параллельный брутфорс ЖЖ-аккаунтов (без поддержки proxy). ljbrut3.pl - параллельный брутфорс ЖЖ-аккаунтов (с поддержкой proxy). base.pl - последовательный брутфорс для Web-авторизации. Дополнения к коду В каждом брутфорсере я использую модуль Getopt::Std, который помогает удобно парсить командную строку, передаваемую скрипту. После функции getopt(), параметрами которой являются буквы-опции командной строки, объявляются переменные $opt_опция. В ней содержится значение того или иного аргумента командной строки. Я давал подробные комментарии в коде, чтобы ты легко ориентировался среди этих опций.