Вступление
Зачастую на пентест-проектах по внешке или внутрянке нам удается захватить хеши паролей. Это могут быть как соленые MD5, SHA* хеши в вебе, так и NTLMv2/NTLM хеши во внутрянке.
Конечно, успех дальнейшего продвижения напрямую зависит от успеха в атаке на перебор паролей (перебор по словарю или полный перебор по маске). Атаку на подбор паролей обычно называют брутфорс или брут от английского выражения «brute force» или же грубая сила. В последствии мы также будем придерживаться общепринятой терминологии.
И все бы ничего, пароли в латинице отлично перебираются как полным перебором по маске всех символов с инкрементом, так и по заранее сгенерированным региональным и отраслевым словарям с дополнительными правилами и без, но как быть с полным перебором кириллических символов?
Казалось бы, вполне себе логичная задача. Наши клиенты находятся в России и пароли могут состоять как из кириллических символов, например, «пароль123!», «Администрация2022», «Йй123456», так и из транслитерации русских слов в английскую раскладку, к примеру, «dkflbckfd - владислав», «vjqgjxnjdsqzobr - мой почтовый ящик» или даже наоборот из английский слов и словосочетаний в кириллическую раскладку - «щзукфешщт_сцфдд - operation_cwall». Поэтому нужно иметь как англоязычные словари, так и сконвертированные в кириллическую раскладку.
Об инструментарии
В своей работе для брута мы используем hashcat, в качестве пилотного проекта ввели hashtopolis для составления очередей задач по перебору. Hashcat полезен для нас, так как процесс перебора может быть распределен на N-видеокарт с CUDA ядрами. Hashcat имеет широкую поддержку различных режимом атак (от перебора по словарю и маске до гибридных атак), так и поддержку множества типов хешей.
Проблема #1. Генерация кириллических чарсетов
Ну, подумали мы, в hashcat уже есть встроенные списки символов для перебора всех символов по маске:
?l = abcdefghijklmnopqrstuvwxyz
?u = ABCDEFGHIJKLMNOPQRSTUVWXYZ
?d = 0123456789
?h = 0123456789abcdef
?H = 0123456789ABCDEF
?s = «space»!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
?a = ?l?u?d?s
?b = 0x00 - 0xff
Должно же быть что-то готовое и для кириллицы. На момент начала ресерча мы уже знали о возможности составления кастомных комбинаций символов (чарсетов).
Несколько раундов гугления привели нас к первой отправной точке, а именно параметр - - hex-charset, который позволяет указывать символы в шестнадцатеричном виде. К слову, на просторах форумов hashcat несколько раз возникал один и тот же вопрос, а именно как же брутить кириллические и нелатинские символы.
В одной из статей форуме hashcat пользователь Toetje предложил свой атаки на SHA1 от кириллических символов:
hashcat -m 100 -a 3 -w 3 -O hashfile.txt --hex-charset
-1 d0d1
-2 b0b1b2b3b4b5b6b7b8b9babbbcbdbebf808182838485868788898a8b8c8d8e8f
-3 909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf
?1?3?1?2?1?2?1?2?1?2?1?2
Первым вопросом стало. «Черт возьми. А что это за хексы вообще?». Всё просто. Hashcat по умолчанию работает с UTF-8 кодировкой. Кастомные чарсеты 1,2,3 являются шестнадцатеричным (хексы) отображением кириллических символов. Это можно увидеть, используя интерактивный питонячий интерпретатор:
ru_chars = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя'
ru_chars.encode('UTF-8').hex()
Но, всё равно, пока не очень понятно, почему хексы кириллических символов находятся именно в таком порядке.
Вторая значимая для понимания статья открыла глаза на оптимизацию перебора кириллических символов.
Кодировки. Нужно больше кодировок
Каждый кириллический символ состоит из двух байт. К примеру, в UTF-8 кириллическая «А» кодируется как «D090», кириллическая «Б» кодируется как «D091». Занимательно. Если перекодировать все кириллические символы, то получается следующее:
Первый байт кириллического символа - это либо «D0», либо «D1». Именно поэтому, если указать в чарсете 1 значения D0D1, а в чарсете 2 все остальные уникальные байты, то мы сможем совершить перебор всех кириллических символов. Важная деталь: так как байтов два, то для перебора одного кириллического символа в hashcat нужно указывать последовательность <?1?2>. Для двух символов необходимо указать <?1?2?1?2> соответственно.
В итоге получится что-то похожее на:
Здесь мы для примера сгенерировали md5 хеш от кириллической строки «нет» и сбрутили этот хеш, используя созданный ранее чарсет.
Но наш брутфорс не является оптимизированным. Будут попадаться несуществующие варианты, например, шестнадцатеричная строка «D192».
Конечно, можно сделать так:
Выделяем первые байты в отдельные чарсеты. Но тут возникает проблема. Мы не можем сделать так: -5 ?1?2?3?4, так как максимальное число кастомных чарсетов равно четырем. На момент написания статьи мы не знаем как решить эту проблему.
Отлично! Теперь мы умеем брутить кириллические MD5 хеши.
Проблема #2. NTLM хеши
Разобрались с MD5 хешами - команда внешних пентестеров радуется. Внутрянщики пока не довольны - приведенный выше метод совершенно не работает для NTLM хешей.
Свежий Github Issue дал понимание о проведении атак на кириллические NTLM хеши. Для начала немного теории.
NTLM-хеш представляет из себя несоленый MD4 хеш. Поэтому для его перебора необходимо использовать режим -m 900 (raw md4) для кириллицы и или -m 1000 для латиницы.
NTLM-хеш формируется как MD4(UTF-16-LE(password)), то есть пароль кодируется в UTF16 Little Endian, а далее от полученный строки берется md4 hash.
Что же это значит? Значит именно то, что нам нужно проделать те же самые операции с кириллическими символами, но в кодировке UTF-16. Опять же используем python:
Также можем сделать обратное преобразование, срезав первые 2 байта:
К сведению читателей, кодировка UTF-16 состоит из целых 8 байт.
В UTF-16 кириллическая «А» кодируется как «1004», кириллическая «Б» кодируется как «1104». Если перекодировать все кириллические символы то получается следующее:
С учетом всего вышесказанного можем сформировать следующую команду хешката для перебора кириллических NTLM-хешей
# Перебор NTLM([<кир. символ> + цифры + "!@$**]{1})
# !!! Неоптимизированный алгоритм. Будут перебираться несуществующие символы, например 1004
time hashcat -O --hex-charset -m900 -a3 ../ntlm_allhashes.txt
-1 3031323334353637383910111213141501161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343551363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f2140242a
-2 0400 ?1?2
Вот такие пароли получилось сбрутить по маске из 6 символов:
Заключение
Несколько дней ресерча позволили научиться проводить атаки полного перебора на кириллические хеши, что в дальнейшем поможет в сборе статистики, формировании новых закономерностей и новым успехам при внутренних и внешних пентестах.
В качестве инструмента для перегона паролей из разных раскладок (ru <-> en) предлагаем наш небольшой форк инструмента crwg.
Существует множество тактик, техник, лайфхаков и просто хороших советов как правильно брутить пароли. За годы своей практики мы наработали свою базу знаний по проведению брутфорс атак. Закономерности в паролях прослеживаются от региона к региону, от компании к компании. Подробнее об этом в будущих статьях :P
VADemon
К опросу: использовал бы, если бы сайты не ограничивали алфавит. Вообще помимо кириллицы логичное продолжение - эмоджи в паролях. Что называется "удачи!" после такого расширения алфавита.
tbp2k5
Если функция хеширования плохая и хеши утекли то никакое расширение алфавита не спасет, а в противном случае и 8-символьный пароль (цифры/латиница/спецсимволы) будут подбирать тысячелетиями…
VADemon
Мне интересно, допустим соленый, но плохой хеш. Исключая криптографические уязвимости, будут чтоли всё бинарное пространство перебирать? Насколько быстрым должен быть перебор?
PS: А вообще, если использовать уникальные пароли, то можно специально нетривиальные, но подбираемо-простые ставить. Будешь знать, какой сервис утек (как альтернатива уникальным почтовым ящикам).