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

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

Если злоумышленник, клиент и сервер расположены в трех различных сегментах, причем трафик от клиента к серверу идет в обход злоумышленника, его задача усложняется. Но, на его счастье, локальные, равно как и глобальные, сети подвержены и другой «болезни» - отсутствию надежных механизмов идентификации отправителя. Это обстоятельство может использоваться злоумышленником:

  • для посылки пакетов от имени рабочей станции супервизора (такой уязвимости подвержены сети Novell NetWare);
  • либо для посылки пакетов от имени сервера, предписывающих жертве передать открытый незашифрованный пароль (атака, актуальная для операционных систем семейства Windows);
  • для фальсификации ответов от служебных узлов сети (например, DNS серверов).

Несмотря на кажущееся многообразие, подавляющее большинство алгоритмов идентификации отправителя работают по одному и тому же сценарию. Все они используют один или несколько счетчиков, изменяющихся по определенному сценарию с каждым посланным или принятым пакетом. Для отправки пакетов от чужого имени необходимо установить верные значения каждого из счетчиков, иначе получатель распознает обман. Но если значения счетчиков не абсолютно случайны, а их разрядность недостаточно велика, эта задача существенно упрощается. Скажем, в локальных сетях NetWare для идентификации пакетов используется один 8-разрядный счетчик, увеличивающийся при каждой итерации на единицу. В худшем случае злоумышленнику требуется послать всего лишь 256 пакетов, чтобы ввести получателя в заблуждение, а практически и того меньше. На этой уязвимости основан целый ряд атак, в том числе, и знаменитая «Голландская атака» [1].

В сетях, основанных на TCP/IP, для идентификации используются два 32-разрядных счетчика, что затрудняет фальсификацию отправителя, но не исключает ее [2]. Проблема усугубляется тем, что пользователи Internet зачастую предпочитают цифровым IP-адресам легко запоминаемые доменные имена. А поскольку служба DNS опирается на протокол UDP, не отягченный механизмами идентификации отправителя, это делает возможной фальсификацию ответов DNS-сервера. В результате, жертва вместо ожидаемого ресурса устанавливает соединение с узлом злоумышленника, «добровольно» передавая ему имя пользователя и пароль [3].

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

Развитие криптографии привело к появлению механизмов аутентификации, не чувствительных ни к перехвату трафика, ни к подделке адреса отправителя. Один из таких механизмов, «запрос-отклик», используется в операционных системах Microsoft, в том числе, и в Windows NT. Это ключевое звено в системе аутентификации, предъявляющее повышенные требования к качеству реализации. Интересно сравнить теоретическую модель механизма «запрос-отклик» с конкретной реализацией этого алгоритма. Рассуждения, содержащиеся в данной статье, в равной степени относятся как к Windows NT, так и к Windows 2000.

Теоретически, схема «запрос-отклик» при должном качестве реализации способна противостоять перехвату трафика и фальсификации узла-отправителя. Однако разработчики потенциально могут совершить ряд упущений:

  • функция шифрования допускает обращение или перебор всех ключей за разумное время;
  • генератор случайных чисел обладает плохим рассеиванием и значения «запрос» достаточно часто повторяются;
  • проверка на короткие или словарные пароли отсутствует;
  • «хранилище» эталонных паролей недостаточно надежно и допускает более быстрое вскрытие, чем подбор паролей.

При реализации алгоритма «запрос-отклик» программисты из Microsoft совершили все четыре типа упущений. В результате механизм аутентификации стал неустойчив как к перехвату трафика, так и к фальсификации адреса узла отправителя.

Шифрование и генерация паролей

Для аутентификации, установки соединений и передачи файлов в операционных системах семейства Windows используется протокол SMB (Server Message Block). Строго говоря, SMB — это не один протокол, а целое семейство диалектов, созданных в разное время для MS-DOS, OS/2, Unix, Windows 3.1, Windows 3.11 for Workgroups и Windows NT. За время своего существования протокол SMB обогатился тремя различными методами аутентификации: а) передача пароля в открытом виде; б) передача хеша пароля; в) обмен ключами по схеме «запрос-отклик».

