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

В данной статье мы рассмотрим как в теории и на практике работает NTLM и какие в него включены меры безопасности, а в следующей статье (с выходом которой я постараюсь не временить) будут рассмотрены вариации атаки NTLM-Relay, которые помогут закрепить понимание данного протокола и составить более целостную картину.

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⚠WARNING⚠

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

Общая идея

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

  • Сервер отправляет пользователю случайную строку.

  • Пользователь, получив эту строку, вычисляет хэш от этой строки, используя в качестве ключа свой пароль, и отправляет её обратно серверу.

  • Сервер, также вычисляет хэш с помощью пароля пользователя, копия которого у него хранится, и сравнивает её с полученной от пользователя.

  • Если обе строки совпадают, то пользователь аутентифицирован, иначе – неаутентифицирован.

Рисунок 1 – Упрощенная схема работы NTLM
Рисунок 1 – Упрощенная схема работы NTLM

Существует две версии NTLM: NTLMv1 и NTLMv2. Они отличаются лишь способом подсчета хэша и механизмами безопасности. NTLMv1 является небезопасным протоколом, недостатки которого закрывает NTLMv2. Сначала мы разберем актуальную версию NTLMv2, а затем ради интереса посмотрим на NTLMv1.

NTLM сам по себе не является самостоятельным протоколом: он не инкапсулируется в полезную нагрузку нижележащего протокола, не имеет своего места в сетевом стеке. Напротив, NTLM-сообщения включаются в другие протколы, которым требуется услуга аутентификации. Такими протоколами являются, например, SMB, LDAP и HTTP. Данные протоколы сами определяют, как и куда они будут помещать NTLM-сообщения; HTTP, к примеру, использует для этого заголовок Authorization.

NTLMSSP

NTLM реализуется с помощью API, которое возвращает NTLM-сообщения в виде массива байтов. Модуль проткола, использующего NTLM, просто вызывает это API, передавая в него нужные параметры, и полученный массив байт просто кладет в определенное поле своего протокола. Далее, код протокола на стороне извлекает NTLM-сообщение и "скармливает" его тому же API.

В Windows NTLM реализован с помощью Security Support Provider Interface (SSPI) – это набор SSP, которые предназначены для работы с протоколами аутентификации, например, NTLM или Kerberos. SSP – это отдельный набор DLL-файлов, которые используются для реализации какого-то конкретного протокола. В Windows реализацией NTLM является NTLMSSP. NTLMSSP использует три функции: AcquireCredentialsHandle, InitializeSecurityContext и AcceptSecurityContext.

Функция AcquireCredentialsHandle используется для того, чтобы получить хэндл (указатель на место в памяти, где начинаются какие-то данные. Тоже самое что и дескриптор в Linux) на учетные данные пользователя.

SECURITY_STATUS SEC_Entry AcquireCredentialsHandle(
   In  SEC_CHAR       pszPrincipal,
   In  SEC_CHAR       pszPackage,
   In  ULONG          fCredentialUse,
   In  PLUID          pvLogonID,
   In  PVOID          pAuthData,
   In  SEC_GET_KEY_FN pGetKeyFn,
   In  PVOID          pvGetKeyArgument,
   Out PCredHandle    phCredential,
   Out PTimeStamp     ptsExpiry
 );

Функция InitializeSecurityContext(), получая хэндл на учетные данные пользователя, формирует контекст (security context). Контекст – это объект, в котором хранятся данные, необходимые для поддержания сессии, например, идентификат��ры сторон, ключи сессии, выбранные алгоритмы/шифры, флаги защиты. Данная функция возвращает NEGOTIATE-токен (тот самый массив байт, который представляет NTLM-сообщение), который потом нужно будет передать серверу.

