суббота, 6 сентября 2014 г.

Продвинутый брутфорс онлайн игр

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

В ходе написания статьи, не один игрок не пострадал, все пользовательские данные собирались с публичных источников, полученные данные через брутфорс были сразу же уничтожены, а администрация серверов была поставлена в известность.

Потенциальная опасность атаки

Стоит ли вообще концентрировать внимание на таких вещах как брутфорс? Давайте предположим что у нас под рукой есть некий логин, к которому необходимо подобрать пароль. Длинна пароля от 4 до 10 символов, допускается латиница и цифры, в таком случае полный перебор займёт 36^10 - 36^4 = 3 656 158 438 383 360 попыток, т.к. 26 букв алфавита (без учёта регистра) + 10 цифр, цифра получилась внушающая. Даже если предположить что пароль с ~30% вероятностью состоит только из цифр это уже 10^10 - 10^4 = 9 999 990 000 попыток, что конечно гораздо меньше но так же не мало. Сетевой перебор при приблизительной скорости 10 000 попыток в минуту займёт около 694 дней. Казалось бы за такие цифры можно не боятся. Но тем не менее такие пароли считаются не надёжными и уязвимыми к бруту. Есть и другие расчёты основанные на реальных наблюдениях, они говорят что на любом сервере с возможностью установки нестойкого пароля, всегда найдутся люди устанавливающие уязвимые к взлому пароли и это тавтология. Иногда игрокам проще установить простой, запоминающийся пароль, или же использовать один и тот же пароль на нескольких серверах\учётках, всё это подвергает учётную запись к возможности взлома.  Давайте в контексте статьи будем считать пароли уязвимыми, если их можно подобрать по простым комбинациям символов, или если эти пароли можно получить из публичных источников.

Из вышесказанного можно сделать первый вывод: удачный брутфорс всегда должен быть нацелен на конкретный результат. По результату можно поделить брут на 2 вида: целевой и рассеянный. Целевой брутфорс выполняется на конкретные учётные записи, т.е. вам точно известны логины на которые вы и выполняете атаку. Рассеянный брутфорс нацелен абсолютно на любые уязвимые учётные записи, вы просто перебираете список логинов, полагаясь на удачу. Как странно бы не звучало, но целевой брут сложнее рассеянного, так как в случае если пароль не уязвим, нам остаётся только выполнять полный перебор диапазона, хоть это справедливо не всегда. Но рассеянный брутфорс обычно приносит больше результатов, но только там, где есть возможность использовать уязвимые пароли.

Сбор информации о логинах\паролях

И так, любой брут начинается с составления списка логинов и паролей, иначе пришлось бы просто тыкать пальцем в небо. Однако логины и пароли специфичны для каждого сервера, например логином может быть фраза из 6 - 20 символов или e-mail. Это необходимо учитывать при составлении списков для брутфорса.

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

Другим вариантом сбора базы является снятия информации о логинах непосредственно с сайта сервера. Обычно это становится возможно, если логин пользователя не является секретом. Например на сайте есть список пользователей или даже просто список персонажей. Если мы парсим список персонажей, то для валидации списка можно применять часто встречаемые AJAX механизмы для быстрой проверки на существование логина. Эти механизмы работают быстро и не требуют капчи.

Для примера возьмём игровой проект http://inoagame.com. Он имеет 3 MuOnline сервера, все аккаунты объединены в одну общую базу, регистрация происходит через основной сайт. Причем при регистрации вас просят указать логин и имя для форума, разумно предположить что большинство пользователей указывают одно и тоже имя в этих полях. На сайте так же присутствует возможность пролистывания списка пользователя и AJAX проверка существования логина, всё что нам нужно для сбора информации.

Для этой задачи я написал скрипт на Python'e:
import urllib
import urllib2
import re

def read_url(url, head):
	req = urllib2.Request(url, '', head)
	response = urllib2.urlopen(req)
	return response.read()
	
url = 'http://inoagame.com'
url_users = '/ru/users/?&page=%d'
url_ref = '/ru/register'
url_ajax = '/ajax.php'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
#authorized php session
phpsession = 'is7tjnasesiri4hnht5dbck7g6'
output = 'logins.txt'

#bypass antiddos prevention
headers = { 'User-Agent' : user_agent }
page = read_url(url, headers)

antiddos_cookie = re.match(".*_ddn_intercept_2_=([a-z0-9]+);*?", page, re.I | re.S).group(1)
headers = {'User-Agent' : user_agent, 
	'Cookie' : '_ddn_intercept_2_=' + antiddos_cookie + ';PHPSESSID=' + phpsession}