В Windows встречаются две различные реализации алгоритма «запрос-отклик», отличающиеся друг от друга алгоритмом вычисления хеш-значения пароля, условно названные в этой статье как «NT-хеш» и «LM-хеш», разработанные для Windows NT и LAN Manager соответственно. LAN Manager изначально разрабатывался для OS/2, а позже был интегрирован в Windows 3.1 и Windows 3.11 for Workgroups. Спустя некоторое время «всплыли» ошибки, допущенные при его реализации, в результате которых вскрытие зашифрованного пароля совсем не требовало сколько-нибудь значительных вычислительных ресурсов и было вполне доступно рядовому злоумышленнику. Поэтому разработчикам NT пришлось выбирать другой алгоритм хеширования. Однако полный отказ от поддержки LM-хеша привел бы к невозможности выполнения сеанса аутентификации с рабочих станций, оснащенных Windows for Workgroups, что в руководстве Microsoft сочли неприемлемым.

В базе данных диспетчера учетных записей SAM (Security Account Manager) обычно хранятся оба хеш-значения - как LM-хеш, так и NT-хеш, поэтому клиент для аутентификации может посылать либо LM-хеш, либо NT-хеш, либо оба одновременно. Но если он вздумает поменять свой пароль из операционной системы, не поддерживающей NT-хеш, его значение в базе SAM аннулируется. Теперь для аутентификации клиент будет вынужден передавать LM-хеш, до тех пор, пока вновь не поменяет свой пароль из операционной системы, поддерживающей оба типа хеш-значений одновременно. Поэтому большинство клиентов одновременно посылают и LM-, и NT-хеш, даже если системе требуется лишь один. Так, в частности, в подразумеваемой конфигурации по умолчанию поступают операционные системы Windows 95, Windows 98 и Windows NT для соединения с другой NT. Но если в Windows NT использование LM-хеша легко запретить, установив пакет обновлений Service Pack 4, то для Windows 95/98, похоже, не существует никаких легальных способов блокирования посылки LM-хеша.

Увы, при разработке Service Pack 4 была допущена грубая ошибка. Если после отказа от использования LM-хеша, пользователь, меняя пароль, пошлет лишь LM-хеш, операционная система благополучно его «проглотит», но аннулирует обе записи в базе SAM. Нулевое же значение обоих хеш-значений интерпретируется процедурой аутентификации как отсутствие пароля, а это, в свою очередь, позволяет злоумышленнику проникнуть в систему.

При аутентификации по схеме «запрос-отклик» хеш никогда не передается по сети в открытом виде, поэтому злоумышленнику перехватить его невозможно. Клиент передает серверу «запрос», хеш, зашифрованный с помощью алгоритма DES значением пароля. Если предполагать алгоритм DES надежным, то, кажется, что вычислить хеш пароля никакой возможности нет, и остается действовать только методом перебора.

Поскольку хеш представляет собой 16-байтовую строку, то возможно всего 2128 комбинаций, и перебор потребует вычислительных мощностей, недоступных злоумышленнику. Даже для современных суперкомпьютеров эта задача слишком сложна, если, конечно, реализация алгоритма DES свободна от ошибок. А такие ошибки есть: функция DES трижды шифрует challenge («запрос»), используя в качестве ключей различные фрагменты хеш-значения пароля, чем значительно уменьшает количество попыток, требуемых для его подбора.

Согласно алгоритму шифрования к 16-байтовому хеш-значению дописываются пять нулей, образуя последовательность из 21 байта (16+5=21), обозначенную здесь как h1..21. Эта последовательность разрезается на три равных части по семь байт, обозначаемые как h1..7, h8..14, h15..21. Каждая их них используется в качестве ключа для шифровки «запроса», переданного сервером с помощью алгоритма DES. Полученный результат R отсылается на сервер. Весь процесс можно представить так:

1) DES(challenge) h1..7-> R1..8
2) DES(challenge) h8..14-> R9..16
3) DES(challenge) h15..21-> R17..24

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

Поскольку пять старших байт ключа h15..21 известны заранее (содержат нули, дописанные для расширения ключа до 21-байтовой строки), то для решения уравнения DES(challenge)-P15..21->R17..24 необходимо перебрать всего два байта. В худшем случае это потребует 216 операций, а в среднем вдвое меньше. Если же длина пароля составляла менее восьми символов, то h15..h21 всегда равны 0x04EE0000000000 и короткие пароли распознаются буквально с одного взгляда.