SECURITY_STATUS SEC_Entry InitializeSecurityContext(
   Inopt_    PCredHandle    phCredential,
   Inopt_    PCtxtHandle    phContext,
   In        SEC_CHAR       *pszTargetName,
   In        ULONG          fContextReq,
   In        ULONG          Reserved1,
   In        ULONG          TargetDataRep,
   Inopt_    PSecBufferDesc pInput,
   In        ULONG          Reserved2,
   Inoutopt_ PCtxtHandle    phNewContext,
   Inoutopt_ PSecBufferDesc pOutput,
   Out       PULONG         pfContextAttr,
   Outopt_   PTimeStamp     ptsExpiry
 );

Функция AcceptSecurityContext на стороне сервера принимает токен, отправленный пользователем, и формирует контекст на стороне сервера. Данная функция возвращает CHALLENGE-токен, в котором содержится challenge, и который надо передать пользователю.

KSECDDDECLSPEC SECURITY_STATUS SEC_ENTRY AcceptSecurityContext(
   [in, optional]      PCredHandle    phCredential,
   [in, optional]      PCtxtHandle    phContext,
   [in, optional]      PSecBufferDesc pInput,
   [in]                unsigned long  fContextReq,
   [in]                unsigned long  TargetDataRep,
   [in, out, optional] PCtxtHandle    phNewContext,
   [in, out, optional] PSecBufferDesc pOutput,
   [out]               unsigned long  *pfContextAttr,
   [out, optional]     PTimeStamp     ptsExpiry
 );

Далее, на стороне пользователя опять вызывается функция InitializeSecurityContext, в которую передается CHALLENGE-токен сервера. Функция формирует AUTH-токен, в котором содержится ответ на challenge.

Сервер, получив ответный токен, вновь вызывает функцию AcceptSecurityContext и передает в нее этот токен. Если все успешно, то для пользователя создается сессия.

Рисунок 2 – Процесс работы NTLMSSP в WIndows
Рисунок 2 – Процесс работы NTLMSSP в WIndows

Продемонстрируем работу NTLMSSP с помощью bash-скрипта, который напрямую взаимодействует с API.

Рисунок 3 – Получение NEGOTIATE-токена
Рисунок 3 – Получение NEGOTIATE-токена

Команда New-LsaCredentialHandle вызывает функцию AcquireCredentialsHandle, которая возвращает нам хэндл на учетные данные пользователя. Команде New-LsaClientContext передается хэндл пользователя, данная команда вызывает функцию InitializeSecurityContext, которая создает контекст и возвращает нам токен. Далее на экран выводится содержимое этого токена. В нем можно наблюдать название токена: “NTLM NEGOTIATE”, а также флаги, содержащие информацию о том, что поддерживает и хочет использовать клиент.

  • Unicode говорит о поддержке Unicode.

  • Oem говорит о поддержке байтовой строки.

  • AlwaysSign говорит о том, что будет сгенерирован сессионный ключ.

  • ExtendedSessionSecurity говорит просьбе использовать более безопасные алгоритмы формирования ключей и NetNTLMv1-хэша (если используется NTLMv1).

  • RequestTarget говорит о просьбе предоставить информацию о сервере.

  • Version говорит о том, что пользователь отправил информацию о версии операционной системы и версии протокола NTLM.

  • Key128Bit и Key56Bit говорят о том, что пользователь может использовать 128-битный и 56-битный ключ соответственно.

Полученный таким образом токен должен быть передан серверу.

Рисунок 3 – Формирование CHALLENGE-токена
Рисунок 3 – Формирование CHALLENGE-токена

В данном коде на стороне сервера мы предоставляем функции AcceptSecurityContext NEGOTIATE-токен посредством команды Update-LSAServerContext. В качестве возвращаемого результата мы получаем CHALLENGE-токен. В нем мы видим флаги, которые сервер выбрал из предлагаемых клиентом (заметим, что сервер убрал флаг Oem и оставил Unicode). Также мы видим информацию о сервере (Target Info), которую запросил клиент. Поле Challenge содержит ту самую строку, которую для имитовставки далее будет использовать клиент.

Рисунок 4 – Формирование AUTHENTICATE-токена
Рисунок 4 – Формирование AUTHENTICATE-токена