#find count of pages
comm_url = url + url_users % 1
page = read_url(comm_url, headers)
pages_count = int( re.match(".*<div class=\"total-pages\">.*<b>([0-9]+)</b>.*", 
	page, re.I | re.S).group(1) )

#parse pages
headers2 = {
	'User-Agent' : user_agent, 
	'Cookie' : '_ddn_intercept_2_=' + antiddos_cookie ,
	'Referer' : url + url_ref
}

f = open(output, 'a')

for i in range(1, pages_count + 1):
	logis_set = set()
	comm_url = url + url_users % i
	page = read_url(comm_url, headers)
	
	logins = re.findall("^.*<h4><a href=\"ru/user/[0-9]*\">(.*)</a></h4>", page, re.M)
	
	found = 0
	for login in logins:
		login = re.sub("[^a-zA-Z0-9]", '', login)
		if 4 <= len(login) <= 10: #check login in database
			#send post request to ajax gate
			values = {'type' : '17', 'login' : login}
			data = urllib.urlencode(values)
			req = urllib2.Request(url + url_ajax, data, headers2)
			response = urllib2.urlopen(req)
			page = response.read()
			
			if (page == 'true'):
				logis_set.add(login)
				found += 1

	print "Url %s parsed, found %d logins" % (comm_url, found)
	
	#save result
	for login in logis_set:
		f.write(login + '\n')

Скрипт парсит список имён на форуме и проверяет их через AJAX запрос проверки на существование логина. Таким образом через пару часов работы скрипта, мы завладели базой из 27617 логинов, что представляет собой чуть менее половины базы, при том что общий размер её ~63550 логинов.

Там где нет AJAXовых проверок логинов, можно попытать выполнять проверку логина через формы регистрации, смены пароля и т.п. Но часто такие формы защищаются капчей. В некоторых случаях капча настолько проста что написать простой код её обхода не составит особого труда. Для примера взглянем на капчи применяемые на страницах регистрации парочки игровых серверов:












Имея на руках обход капчи мы просто отправляем специально сформированный HTTP запрос и анализируем полученный ответ, который скажет нам числится ли данный логин в базе. Я уже не стал писать код для примера, так как не думаю что он кому-либо потребуется ввиду специфики капчи для разных веб-сайтов.

Если у нас нету списка паролей, но есть богатый список логинов, то можно использовать списки самых простых и популярных паролей. Не менее велика вероятность что логин будет совпадать с паролем. Но я крайне не рекомендуется использовать большие, публичные базы логинов и паролей собранных из источников не относящихся к атакуемой игре. Такие данные могут не соответствовать формату логина\пароля игрового сервера, к тому же большая часть данных будет просто бесполезна. Всегда следует собирать таргетированные на конкретную игру базы.

Поиск источника атаки

Теперь нам надо подумать что же именно мы будем брутить. Обычно варианта два: это веб-сайт или геймсервер(сервер авторизации). Оба способа имеют свои преимущества и недостатки.

Брутфорс через геймсервер обычно выполняется достаточно быстро, но организовать его достаточно сложно. Это связано с тем что инструментария для такого вида атаки может не существовать вообще, тогда нам потребуется исследовать протокол авторизации и возможно бороться с шифрованием и т.п. Тем не менее при отсутствии средств защиты от брута, мы можем достигать значительных скоростей перебора.

Брутфорс через веб-сайт достаточно медленный, однако организовать такую атаку гораздо проще. Программ для брутфорса веб-сайтов пруд-пруди. Тем не менее обычно во время авторизации на веб-сайте нам необходимо принять и распарсить целую страницу, что весьма долго. Очень хорошо если такая авторизация закручена на AJAX'е.

Еще одним недостатком web-брутфорса является то, что такие запросы требуют жирные куски ресурсов сервера. Это станет заметно если вы осуществляете распределённую параллельную атаку брутфорсом, которая в последствии перерастёт в HTTP DDOS. Другое дело геймсервер, для него авторизация это очень лёгкая операция, вы можете генерировать достаточно внушительные объёмы запросов авторизации, которые не повесят сервер и будут корректно обработаны, а это огромный плюс. Ведь брутфорс должен проходить максимально незаметно.

Распределённый, параллельный брутфорс

Вот мы и пришли к самому интересному. Мы не станем рассматривать простой одиночный брут, так как это детские побрикушки. А вот реализовать атаку через распределённый параллельный перебор, это уже достаточно эффективный подход.