В свою очередь, это облегчает решение уравнения DES(0) P8..14->H9 ..16, поскольку H15..16=h15..16. Перебором всех возможных значений P8..14, всего за 1+k+k2+k3+k4+k5+k6+k7 операций можно найти всех «кандидатов» в пароли, которых будет не более чем (1+k+k2+k3+k4+k5+k6+k7)/216. Остается перебрать 28*(1+k+ k2+k3+k4+k5+k6+k7) /216 комбинаций, чтобы среди «кандидатов» в ключи уравнения DES(challenge) P8..14-> R9..16 найти единственный действительный ключ. Уравнение

DES(challenge) P1..7-> R1..8

решается перебором 1+k+k2+k3+ k4+k5+k6+k7 вариантов в худшем случае и (1+k+k2+k3+k4+k5+k6+ k7)/2 в среднем, поэтому сравнения значений DES(0) P1..7->H1..8;можно вести одновременно с DES(0) P8..14->H9..16 сократив количество требуемых операций вдвое.

Но сколько времени в худшем случае займет поиск пароля? Для этого необходимо знать величину k (количество допустимых символов) и скорость вычисления функции DES.

В пароль могу входить цифры от ?0? до ?9?, 26 заглавных букв латинского алфавита и все 32 спецсимвола, итого 68, следовательно, всего существует 680+681+682+683+684+685+686+687 = 6823331935125 или приблизительно 7 x 1012 комбинаций. Скорость вычисления функции DES в зависимости от производительности процессора и эффективности реализации варьируется от стотысячных (на младших моделях Pentium) до миллионных (Pentium III Xeon) долей секунды. В худшем случае поиск пароля потребует 216+2*(1+k+ k2+k3+k4+k5+k6+k7) операций, а в среднем и того меньше — 215+(1+k+k2+ k3+k4+k5+k6+k7).

Если перебирать все возможные пароли со скоростью 500 тыс. операций в секунду, то поиск займет в худшем случае (65536+ 2*6823331935125) / 500000 = 27293328 секунд или около 316 дней, а в среднем порядка пяти месяцев. Но если перебирать пароли, состоящие из одних латинских символов, то в худшем случае процесс закончится за 33412 секунд (около девяти часов).

Процесс перебора очень легко распараллелить, задействовав более одного компьютера. Группа злоумышленников, вооруженная десятком Pentium II способна гарантированно найти любой пароль менее чем за месяц.

А если учесть, что пользователи склонны выбирать не абсолютно случайные, а в той или иной степени осмысленные пароли, этот срок можно заметно сократить.

Хранение паролей в NT

Во всех наших предыдущих рассуждениях молчаливо предполагалось, что эталонный пароль пользователя, хранящийся на сервере, надежно защищен от посягательств злоумышленника и недоступен никому, кроме администратора. Но так ли это на самом деле?

По соображениям безопасности в NT пароли пользователей вообще нигде не хранятся. Вместо этого в базу данных SAM заносятся хеш-значения паролей. Сама база хранится в файле «%SystemRoot%SYSTEM32CONFIGsam», доступ к которому закрыт. Файл «sam» представляет собой ветвь реестра «HKEY_LOCAL_MACHINESECURITYSAM», права чтения которого предоставляются только администратору (и то по умолчанию они заблокированы). Функции API-интерфейса, манипулирующие с именами пользователя и хеш-значением, недоступны непривилегированным пользователям.

Тем временем резервная копия системы, хранящаяся в каталоге «%SystemRoot%Repair», доступна каждому члену группы Everyone. Резервная копия создается при установке (или переустановке) Windows NT, а также всякий раз при запуске утилиты «rdisk» с ключом «/s». Распаковав добытый файл командой «expand sam._ sam», злоумышленник сможет извлечь из него имена пользователей и хеш-значения паролей. Существуют и готовые программные реализации, например, утилита SAMDUMP Дмитрия Адрианова, которая входит в пакет L0phtcrack (см. www.l0pht.com).

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