Команда Update-LsaClientContext вызывает InitializeSecurityContext, куда передается CHALLENGE-токен. На выходе мы получаем AUTHENTICATE-токен, который содержит среди всего прочего ответ на challenge сервера. Далее, данный токен должен быть передан серверу.

Ответ на challenge сервера называется NetNTLMv2-хэшем и строится следующим образом:

Сначала мы высчитываем NTLM-хэш, который является MD4-хэшем пароля пользователя MD4(UNICODE(Passwd)). Затем мы высчитыв��ем ResponseKeyNT, который равняется выводу NTOWFv2 (NT one-way function version 2). Для этого мы конкатенируем имя пользователя и домен в высоком регистре и считаем MD5-HMAC, используя NTLM-хэш в качестве ключа.

Define NTOWFv2(Passwd, User, UserDom) as HMAC_MD5(
MD4(UNICODE(Passwd)),
UNICODE(ConcatenationOf(Uppercase(User),UserDom)))

Set ResponseKeyNT to NTOWFv2(Passwd, User, UserDom)

Для того чтобы сформировать окончательный ответ для севера, пользователь конкатенирует кучу информации, куда включается challenge сервера, временная метка клиента, challenge клиента (случайное 8-байтовое число, сгенерированное клиентом), а также имя сервера. Responserversion и HiResponserversion, согласно акутальной документации, равны 1. Z(m) – m нулей. Данная строка называется temp.

Set temp to ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,
Responserversion, HiResponserversion,
Z(6), Time, ClientChallenge, Z(4), ServerName, Z(4))

Далее, клиент конкатенирует challenge сервера с temp и берет от этого HMAC_MD5, используя в качестве ключа ResponseKeyNT. Данная строка называется NTProofString. Серверу клиент отправляет NTProofString и temp.

Set NTProofStr to HMAC_MD5(ResponseKeyNT,
ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp)

Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp)

Передав AUTHENTICATE-токен серверу, посредством вызова функции AcceptSecurityContext командой Update-LsaServerContext, сервер проверяет ответ на challenge (секреты пользователей хранятся на сервере), и, если пользователь аутентифицирован, создает для него logon-сессию.

Рисунок 7 – Успешная аутентификация пользователя
Рисунок 7 – Успешная аутентификация пользователя

NTLM внутри SMB

Теперь, когда мы познакомились с NTLMSSP, посмотрим на дамп SMB-трафика.

Как видно из захваченного пакета, NTLM-сообщение находится в поле Security Blob. В начале мы видим обязательную сигнатуру NTLMSSP, которая знаменует всякое NTLM-сообщение, затем тип сообщения NTLMSSP_NEGOTIATE, а в конце взведенные флаги.

Рисунок 8 – Negotiate-сообщение
Рисунок 8 – Negotiate-сообщение

Далее мы видим Challenge-сообщение в котором содержится сам challenge, для которого мы будем считать имитовставку, флаги, которые выбрал сервер, а также информацию о сервере, включающую:

  • NetBIOS-имя домена/компьютера

  • DNS-имя домена/компьютера

  • Текущую отметку времени

Рисунок 9 – Challenge-сообщение
Рисунок 9 – Challenge-сообщение

В ответном сообщении мы видим наш NetNTLMv2-хэш, который содержится в поле NTLMv2 Response. Обратим внимание, что первые байты отводятся под NtProofString, а далее приводится вся инфомрация, используемая для формирования NtProofString.

Рисунок 9 – Authenticate-сообщение
Рисунок 9 – Authenticate-сообщение

Внимательный читатель заметит незнакомое поле Lan Manager Response. Данное поле нужно для pass-through authentication. Если сервер сам не аутентифицирует пользователя, то он может отправить значение в Lan Manager Response тому, кто ей занимается (например, контроллеру домена), а затем, получив ответ сервера, уже решать – предоставлять услуги клиенту, или слать сообщение с ошибкой. Ныне это поле, как видно, не используется. Считается это следующим образом:

