TACACS+ проектировался в 1998 году как протокол для централизованной аутентификации, авторизации и учёта сетевого доступа. Спустя почти три десятилетия он остаётся в эксплуатации — и именно поэтому разбор его механики важен: слабости, заложенные в исходном дизайне, никуда не исчезли, а обратная совместимость удерживает их в работающих сетях.
Что именно признано проблемным в исходном дизайне
RFC 8907 прямо говорит: проект 1998 года не решил все ключевые вопросы безопасности, которые рассматриваются при проектировании современных стандартов. Протокол не включает механизм безопасности, отвечающий современным требованиям, а используемые механизмы корректнее называть обфускациейзапутывание данных, не дающее настоящей криптографической защиты, а не шифрованием — они не обеспечивают осмысленной целостности, приватности и защиты от повторов.
Из этого следуют конкретные свойства. Атакующий с доступом к потоку данных может читать и изменять все пакеты TACACS+. Учётная информация, проходящая через человека посерединеатакующий, встраивающийся в канал между клиентом и сервером, может быть изменена, что делает такие журналы непригодными и не заслуживающими доверия для аудита. Более того, атакующий может вставлять недействительные или вводящие в заблуждение значения в различные поля по известным смещениям, чтобы обойти проверки аутентификации или авторизации даже внутри обфусцированного тела.
К этому добавляется список криптографических слабостей: атаки перебором, использующие повышенную эффективность вычисления MD5; атаки с известным открытым текстом, снижающие стоимость перебора; атаки с выбранным открытым текстом с тем же эффектом; и отсутствие прямой секретности. Метод шифрования, по признанию авторов, не был строго протестирован.
Обратная совместимость как условие эксплуатации
Протокол никогда не был стандартизирован, и развёрнутые реализации могут быть несовместимы неочевидным образом, что само по себе создаёт дополнительные риски безопасности. Совместимость держится на правиле: если демон TACACS+ получает пакет с неподдерживаемой им minor_versionмладшая часть номера версии протокола, различающая варианты обработки аутентификации, он должен вернуть статус ERROR с minor_version, установленной в ближайшее поддерживаемое значение.
Различия между minor_version 0 и 1 касаются только обработки CHAP, ARAP и PAP. В версии 0 NAS отправлял SENDPASS, чтобы получить открытый пароль и сам выполнить хеширование и шифрование. В версии 1 используется LOGIN с обменом через поле data: NAS шлёт один START и ждёт PASS или FAIL. RFC 8907 уточняет, что minor_version 1 допустима только для видов аутентификации, явно указанных в таблице (PAP, CHAP, MS-CHAPv1/2), тогда как вся авторизация, аккаунтинг и ASCII-аутентификация используют minor_version 0. Если сервер не реализует опцию, он должен ответить TAC_PLUS_AUTHEN_STATUS_FAIL.
Для эксплуатации это критично: сервер обязан принимать пакеты в старом формате, а несовместимости развёрнутых реализаций открывают дополнительные пути.
Анатомия пакета
Каждый пакет TACACS+ начинается с 12-байтового заголовка, который всегда передаётся в открытом виде и описывает остаток пакета. Поле version объединяет major_version (0xc) и minor_version (0x0 или 0x1). Поле type задаёт тип пакета: аутентификация, авторизация или аккаунтинг. Поле seq_no — номер пакета в сессии: первый пакет обязательно имеет номер 1, далее инкремент на единицу, клиенты шлют только нечётные номера, серверы только чётные, и номер не должен переполняться — при достижении предела сессия завершается и перезапускается с 1.
Поле flags содержит битовые флаги. TAC_PLUS_UNENCRYPTED_FLAG (0x01) означает, что тело не обфусцировано, и не должно использоваться в продакшене. TAC_PLUS_SINGLE_CONNECT_FLAG (0x04) служит для согласования Single Connection Modeрежима, в котором несколько сессий TACACS+ передаются по одному уже установленному TCP-соединению вместо открытия нового соединения на каждую сессию. Остальные биты должны игнорироваться при чтении и быть нулевыми при записи.
Поле session_idидентификатор сессии TACACS+, который не меняется на протяжении всей сессии и должен генерироваться криптостойким генератором псевдослучайных чисел. Поле length задаёт длины полей данных и сообщений; все длины беззнаковые и в сетевом порядке байт.
Поскольку заголовок всегда передаётся в открытом виде, атакующий с доступом к потоку данных может читать и модифицировать все пакеты TACACS+, то есть контролировать все поля заголовка до аутентификации.
Обработка перечисляемых значений
Документ определяет перечисляемые значения в заголовке пакета и заголовках конкретных типов пакетов. Например, в authentication start поле action имеет три значения: LOGINзапрос на вход в систему под учётной записью, CHPASSзапрос на смену пароля пользователя и SENDAUTHпередача серверу данных для выполнения аутентификации на стороне сервера. Если сервер не реализует один из определённых вариантов в полученном пакете или встречает вариант, не перечисленный в документе для поля заголовка, он должен ответить ERROR и завершить сессию — это позволит клиенту попробовать другой вариант.
Если ошибка возникает, но тип входящего пакета не может быть определён, должен быть возвращён пакет с идентичным открытым заголовком, но с номером последовательности, увеличенным на единицу, и длиной, установленной в ноль, чтобы указать на ошибку.
Как работает обфускация тела
Тело пакета обфусцируется побайтовым XOR с псевдослучайной подушкой, которая формируется конкатенацией MD5-хешей по 16 байт и усечением до длины входных данных. Первый хеш вычисляется от конкатенации session_id, секретного ключа, номера версии и номера последовательности:
MD5_1 = MD5{session_id, key, version, seq_no}
MD5_2 = MD5{session_id, key, version, seq_no, MD5_1}Последующие хеши используют тот же входной поток, но с добавлением предыдущего хеша в конец. session_id подаётся в сетевом порядке байт, а version — это один байт, объединяющий старший и младший номера версии. При TAC_PLUS_UNENCRYPTED_FLAG == 0x1 тело передаётся в открытом виде, что допустимо только для отладки.
Почему это не шифрование
RFC 8907 не называет обфускацию шифрованием, потому что алгоритм не соответствует современным стандартам. Требование к длине общего ключа: серверы и клиенты TACACS+ обязательно должны поддерживать общие ключи длиной не менее 32 символов. При этом клиентам не следует разрешать настройку серверов без общего секрета или с ключом короче 16 символов, а администраторам следует задавать ключи минимум из 16 символов.
Среди атак на слабый ключ или его отсутствие перечислены всё те же атаки перебором с использованием повышенной эффективности MD5, атаки на основе известного открытого текста, атаки на основе выбранного открытого текста и отсутствие прямой секретности. Злоумышленник, который может угадать ключ или иным образом сломать обфускацию, получит неограниченный и незаметный доступ ко всему трафику TACACS+.
Что происходит без ключа или с коротким ключом
Обфускация опирается на общий секрет, известный клиенту и серверу, и секретные ключи должны оставаться секретными. Серверы TACACS+ обязаны отклонять соединения с установленным TAC_PLUS_UNENCRYPTED_FLAG, и для клиента, запрашивающего соединение, на сервере всегда должен быть задан общий секрет.
При обнаружении неверного общего секрета при обработке пакетов сервер не должен принимать новые сессии на этом соединении и должен завершить соединение после завершения ранее установленных сессий с действительным общим секретом. Последствия для целостности и конфиденциальности тела пакета прямые: механизм не обеспечивает осмысленной целостности, приватности или защиты от повторов.
Пошаговый разбор механизма эксплуатации
Обмен START/REPLY/CONTINUE
Каждый пакет START, REPLY и CONTINUE содержит поле data, назначение которого зависит от вида аутентификации. START запускает поток, сервер отвечает либо запросом дополнительных сведений (GETDATA, GETUSER или GETPASS), либо завершением PASS, FAIL, ERROR или RESTART. При статусе REPLY, равном GETDATA, GETUSER или GETPASS, аутентификация продолжается, и клиент обязан вернуть CONTINUE с запрошенными сведениями в поле user_msg.
В ASCII-логине START может содержать имя пользователя; если его нет, сервер получает имя через CONTINUE со статусом GETUSER, а затем получает пароль через CONTINUE со статусом GETPASS. И имя, и пароль передаются в поле user_msg. Поля data в START и CONTINUE для ASCII-логина не используются, и любое их содержимое должно игнорироваться.
В PAP-логине весь обмен состоит из одного START и одного REPLY. START обязан содержать имя пользователя, а поле data обязано содержать PAP ASCII-пароль. Именно здесь сервер впервые полагается на данные тела пакета до проверки учётных данных: пароль принимается в data уже в START.
Запросы на повышение привилегий
ENABLE-запросзапрос на изменение текущего рабочего уровня привилегий пользователя, который может состоять из нескольких сообщений, пока сервер собирает нужную информацию используется для изменения текущего рабочего уровня привилегий субъекта и может состоять из нескольких сообщений, пока демон собирает нужную информацию. Чтобы отличить такой запрос от других, поле service должно быть установлено в TAC_PLUS_AUTHEN_SVC_ENABLEзначение поля service, которым помечается ENABLE-запрос и которое не должно использоваться при любых других операциях при запросе ENABLE и не должно устанавливаться в это значение при любых других операциях.
Уровни привилегий — упорядоченные значения от 0 до 15, где каждый уровень является надмножеством следующего нижнего. Клиент вставляет требуемый уровень привилегий в заголовок аутентификации для ENABLE-запросов. Сервер возвращает уровень привилегий в session-based shell authorization (когда service равен shell и cmd пуст). Когда пользователю нужно выполнить действия, отображённые на более высокий уровень, клиент может инициировать ENABLE-реаутентификацию.
Завершение сессии
Клиент может досрочно завершить сессию, установив флаг TAC_PLUS_CONTINUE_FLAG_ABORTфлаг в сообщении CONTINUE, которым клиент досрочно прерывает сессию в сообщении CONTINUE. При установленном флаге часть data может содержать текст с причиной прерывания, который сервер обрабатывает по требованиям развёртывания.
Обычная сессия завершается по статусу в REPLY-пакете: PASS или FAIL означают, что обработка запроса завершена и клиент может применить результат; ERROR означает, что обработка не завершилась, и клиент обязан вести себя так, будто сервер недоступен. Если TCP-соединение закрывается, пока сессия в процессе, клиент должен обработать это согласно разделу о завершении сессии; если сессия не в процессе, клиент должен обнаружить это и перезапустить Single Connection Mode при следующей сессии.
Сессии аутентификации следует использовать через защищённый транспорт, поскольку атака «человек посередине» может полностью их подорвать. Из-за отсутствия целостности любой байт полезной нагрузки может быть изменён без обнаружения ни одной из сторон.
Ограничения на длину тела
Раздел 4.2 RFC 8907 не задаёт ограничений на максимальную длину тела и не описывает поведения при превышении лимита — там лишь сказано, что типы тела определяются в заголовке пакета, а содержимое разных тел рассматривается в последующих разделах. Ограничение по размеру задано отдельно для поля length заголовка: реализации обязаны позволять настраивать максимальный принимаемый размер пакета, а рекомендуемое значение — 216. Общих правил о переполнении буфера или отказе в обслуживании при превышении лимита в этих разделах не приводится.
Пограничные случаи и условия эксплуатации
Несовпадение версий
Когда демон TACACS+ получает пакет с minor_version, который он не поддерживает, он должен вернуть статус ERROR, установив в ответе minor_version в ближайшее поддерживаемое значение к запрошенному. Различия между minor_version 0 и 1 касаются только способа обработки аутентификаций CHAP, ARAP и PAP.
В minor_version 0 эти аутентификации выполнялись через отправку NAS пакета SENDPASS демону для получения открытого пароля пользователя, а хеширование CHAP и шифрование ARAP выполнялись на NAS. В minor_version 1 CHAP, ARAP и входящий PAP используют LOGIN и поле data, так что NAS отправляет один START и ожидает PASS или FAIL; SENDPASS объявлен устаревшим и заменён на SENDAUTH.
При обнаружении minor_number 0 демон должен допускать PAP-аутентификации, которые не присылают пароль в поле data, а ожидают прочитать пароль PAP из последующего пакета CONTINUE.
Риски сессий аутентификации
Раздел 10.2 перечисляет риски, специфичные для сессий аутентификации, в дополнение к общим рискам обфускации и отсутствия проверки целостности. Первый риск — атака «человек посередине», которая может полностью подменить сессию, поэтому сессии аутентификации следует использовать через защищённый транспорт. Даже CHAP, считающийся устойчивым к перехвату пароля, небезопасен, поскольку не защищает имя пользователя от тривиальной атаки «человек посередине».
Второй риск связан с механизмом перенаправления через опцию TAC_PLUS_AUTHEN_STATUS_FOLLOWстатус ответа сервера, которым он перенаправляет клиента на другой сервер аутентификации: при его использовании секретный ключ нового сервера отправлялся клиенту, и публичный обмен ключами означает, что при компрометации одной сессии этот ключ можно использовать для атаки на соединения с другими серверами. Этот механизм объявлен устаревшим и не должен применяться в современных развёртываниях и вне защищённой среды.
Прямого описания внедрения команд или повышения привилегий именно в разделе 10.2 нет; пример изменения поля authen_method с TAC_PLUS_AUTHEN_METH_TACACSPLUS на TAC_PLUS_AUTHEN_METH_LINE, дающий доступ ко всем командам, приведён в разделе 10.3 для сессий авторизации.
Что видит клиент и администратор
Статусы ответа
Тело REPLY-пакета содержит поле status — текущее состояние аутентификации. Допустимые значения: PASSаутентификация успешно завершена, клиент может применить результат, FAILаутентификация завершена неуспешно, клиент применяет отказ, GETDATAсервер запрашивает у клиента дополнительные данные, аутентификация продолжается, GETUSERсервер запрашивает у клиента имя пользователя, аутентификация продолжается, GETPASSсервер запрашивает у клиента пароль, аутентификация продолжается, RESTARTсервер требует перезапустить поток аутентификации, ERRORобработка запроса не завершилась, клиент обязан вести себя так, будто сервер недоступен и FOLLOWсервер перенаправляет клиента на другой сервер аутентификации. Тело CONTINUE-пакета, отправляемого клиентом серверу после получения REPLY, определяет только флаг TAC_PLUS_CONTINUE_FLAG_ABORT (0x01), а не статусы ответа.
Сервер отвечает либо запросом дополнительной информации (GETDATA, GETUSER или GETPASS), либо завершением PASS, FAIL, ERROR или RESTART. При GETDATA/GETUSER/GETPASS аутентификация продолжается, и клиент обязан вернуть CONTINUE с запрошенной информацией в поле user_msg.
Механизм перенаправления через TAC_PLUS_AUTHEN_STATUS_FOLLOW признан устаревшим и не должен использоваться в современных развёртываниях. Поскольку протокол не обеспечивает целостности, приватности и защиты от повторов, атакующий с доступом к потоку данных может читать и изменять все пакеты TACACS+, а также вставлять недействительные или вводящие в заблуждение значения в различные поля по известным смещениям, чтобы обойти проверки аутентификации или авторизации даже внутри обфусцированного тела.
Записи в accounting-логах
Тип accounting-записи задаётся битовыми флагами в поле flags: TAC_PLUS_ACCT_FLAG_START (0x02), TAC_PLUS_ACCT_FLAG_STOP (0x04), TAC_PLUS_ACCT_FLAG_WATCHDOGБитовый флаг 0x08 в поле flags accounting-запроса TACACS+, обозначающий промежуточное уведомление о том, что сервис всё ещё выполняется. (0x08). В draft-grant-tacacs-02 к ним добавлен устаревший TAC_PLUS_ACCT_FLAG_MORE (0x01).
Тело accounting-запроса содержит поля flags, authen_method, priv_lvl, authen_type, authen_service, длины user, port, rem_addr, arg_cnt и аргументы. Все поля, кроме flags, определены в разделах аутентификации и авторизации и имеют ту же семантику. Обнаружить аномалию можно по flags (например, несоответствие Start/Stop/Watchdog), по priv_lvl (уровень привилегий пользователя) и по arg — атрибут-значение, описывающее выполняемую команду.
Компромиссы и почему так сделано
Ограничения развёртывания
Механизмы безопасности TACACS+ следует называть обфускацией, а не шифрованием, поскольку они не обеспечивают осмысленной целостности, приватности и защиты от повторов. Поэтому развёртывающие TACACS+ обязаны ограничивать доступ известными клиентами и контролировать безопасность всего пути передачи.
Администратор сети не должен полагаться на обфускацию TACACS+: протокол должен использоваться в защищённом развёртывании, в сети, обеспечивающей приватность и целостность и отделённой от другого трафика. Эти ограничения не были наложены в исходном проекте, но новые реализации и обновления текущих обязаны их реализовать, а вендоры должны предоставлять механизмы помощи администратору.
Компромисс между гибкостью и безопасностью прямо описан для авторизации: расширяемость через новые имена аргументов требует, чтобы администраторы и разработчики обеспечивали согласованную интерпретацию пар аргумент-значение.
Рекомендации по отказу от опасных опций
Опции TAC_PLUS_AUTHEN_SENDAUTH и TAC_PLUS_AUTHEN_SENDPASS не следует использовать из-за присущих им рисков безопасности; серверы не должны их реализовывать, а при необходимости реализации обязаны по умолчанию отключать их и предупреждать администратора об их небезопасности.
Клиенты, получившие нераспознанный обязательный аргумент, должны оценивать ответ сервера так, как если бы получили TAC_PLUS_AUTHOR_STATUS_FAILстатус отказа в авторизации.
Защита трафика и препятствия
Администратор не должен полагаться на обфускацию TACACS+; протокол должен разворачиваться в защищённой сети, обеспечивающей приватность и целостность и отделённой от другого трафика. Серверы обязаны принимать соединения только от заранее определённых клиентов и отклонять пакеты с флагом TAC_PLUS_UNENCRYPTED_FLAG.
Практическое препятствие — унаследованные реализации: развёрнуто множество реализаций протокола из исходного проекта, и из-за отсутствия стандартизации они могут быть несовместимы неочевидным образом, создавая дополнительные риски безопасности. Сам механизм защиты старого протокола не даёт нужных свойств. В долгосрочной перспективе рекомендуется переход на TACACS+ поверх TLS 1.3, поскольку старый механизм защиты трафика больше не соответствует современным требованиям безопасности.
Что из этого следует на практике
- Обфускация тела пакета — не шифрование: она не даёт целостности, приватности и защиты от повторов, поэтому любой, кто видит поток данных, может читать и изменять все пакеты TACACS+.
- Заголовок всегда открыт, поэтому все его поля контролируются атакующим до аутентификации; вставка значений по известным смещениям позволяет обходить проверки даже внутри обфусцированного тела.
- Сервер обязан принимать пакеты со старым minor_version и допускать PAP-аутентификации, ожидающие пароль в последующем CONTINUE, — это расширяет поверхность атаки на развёрнутых реализациях.
- В PAP-логине пароль приходит в поле data уже в START, то есть сервер полагается на данные тела до проверки учётных данных.
- Ключ короче 16 символов или его отсутствие сводят обфускацию к нулю; серверы обязаны отклонять соединения с TAC_PLUS_UNENCRYPTED_FLAG и завершать соединение при обнаружении неверного общего секрета.
- Механизм перенаправления через FOLLOW устарел и не должен применяться: публичный обмен ключами позволяет переиспользовать скомпрометированный ключ против других серверов.
- Обнаружить аномалии в accounting-логах можно по flags (несоответствие Start/Stop/Watchdog), priv_lvl и arg; поля task_id и cmd для этого не описаны.
- Единственная надёжная защита — защищённый транспорт и изоляция трафика; в долгосрочной перспективе — переход на TACACS+ поверх TLS 1.3.