А если используется автоматический вход в систему (а используется он удручающе часто и в Windows 2000 установлен по умолчанию), то в ветке реестра «HKEY_LOCAL_MACHINESoftwareMicrosoftWindows(NTCurrenetVersionWinlogon», по умолчанию доступной группе Everyone (т. е. всем пользователям), содержатся имя пользователя, вошедшего в систему, («DefaultUsername») и его пароль («DefaultPassword»). Если удаленные подключения разрешены, то всякий, зарегистрированный в системе пользователь, сможет просмотреть указанную ветвь реестра, со всеми отсюда вытекающими — для администратора — последствиями.

К тому же, в отличие от Unix, процедуры аутентификации в NT не использует ничего похожего на механизм привязки (salt), и если пароли двух пользователей случайным образом совпадут, то и их хеш-значения окажутся идентичны. Как показывает практика, в многопользовательской системе такое событие не редкость.

Хранение паролей в Windows 95/98

Большинство пользователей Windows 95/98 предпочитают не набирать сетевой пароль вручную, а используют возможность его сохранения в собственном профиле. Пароли на вход в сеть сохраняются в файлах PWL, причем имя пользователя совпадает с именем файла и пароли пользователя «KPNC» сохраняются в файле «KPNC.PWL» из каталога Windows. Содержимое файлов PWL файлов зашифровано производным значением от пароля, под которым пользователь входит в систему.

В Windows 95 алгоритм шифрования в общих чертах выглядит так: пароль, заданный при регистрации нового пользователя в системе, приводится к верхнему регистру и посредством хеш-функции сворачивается к двойному слову (32 бита), используемого в качестве ключа для генерации гаммы по алгоритму RC4. Полученной гаммой и зашифровывается содержимое файла PWL. При входе пользователя в систему, введенный им пароль аналогичным образом сворачивается к двойному слову, на основе которого генерируются гамма, использующаяся для расшифровки содержимого PWL. Среди прочих, содержащихся данных, в нем хранится имя пользователя. Если, в результате расшифровки оно совпадет с именем файла, то пароль считается истинным и наоборот. Таким образом, ни хеш-значение, ни сам пароль нигде не хранятся и при условии правильной реализации криптоалгоритма, расшифровать содержимое файла PWL было бы невозможно. На самом же деле, все происходит не совсем так.

Слишком короткая длина ключа (32 бита) позволяет злоумышленнику воспользоваться тривиальным перебором. Поскольку алгоритм RC4 достаточно прост и допускает эффективную реализацию, уже на младших моделях Pentium хорошо оптимизированная программа способна достичь скорости перебора от нескольких сотен тысяч ключей в секунду и выше. Поэтому, приблизительное время, за которое гарантированно удастся расшифровать файл PWL равно: 232/500000 = 8590 секунд — меньше двух с половиной часов.

Разработчиками были допущены и другие ошибки, позволяющие расшифровать файл, даже не прибегая к перебору. Так, алгоритм «свертки» представляет собой пример очень слабой хеш-функции. Плохое рассеяние порождает множество паролей-двойников — различных паролей, но дающих одинаковые ключи. А некоторые пароли в результате свертки обращаются в нуль, что равносильно отсутствию пароля вообще. Поэтому, представляет интерес рассмотреть алгоритм хеширования поближе. Он невероятно прост: пароль приводится к верхнему регистру, затем над каждым его символом (включая нуль, завершающий строку) выполняются следующие операции:

  • сложить значение ключа с очередным символом пароля;
  • выполнить циклический двоичный сдвиг ключа на 7 позиций влево.

Легко видеть, насколько слабо взаимное влияние соседних символов друг на друга. В самом деле, схематично этот алгоритм можно записать как: 27*sym1+27*sym2+27*sym3,.. где symn N-ый символ пароля. Для смежных символов из интервала 0 — 127 взаимное влияние друг на друга отсутствует полностью. В качественных хеш-функциях изменение одного бита исходной строки обычно изменяет до половины битов полученного результата. Рассматриваемый алгоритм к таковым, как видно, не принадлежит.

Приведенная программа демонстрирует одну из возможных реализаций этого алгоритма, рассчитывая «свертку» пароля, указанного в командной строке:

#include 
#include 
main (int argc, char **argv) {
	int a=0,key=0;
	if (argc<2)
		printf («USAGE: win95.hashe.exe
 MyGoodPassword
»);
else {
	_strupr(argv[1]);
	for (;a<(strlen(argv[1])+1);a++) {
		key+=(unsigned char) argv[1][a];
			_asm
			{
			ROL key,7
			}
		}
		printf(«%08X 
»,key);
	}
}

Если в качестве пароля задать «FFFFKKKKL», то хеш-функция возвратит нулевое значение. И подобных «пустых» паролей существует достаточно много.

Алгоритм шифрования так же реализован с изъянами. Одна и та же гамма используется несколько раз - как для шифровки имени пользователя, так и для шифровки ресурсов. Но имя пользователя заранее известно (оно совпадает с именем файла), поэтому, можно мгновенно восстановить первые 20 байт гаммы (20 символов отведено под имя пользователя). Причем, файлы PWL содержат множество избыточной (дублирующейся) и предсказуемой информации, поэтому существует возможность без всякого перебора вычислить и остаток гаммы.

Существует ряд программ, которые, используя описанную «дырку», извлекают из файлов PWL хранящуюся в них информацию, в том числе и пароль доступа в сеть.

В Windows 95 ORS2 механизм шифрования был усовершенствован. Вместо вращения бит для свертки пароля разработчики использовали алгоритм MD5, с помощью которого теперь вычисляются четыре двойных слова - хеш-значения имени и пароля. Поэтому, перебором хеш-значений расшифровать файлы PWL стало не быстрее, чем перебором исходных паролей. Простой подсчет показывает, что всего существует 2128 возможных хеш-значений, полный перебор которых потребует весьма длительного времени. При скорости 250000 комбинаций в секунду в худшем случае понадобится порядка 15753813283376780715896972566 дней.

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

Скорость же перебора паролей по сравнению с предыдущей версией Windows падает приблизительно в два раза, за счет использования более громоздких алгоритмов получения хеш-значений пароля и проверок его подлинности. Подробное описание потребовало бы много места и, поэтому, здесь не приводится. Вся необходимая информация может быть получена путем дизассемблирования файла MSPWL32.PWL.

Отличия Windows 98 от Windows 95 ORS 2 незначительны - снято ограничение на максимальную длину пароля, вот, пожалуй, и все. Однако простые пароли, выбираемые пользователями, по-прежнему позволяют вскрыть PWL за короткое время, хотя в целом защиту можно считать удовлетворительной.

Вместо морали

Вполне резонно закончить эту статью словами Кена Томсона: «Нельзя доверять программам, написанным не вами самими...». Иначе говоря, не стоит полагаться на рекламные акции и «чистосердечные» заверения разработчиков, отказывающихся предоставлять исходные тексты для анализа.

Литература

[1] Евгений Ильченко, «Черный ход в NetWare», LAN Magazine, № 6, 1997
[2] Bell Labs Computer Science Technical Report #117, February 25, 1985
[3] Илья Медведовский, «DNS под прицелом», LAN Magazine, № 5, 1997
[4] Брюс Шнайер, «Прикладная криптография», Издание второе. Глава 22, «Алгоритмы обмена ключами»
[5] Павел Семьянов, «Почему криптосистемы ненадежны?»

Об авторе

Крис Касперски — независимый автор. С ним можно связаться по электронной почте по адресу: Kpnc@aport.ru


Схема «запрос-отклик»

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

Существует множество различных алгоритмов обмена ключами по незащищенным каналам [4], но наибольшую популярность завоевала схема «запрос-отклик» (handshake), которая в общих чертах выглядит следующим образом. После успешной установки соединения сервер посылает клиенту случайную последовательность, именуемую «запрос пароля» (challenge). Клиент шифрует «запрос», используя свой пароль в качестве ключа и возвращает полученный результат серверу. Сервер, выполнив аналогичную операцию, сравнивает результат свой работы с «откликом» (response) клиента. Если они совпадают - хорошо, а нет - invalid user.

Важно понимать, что «запрос» шифруется паролем, а не наоборот. В противном случае, в такой операции не было бы никакого смысла - злоумышленник, перехватив запрос сервера и отклик клиента, сумел бы расшифровать пароль. Иначе говоря, если f - функция шифрования, key - ключ, а value - шифруемые данные, то: f(value) —key-> crypt, а F(crypt) —key-> value, где F - функция дешифрования. Если key - «запрос», а value -пароль пользователя, то злоумышленник, перехватив «запрос» и crypt, сможет восстановить искомый пароль. Но как все изменится, если key - это пароль, а value - «запрос»! Для того чтобы из crypt извлечь «запрос» (да и зачем его извлекать, когда он и без того известен?), необходимо знать ключ шифрования, а его как раз и требуется найти. При этом функция шифрования специально подобрана таким образом, чтобы, зная исходный и зашифрованный текст, вычислить ключ было невозможно иначе, как с помощью прямого перебора.

Отклик клиента не способен расшифровать никто, даже сам сервер. Но это и не нужно: функция необратимого шифрования позволяет установить идентичность аргументов, но не позволяет по значению функции узнать сами аргументы. Строго говоря, это верно только в том случае, если каждому значению функции f() соответствует только один аргумент. В противном случае возможна такая ситуация, когда x ≠ x1, но f(x) = f (x1).


Предсказуемый «запрос»

Значение «запроса», посылаемое клиенту сервером, должно быть не только случайным, но и не повторяющимся и непредсказуемым. Очевидно, что использование временной метки, не отвечает последнему условию и проводит к потенциальной уязвимости системы [5].

В общем случае временная метка равна числу так называемых «тиков», прошедших с момента начала работы сервера (или с момента включения питания компьютера). При хорошем качестве соединения с сервером все временные метки с небольшим разбросом лежат вдоль прямой f(t) = k*t+c, где k и с - некоторые коэффициенты. Подключаясь к серверу через определенные промежутки времени, злоумышленник сможет вычислить коэффициенты k и c, а отсюда, с определенной вероятностью сумеет предсказать «запрос» в любой момент времени t. В этом случае атака может выглядеть приблизительно так: дождавшись начала сеанса аутентификации, злоумышленник от имени сервера посылает жертве предсказанный им «запрос», которым жертва зашифровывает свой пароль, возвращая обратно полученный результат. Разумеется, злоумышленник не в состоянии восстановить искомый пароль, но это ему и не нужно! Последовательными запросами на аутентификацию он добивается получения предсказанной им временной метки и возвращает зашифрованный ею пароль пользователя. Экспериментально установлено, что в большинстве существующих систем разброс выдачи временных меток находится в интервале от одного до нескольких десятков «тиков», следовательно, такие системы могут быть взломаны относительно быстро.

В некоторых случаях, разработчики обрабатывают временные метки «секретной функцией» (как правило, базирующейся на логической операции XOR), скрывая линейную зависимость выдачи «запроса» и создавая впечатление генерации абсолютно случайных значений. Но на самом деле «секретная функция» никакой тайны для атакующего не представляет. Ее можно выявить путем банального дизассемблирования, а потом либо обратить, либо вскрыть методом перебора, если эта функция - односторонняя, причем перебирать придется недолго, поскольку две соседних временных метки в большинстве случаев отстоят друг от друга не более чем на 100-500 «тиков».

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


Аутентификация по протоколу SMB

В общих чертах процесс аутентификации по протоколу SMB выглядит следующим образом. Клиент, установив сеанс по протоколу NetBIOS, уведомляет сервер о своем желании вступить с ним в связь, посылая сообщение SMB_CON_NEGOTIATE, в котором перечисляются все известные клиенту диалекты SMB. Как правило, сервер, выбрав самый современный из доступных ему и клиенту протоколов, возвращает либо случайный 8-байтовый «запрос», либо просьбу передать пароль в открытом виде. Клиент открытым текстом отвечает сообщением SMB_SESSION_SETUP_ANDX, содержащим пользовательское имя и открытый пароль или «запрос», зашифрованный хеш-значением пароля.

Если злоумышленник сумеет «прикинуться» сервером, он сможет послать клиенту сообщение SMB_COM_NEGOTIATE с флагом, предписывающим передавать пароль в незашифрованном виде. И клиент, поверив, что сервер не поддерживает зашифрованные пароли, выполнит его требование. Задачу злоумышленника значительно облегчает тот факт, что прикладной SMB-протокол реализуется поверх транспортных протоколов, совместимых с интерфейсом NetBIOS, а, устанавливая соединение по протоколу NetBIOS, сервер проверяет имя, сообщенное ему клиентом, а не его сетевой адрес.

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

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


Алгоритм получения LM-хеша

Введенный пользователем пароль превращается в строку фиксированной длины, равной четырнадцати символам, короткие пароли расширяются добавлением нулевых элементов, а длинные усекаются. Все символы из множества ?a?-?z? переводятся в верхний регистр, и, полученная в результате этой операции, строка расчленяется на две независимые половинки, состоящие из семи символов. Каждая из них играет роль ключа для шифровки последовательности нулей алгоритмом DES. Затем, полученные строки дописываются друг к другу, образуя 16-байтовый LM-хеш.

Независимое хеширование половинок пароля, в 1 000 000 000 миллионов раз уменьшает количество попыток, требующихся для его перебора (а вовсе не в два раза, как это может показаться на первый взгляд). Действительно, пусть f - некая хеш-функция, а x1..7y8..14 - введенный пользователем пароль. Тогда LM-хеш будет равен f(x)+264*f(y), поскольку область допустимых значений функции f лежит в интервале целых неотрицательных чисел, не превышающих 264, то поиск подходящего пароля заключается в решении двух уравнений (где P - искомый пароль, а H — значение LM-хеша). Строка нулей шифруется алгоритмом DES, а роль ключа играют семь символов пароля:

1) DES(0) P1..7-> =H1..8;
2) DES(0) P8..14-> =H9..16;