Set LmChallengeResponse to ConcatenationOf(HMAC_MD5(ResponseKeyLM,
ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge)),
ClientChallenge 

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

NTLMv1

NTLMv1 отличается от NTLMv2 только способом формирования NetNTLM-хэша, ключей шифрования (о них позже) и механизмов безопасности. Все остальные поля NTLM-сообщений и процесс обмены сообщениями одинаковы как для первой, так и для второй версии. Версии протокола никак не согласуются самим NTLM, о версии NTLM должны дого��ариваться протоколы, пользующиеся услугами NTLM.

Для начала мы высчитываем ResponseKeyLM.

LMOWFv1(Passwd, User, UserDom) as 

ConcatenationOf(DES(UpperCase(Passwd)[0..6],"KGS!@#$%"),
DES(UpperCase(Passwd)[7..13],"KGS!@#$%"))

Set ResponseKeyLM to LMOWFv1(Passwd, User, UserDom)

Обратите внимание, что берутся первые ЧЕТЫРНАДЦАТЬ!1!!1!! символов пароля, и при всем этом они переводятся В ВЫСОКИЙ РЕГИСТР!11!! Думаю, не стоит объяснять: почему использование такого алогритма подсчета ResponseKeyLM очень небезопасно.

Затем, если активен флаг Extended Session Security, ответ строится следующим образом:

Set NtChallengeResponse to DESL(ResponseKeyNT,
MD5(ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,
ClientChallenge))[0..7])

Set LmChallengeResponse to ConcatenationOf(ClientChallenge,Z(16))

Если флаг ESS не установлен, то действуем следующим образом:

Set NtChallengeResponse to DESL(ResponseKeyNT,CHALLENGE_MESSAGE.ServerChallenge)
 
If (NoLMResponseNTLMv1 is TRUE)
Set LmChallengeResponse to NtChallengeResponse

Else
Set LmChallengeResponse to DESL(ResponseKeyLM,CHALLENGE_MESSAGE.ServerChallenge)

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

Рассмотрим захваченное NTLM-authenticate сообщение.

Рисунок 10 – Authenticate-сообщение при NTLMv1
Рисунок 10 – Authenticate-сообщение при NTLMv1

Как можно видеть из дампа, authenticate-сообщение ничем кроме NTLM-Response'а не отличается (остальные сообщения тоже. Можете проверить самостоятельно).

В данном случае Lan Manager Response уже используется не для pass-through authentication, а для совместимости с доисторическими серваками, которые функционуруют на версиях предшествующих Windows NT и используют суперстарую версию протокола, которая называется LM (который потом стал NTLM, типа, после Windows NT, ю ноу), и где это поле непосредственно использовалось для аутентификации.

NTLM-Relay

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

  • Жертва отправляет Negotiate-запрос хакеру, хакер переправляет его серверу.

  • Хакер получает Chellahe-ответ от сервера и отправляет его жертве.

  • Жертва формирует NetNTLM-hash с помощью своего секрета и отправляет его хакеру.

  • Хакер отправляет полученное Authenticate-сообщение серверу.

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

Рисунок 11 – NTLM-Relay
Рисунок 11 – NTLM-Relay

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

Рисунок 12 – NTLM-Relay со стороны сервера
Рисунок 12 – NTLM-Relay со стороны сервера

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

К счастью, или же сожалению тех, кто уже на радостях собрался получать доступ к админским шарам, в современных реализациях NTLM существуют механизмы, которые помогают избежать атаки NTLM-Relay.

Signing and Sealing

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

Идея следующая: клиент формирует ключ случайным образом, шифрует его, используя свой секрет, затем этот ключ помещает в Authenticate-сообщение. Затем сервер расшифровывает его, и с помощью него как клиент, так сервер могут подписывать и шифровать сообщения друг для друга. Можете вернуться к рисункам с захваченными Authenticate-сообщениями и найти там Session Key – это и есть зашифрованный ключ.

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

