Код:
Пишем универсальный брутфорсер
Методика взломов бывает разной. Обычно хакер долгое время собирает информацию о сервере, а лишь затем начинает свои атаки. Причем сам процесс вторжения длится совсем недолго. Но бывает, приходится проникать на сервак сложными и неблагодарными способами. Один из таких методов - брутфорсинг паролей.
В чем сложность?
Брутфорс, если рассматривать его на практике сетевых ресурсов, это не что иное, как простая попытка входа на определенный сервис удаленной машины. По ее результату программа-переборщик определяет, верный пароль или нет. Если аккаунт верный - происходит завершение работы и сохранение информации о последней попытке входа. В противном случае, брутфорсер берет следующую пару логин/пароль и продолжает перебор.
Казалось бы, все просто, и никаких трудностей в методике быть не должно. На самом деле они есть, как и в любых других способах взлома. В нашем случае можно выделить три основных недостатка.
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_опция. В ней содержится значение того или иного аргумента командной строки. Я давал подробные комментарии в коде, чтобы ты легко ориентировался среди этих опций.