Какие существуют способы решения данных уравнений? Алгоритм DES специально разрабатывался так, чтобы, зная исходный (в данном случае строка нулей) и зашифрованный (в данном случае 8 байт хеш-значения) тексты, злоумышленник не мог эффективно вычислить ключ шифрования (искомый пароль). Однако алгоритм DES нестоек к перебору и не требует больших объемов вычислений.

В среднем злоумышленнику потребуется 1+k+k2+k3+k4+k5+k6+k7 операций (вычисление функции DES и сравнение полученного результата с исходным хеш-значением) для подбора пароля, где k максимально допустимое количество символов, использующихся для составления пароля. Это намного меньше ожидаемого количества операций, необходимых для подбора 14-символьного пароля:

1+k+k2+k3+k4+k5+k6+k7 << (1+k+k2+k3+k4+k5+k6+k7++k8+k9+k10+k11+k12+k13+k14)*1/2

Таким образом, пароль, состоящий из восеми и более символов, окажется ничуть не сложнее пароля, состоящего всего из семи символов — фактически система запрашивает два 7-символьных пароля и обрабатывает их независимо друг от друга. Стоит заметить, что даже старые версии ОС Unix ограничивали пароль восемью символами, различая при этом заглавные и строчные буквы.

Очевидно, что пароли, состоящие менее чем из восьми символов, легко распознать, поэтому для пароля меньше четырнадцати символов система расширяет его нулями до требуемой длины. Пусть пользователь ввел пароль из семи или менее символов, тогда P8..14 = 0, а DES(0) P8..14-> 0xAAD3B435B5140EE. Однако, это константа. В результате, если старшие восемь байт LM-хеша равны 0xAAD3B435B5140EE, то исходный пароль состоит из семи или менее символов.