Рисунок 13 – Защита от NTLM-Relay засчет подписи
Рисунок 13 – Защита от NTLM-Relay засчет подписи

Для использования подписи/(шфирования и подписи) должны быть установлены флаги NTLMSSP_NEGOTIATE_SIGN/NTLMSSP_NEGOTIATE_SEAL.

Формирование ключа

Рассмотрим то, как формируются ключи подписи и шифрования для NTLMv2. Для NTLMv1 процесс отличается, и желающие могут заглянуть в документацию к протоколу.

Для начала клиент формирует случайный 16-байтовый сессионный ключ ExprotedSessionKey, затем с помощью своего секрета формирует KeyExchangeKey, с помощью которого шифрует ExprotedSessionKey и отправляет его в Authenticate-сообщении.

Set ExportedSessionKey to NONCE(16)

Set KeyExchangeKey to HMAC_MD5(ResponseKeyNT, NTProofStr)

Set AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey to \

RC4(KeyExchangeKey, ExportedSessionKey)

Для формирования ключа подписи и шифрования берется MD5-хэш от конкатенации ExportedSessionKey и константы (для сервера и клиента, подписи и шифрования они разные).

Set SignKey/SealKey to MD5(ConcatenationOf(ExportedSessionKey,"signing/sealing constant")

Алгоритм подписи и шифрования сообщений

Для подписи сообщений мы считаем имитовставку от конкатенации порядкого номера сообщения в сессии и самого сообщения, которое нам надо подписать. SeqNum - это порядковый номер сообщения, который обычно при начале сессии равняется нулю, а затем инкриментируется. Затем от имитовставки берутся первые 16 байт и шифруются; они и являются подписью сообщения (что, честно говоря, идет в разрез с тем, что обычно называют подписью, ведь тут мы имеем дело с имитозащитой, а не подписью... Но у Microsoft, видимо, свои взгляды на криптографию).

Set Signature to RC4(ExportedSessionKey,
HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7])

Set SeqNum to SeqNum + 1

Если во время аутентификации был установлен флаг NTLMSSP_NEGOTIATE_SEAL, то вместе будет использовать как шифрование, так и подпись. Шифрование представляет собой просто применение алгороитма RC4.

Set Seal to RC4(SigningKey,Message)

Если протоколу, который пользуется услугами NTLM достаточно просто получить симметричный ключ, а услуги по шифрованию и подписи его не интересуют, достаточно включить флаг NTLMSSP_NEGOTIATE_ALWAYS_SIGN.

Подпись в SMB

Подпись сообщений сообщений это хорошо, но её ведь нужно еще согласовать. В SMB присутствует 3 состояния для подписи: Disabled, Enabled и Required. О них заявляют участники переговоров во время выбора версии SMB протокола еще до начала аутентификации.

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

Рисунок 15 – Подпись SMB в зависимости от флагов
Рисунок 15 – Подпись SMB в зависимости от флагов

Из приведенной выше таблицы мы можем сделать несколько важных выводов:

  • Состояние Disabled отсутствует у SMBv2, что делает его предпочтительнее чем SMBv1

  • По умолчанию контроллер домена требует использование подписи

  • Если сервер не является контроллером домена, то по умолчанию сообщения не подписываются

На следующем рисунке мы можем видеть как выглядит подпись внутри SMB.

Рисунок 16 – Подпись внутри SMB-сообщения
Рисунок 16 – Подпись внутри SMB-сообщения

Стоит отметить, что в SMB подпись расчитывается своим образом без использования услуг NTLMSSP. NTLM помогает SMB только обменом ключа подписи.

Service Binding

Данный механизм защиты предельно прост: клиент включает в NTLMv2 Response информацию о том, с кем он хочет взаимодействовать. Сервер проверяет эту информацию, и если она не протворечит идентичности сервера, то он аутентифицирует клиента, иначе – нет.

