
Вспомогательная функция writeToFile в Consul Template напрямую открывала указанное оператором место назначения и следовала по связанным компонентам пути, позволяя сгенерированному выводу выйти за пределы целевого каталога и перезаписать уже существующий файл.
Помощник writeToFile в Consul Template открывал указанное оператором место назначения напрямую и следовал по связанным компонентам пути, позволяя отрисованному выводу выйти за пределы задуманного каталога и перезаписать существующий файл.
Я нашёл эту проблему при анализе HashiCorp Consul Template, задав прямой вопрос о безопасности файловой системы:
Если writeToFile получает путь, который выглядит находящимся внутри задуманного каталога, проверяет ли он, куда файловая система фактически запишет данные?
В данном случае ответ был отрицательным.
Помощник шаблона writeToFile открывал итоговый указанный пользователем путь напрямую через os.Create() или os.OpenFile().
Эти операции следовали по символическим ссылкам, junction каталогов и эквивалентным перенаправлениям файловой системы, уже присутствующим в пути назначения.
Это означало, что строка пути могла оставаться под задуманным оператором корневым каталогом, в то время как фактическая запись попадала в другое место.
В моём контролируемом proof of concept связанный родительский каталог перенаправил отрисованный вывод за пределы задуманного дерева каталогов и привёл к перезаписи существующего целевого файла.
Эта проблема стала CVE-2026-14361.
Бюллетень HashiCorp: HCSEC-2026-20
Бюллетень IBM: бюллетень безопасности CVE-2026-14361
CVE: CVE-2026-14361
Исправлено в: 0.42.1
photo0
указанное оператором место назначения выглядит находящимся внутри задуманного корневого каталога -> атакующий заранее размещает связанный родительский или конечный компонент пути -> writeToFile открывает путь напрямую -> файловая система разрешает запись за пределами задуманного каталога -> отрисованный секрет перенаправляется -> существующий целевой файл может быть перезаписан
Consul Template отрисовывает данные из таких источников, как Consul и Vault.
Помощник writeToFile позволяет шаблону записывать выбранное содержимое в отдельный локальный файл, применяя запрошенных владельца, группу и режим прав доступа.
В документации HashiCorp этот помощник специально демонстрируется на материалах PKI:
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem
Это делает его не просто обычным помощником для вывода данных.
Содержимое, пересекающее эту границу, может включать:
Важным вопросом было не то, может ли writeToFile создать запрошенное имя файла.
Настоящий вопрос заключался в следующем:
Записывает ли процесс данные в то место файловой системы, которое задумал оператор, или просто в тот объект, на который путь разрешается в момент открытия?
В уязвимых версиях он доверял второму.
Помощники записи — это поверхности безопасности высокой ценности, поскольку они переходят от данных приложения к изменениям файловой системы.
Интересные сбои часто не являются классическим обходом через ../.
Это сбои разрешения путей:
Это особенно важно, когда процесс работает с большими привилегиями в файловой системе, чем атакующий.
Локальный атакующий с низкими привилегиями может быть не в состоянии напрямую перезаписать чувствительный файл.
Но если он может влиять на компонент пути внутри задуманного каталога записи, более привилегированный процесс Consul Template может выполнить запись за него.
Именно эту границу я исследовал.
Я подошёл к этому не как к обычному анализу обхода путей.
Предоставленному пути не нужны сегменты ...
Он мог всё время оставаться лексически внутри задуманного корневого каталога.
Более сильный вопрос звучал так:
Отвергаются ли связанные компоненты назначения до того, как будет записано чувствительное содержимое?
Этот вопрос важен для двух случаев:
Второй случай особенно полезен, потому что журналы и конфигурация по-прежнему показывают безобидно выглядящий путь внутри ожидаемого каталога.
Файловая система разрешает его в другом месте.
Корневая причина — прямое создание файла на основе пути без проверки символических ссылок.
В протестированной ревизии writeToFile() выбирал один из двух путей открытия.
Режим добавления использовал:
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
Обычный режим записи использовал:
dirPath := filepath.Dir(path)
if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}
f, err = os.Create(path)
Ни один из путей не отвергал связанные компоненты назначения до открытия файла.
Это важно, потому что:
os.Create(path) следует существующим перенаправлениям файловой системы и усекает разрешённый файлos.OpenFile(path, ...) следует связанным компонентам пути в режиме добавленияТо же предположение, основанное на пути, сохранялось и после записи.
Владелец и права доступа снова применялись через путь:
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
Это означало, что операции с метаданными также были привязаны к изменяемому имени пути, а не к уже открытому файловому дескриптору.
Потому что атакующий может подготовить перенаправление до запуска writeToFile.
Для базовой атаки не требуется вероятностная гонка.
Последовательность проста:
writeToFile открывает её напрямуюВ этом и заключается вся уязвимость.
Действительно, операционные системы обычно следуют по символическим ссылкам при открытии файлов по пути.
Это не делает такое поведение приложения безопасным.
Вопрос безопасности не в том:
«Вёл ли себя Go так, как задокументировано?»
Настоящий вопрос в том:
Проверял ли помощник, записывающий чувствительные отрисованные данные, что разрешённое место назначения совпадает с задуманным оператором?
В уязвимых версиях — нет.
Это различие важно, потому что Consul Template может работать:
Следование перенаправлению файловой системы, созданному атакующим, в таком контексте создаёт реальную проблему привилегий и границ доверия.