Если оптимизировать данный алгоритм, то скорость перебора можно увеличить вдвое. В самом деле, совершенно ни к чему дважды вычислять значение функции DES в уравнениях 1) и 2), поскольку области определения обеих функций совпадают. Следовательно, для каждого P достаточно один раз вычислить значение DES(0) и поочередно сравнить его с H1..8 и с H9..16. Если пренебречь временем, затраченным на сравнение значения функции с H1..8 и H9..16 (а их для ускорения можно поместить в один 16-разрядный регистр), то скорость перебора возрастет вдвое.

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


Устройство генератора паролей

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

Существует несколько алгоритмов генерации паролей. Например, можно составить список наиболее распространенных паролей, а затем извлекать слова из списка одно за другим. Это так называемый словарный перебор. Его достоинство заключается в том, что удачно подобранный словарь способен позволить найти пароль за сравнительно небольшое количество попыток. Но что означает «удачно подобранный»? Пользователи склонны выбирать короткие запоминающиеся пароли, часто представляющие собой собственные имена, торговые марки, географические названия и слова ненормативной лексики (это уж как у кого голова работает). Все вместе взятые они с легкостью вмещаются в 5-10 тыс. вариантов, и могут быть перебраны за очень короткое время. (Современные домашние компьютеры способны испытать его буквально за секунду).