Имя, с которым пользователь взаимодействует он включает в атрибут Target Name, которое используется для получения NTProofString. Даже если злоумышленник изменит данный атрибут, то NTProofString, посчитанный сервером не будет совпадать с переданным.

Рисунок 17 – Service Binding
Рисунок 17 – Service Binding

Channel Binding

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

Рисунок 18 – Channel Binding
Рисунок 18 – Channel Binding

Можно видеть, что на 16-ом рисунке Channel Bindings пуст, так как там TLS не использовался.

Для совершения атаки тать построит два туннеля:
1. С жертвой, посылая ей свой сертификат.
2. С сервером, принимая его сертификат.

В таком случае, сервер будет ждать, что в Channel Binding будет находится его сертификат, и отклонит AUTH-сообщение, так как там будет находистя сертификат злоумышленника.

Рисунок 19 – Демонстрация работы Channel Binding для предупреждения атаки
Рисунок 19 – Демонстрация работы Channel Binding для предупреждения атаки

Важно отметить, что Target Name и Channel Binding не поддерживается в версииNTLMv1, также в Windows NT, Windows 2000, Windows XP, Windows Server 2003, Windows Vista и Windows Server 2008.

Хм🤔, а поч просто не заменить, что channel binding, что target name на те, которые примет сервак?

MIC

Для того, чтобы гарантировать, что в процессе обмена сообщениями ничего не было изменено: никакие лишние флаги не были взведены, никакие флаги не были обращены в ноль, а также, что важно channel binding и target name не были изменены, используется Message Integrity Code, который считается довольно-таки просто: мы берем сессионный ключ и с помощью него считаем HMAC_MD5 от конкатенации всех сообщений.

HMAC_MD5(Session key, NEGOTIATE_MESSAGE + CHALLENGE_MESSAGE + AUTHENTICATE_MESSAGE)

Теоретически, если протокол полгается на флаги sign и seal для подписи и ЗаПеЧаТыВаНиЯ, то без MIC мы бы могли отключить подпись и спокойно релеить NTLM (мб такой андеграундный протокол и существует).

В версиях Windows NT, Windows 2000, Windows XP, and Windows Server 2003 MIC не поддерживается.

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

Этот флаг находится в структуре AvPairs, которая добавляется только в NTLMv2, в NTLMv1 эта структура отсутствует. В этой структуре помимо флага, отвечающего за присутствие MIC находится также куча всего, включая Channel Binding и Target Info.

Рисунок 20 – Структура AvPairs
Рисунок 20 – Структура AvPairs

В Wireshark AvPairs показывается следующим образом

Рисунок 21 – AvPairs в AUTH-токене
Рисунок 21 – AvPairs в AUTH-токене

В данном источнике автор утверждает, что в LDAP подпись зиждется на флаге sign, но на практике я не смог заставить клиент и сервер не использовать подпись, отключая sign. Также я пробовал отключать seal, но шифрование пакетов все равно проходило. Если я рукожоп, и у вас это получилось – дайте знать в комментах.

Заключение

В данной статье мы с вами достаточно глубоко погрузились в работу протокола NTLM, узнали об атаке NTLM Relay, выяснили, какие механизмы защиты присутствуют в NTLM и поняли, почему использование NTLMv1 – прямой путь к компрометации ваших сервисов. Напомним, почему NTLMv1 небезопасен:

  1. Слабая криптография

  2. Полная уязвимость к NTLM Relay

Список используемой литературы

  1. NTLM Relay // hackndo URL: https://en.hackndo.com/ntlm-relay/#channel-binding (дата обращения: 07.02.2026);

  2. [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol // Microsoft URL: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 (дата обращения: 07.02.2026);

  3. Forshaw J. Windows Security Internals: A Deep Dive into Windows Authentication, Authorization, and Auditing. – No Starch Press, 2024;

  4. The NTLM Authentication Protocol and Security Support Provider // Davenport WebDAV-SMB Gateway URL: https://davenport.sourceforge.net/ntlm.html (дата обращения: 09.02.2026).