Для данной статьи я написал параллельный брутфорсер Parallel Ninja (sources, binaries), который пока что умеет работать только с протоколом MuOnline. Исходный код можно доработать в принципе под любую другую игру. Брутфорсер реализован на интерфейсе MPI, бинарники собраны под реализацию Microsoft MPI (MPICH2). Благодаря MPI данный брутфорсер достаточно просто масштабируется на любое кол-во рабочих узлов.

Для эксперимента мы организовали вычислительную сеть из 6 удалённых виртуальных серверов (VPS). Был выбран произвольный MuOnline сервер с большой базой логинов в качестве цели атаки. Так же мы собрали базу данных из публичных источников, насчитывающую около 32 000 логинов(включая логины с inoagames) и 5 000 паролей. В результате первых запусков выяснилось, что пиковая скорость перебора на атакуемом сервере меньше возможности нашей вычислительной сети. Поэтому экспериментальным путём было установлено что для оптимального перебора достаточно всего  трёх вычислительных узлов. Средняя скорость перебора варьировалась в пределах 17 000 - 21 000 паролей в минуту, а это около 300-350 паролей в секунду. Если отправлять 300 HTTP запросов в секунду, то это вызвало бы достаточно сильную нагрузку на HTTP сервер и скорее всего привело бы к его отказу. Так что этот результат подтверждает эффективность брутфорса через игровой протокол.

После запуска брутфорса возникли первые трудности. Спустя первых 8-10 часов работы, атакуемый сервер упал, видимо произошел отказ в обслуживании. Поэтому было принято решение нацелить вычислительные узлы каждый на отдельный игровой саб-сервер, таким образом уравнять нагрузку и снизить шансы обнаружения атаки. Мало того, такой подход поднял скорость перебора до 24 000 - 28 000 паролей в минуту. Так же вёлся мониторинг тормозов непосредственно в игре, как и предполагалось атака не вызывает проблем и в принципе не заметна. 

В общей сложности на атаку ушло 126 часов, за это время удалось захватить 131 игровой логин, а это около 1 логина в час. Примерно 25% логинов имели простейшие шаблонные пароли, вроде 123456 и около 45% паролей состояло чисто из цифр, большинство из которых имели длину менее 10 символов.

Способы противодействия брутфорсу

Задача защиты от брутфоса не является особо сложной, не смотря на то что возможность такой атаки часто игнорируется разработчиками. Например простейшей программной реализацией такой защиты может быть задержка ответа сервера на запрос авторизации. Секунда задержки никакого значения для пользователя не сыграет, а вот для брутфорса это вызовет ряд неудобств. Тем не менее задержка только лишь снизит интенсивность атаки, поскольку атакующий может одновременно инициировать несколько сотен подключений, так что такой вариант рассматривать в качестве защиты не стоит. Другой вариант это блокировка IP при множественных запросах авторизации в течении определённого промежутка времени. Данная техника более эффективна, но требует вести базу активности IP адресов и возможно такой вариант можно реализовать средствами межсетевого экрана или аппаратными защитами. Обход такой защиты возможен только при снижении интенсивности перебора, но в таком случае скорость работы брутфорса оставит желать лучшего. Последний вариант который пришел в голову, это использование капчи непосредственно в игровом клиенте. Звучит непривычно, но при стойкой капче подход эффективен.

Помимо программной реализации защиты есть еще и ряд мер предосторожности, которые могут помочь избежать утечек информации и брутфорса. Например как уже говорилось информативность сайта может играть на руку при сборе информации, поэтому информацию о логинах необходимо держать в тайне. Любая страница авторизации при нестойкой капче может выступать в качестве источника брутфорса, тоже относится и к AJAX модулям. Поэтому стоит крайне разумно относится к данным технологиям или не использовать их вовсе. Не менее важную роль играет и структура логина и пароля, например если регистрация допускает нестойкие пароли (короткие, не содержащие символов, без учёта регистра), то пароль состоящий скажем только из цифр, длинной 4-7 символов, можно взломать со 100% вероятностью за пару часов. Поэтому разумно вносить более строгие требования к составлению пароля. Но не стоит требовать или генерировать очень сложные пароли, пользователи скорее всего начнут их записывать на бумажку или в файл, а это приведёт уже совсем к другим проблемам :) Продолжать можно ещё много, так что я думаю суть ясна.

Вот в принципе и всё

1 комментарий:

  1. Отписал в ЛС на ег-нетворк, дай знать если доходят сообщения. Хочу сделалать заказ.

    ОтветитьУдалить