Словарный перебор срабатывает часто, но не всегда. Привилегированные пользователи (например, системные администраторы) склонны выбирать бессмысленные пароли наподобие «acsW%9*m$», надежно защищая себя от словарной атаки.

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

Поэтому последовательный перебор целесообразен только в поиске коротких паролей или паролей, состоящих из небольшого количества символов. Некоторые системы аутентификации ограничивают максимальную длину пароля, позволяя гарантированной найти его путем перебора за приемлемое время. Случается, что такое ограничение возникает неявно в результате программной ошибки реализации (например, фактическая длина пароля LAN Manager составляет семь символов, поскольку две половинки 14-символьной строки обрабатываются независимо друг от друга).

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

Какой поиск пароля дает наилучший результат - последовательный или хаотичный? Вопрос этот нелеп в своей постановке, если не оговаривается, откуда и как возник искомый пароль. Если принять, что он был выбран случайно, то последовательной перебор вариантов будет ничем не хуже, но и не лучше, хаотичного поиска, поскольку никаких сведений (ни явных, ни предполагаемых) об искомом паролей нет, и одному методу поиска нельзя отдать предпочтение перед другим.

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

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

Поэтому в большинстве случаев используется простой линейный поиск, заключающийся в последовательном переборе возможных паролей один за другим. Ниже приведен один из простейших алгоритмов перебора, получивший название «алгоритм счетчика».

#include 
main()
{
	char pswd[10];
	int p=0;
	pswd[0]=?!?;
	pswd[1]=0;

	while(1) {
		while((++pswd[p])>?z?) {
			pswd[p]=?!?;
			p++;
			if (!pswd[p]) {
				pswd[p]=? ?;
				pswd[p+1]=0;
			} 
		}
	p=0;
	printf(«%s
»,&pswd[0]);
	}
}

Суть алгоритма заключается в следующем: первый слева символ пароля увеличиваться до тех пор, пока не превысит максимально допустимое значение. Когда такое произойдет, он «обнуляется», принимая минимально допустимое значение, а символ, стоящий справа от него увеличивается на единицу. В упрощенном виде алгоритм записывается в одной строке на языке Си:

while ((++pswd[p])>MAX_VAL)
 pswd[p++]=MIN_VAL;p=0;

Такая конструкция скрывает рекурсию и тот же алгоритм в рекурсивной форме записи может выглядеть так:

void GetNextPasswd
	(char pswd, int p)
{	pswd[p]++;
	if (!(pswd[p]>MAX_VAL)) return;
	pswd[p]=MIN_VAL;
	Count(pswd,++p);
}

Существует готовая программная реализация описанной атаки, воплощенная в пакете L0PhtCrack 2.5, которая занимается подбором LM- и NT-хешей [4]. Утверждается, что с помощью этой утилиты на Pentium II/300 МГц более 90% паролей удается найти в течение 48 часов, а 18% паролей вскрываются менее чем за 10 минут.

Приведенные цифры интересны сами по себе. При условии криптостойкости алгоритма DES методом грубой силы необходимо перебрать по крайней мере 1+k+k2+k3+k4+k5+k6+k7 комбинаций. И если бы утилита из L0PhtCrack 2.5 действовала тривиальным перебором, для обеспечения заявленной скорости перебора пришлось бы совершать (1+k+k2+k3+k4+k5+k6+k7)/(48*60*60) операций в секунду, то есть 6823331935125/172800 = 39486874 - почти 40 млн. вычислений функции DES каждую секунду. Ни один из процессоров Intel не обеспечивает пока такой производительности.

На самом деле, L0phtCrack 2.5 комбинирует «лобовую» атаку с перебором по словарю. Этим и объясняется полученный результат. Однако словарная атака не гарантирует, что пароль будет найден, и для нахождения оставшихся 10% паролей L0phtCrack тратит больше 48 часов. Поэтому, реальное время, требуемое для нахождения пароля, в значительной мере определяется его наличием (отсутствием) в словаре, а вовсе не скоростью перебора.

Существует возможность задать в качестве одного из символов пароля знак перевода каретки. Его можно ввести с вспомогательной цифровой клавиатуры, удерживая клавишу Alt («Alt+?0 1 3?»). Большинство переборщиков паролей не учитывают такой тонкости и не включают этот символ в список допустимых символов. Поэтому, в какой-то степени это затрудняет злоумышленнику проникновение в систему. Впрочем, весьма вероятно, что уже следующие версии переборщиков исправят свою ошибку и смогут корректно обрабатывать символ перевода строки.