File Upload: Обход расширений и Path Traversal
Сервер принимает файлы с расширением .jpg, но слепо доверяет имени файла из Content-Disposition. Используйте Path Traversal, чтобы подбросить свой токен конфигурации и получить флаг.
hardgolangPro
Задача
# File Upload: подмена имени файла как канал записи произвольного пути
## Сценарий
Вы зарегистрированы в интернет-магазине **SecureShop** как пользователь **demo**. На странице профиля доступна загрузка аватара: пользователь выбирает изображение, multipart-запрос уходит на сервер, который сохраняет файл в директорию для статики. Команда уверена, что эндпоинт безопасен — пользователь не контролирует имя сохранения, контролируется лишь содержимое аватара. По данным внутреннего аудита, на сервере хранится конфиденциальная метка в переменной окружения `LAB_FLAG`, доступная коду рендеринга страниц (через зарегистрированную template-функцию); её требуется извлечь.
## Цель
1. Изучите multipart-поток, который отправляет форма загрузки аватара. Какие части запроса полностью контролируются клиентом?
2. Найдите способ направить сохранение файла за пределы директории статики — в место, где сервер использует загруженный контент при рендеринге следующих запросов.
3. Используйте этот канал, чтобы получить значение `LAB_FLAG` через template-функцию, доступную в шаблонизаторе сервера. Извлеките флаг.
## Теория
Multipart-запрос включает заголовки для каждой части — в том числе `Content-Disposition` с полем `filename`, в которое браузер по умолчанию помещает оригинальное имя файла, выбранного пользователем. Это полностью **клиентский** ввод: атакующий, отправляющий запрос вручную (через curl, Burp, Python `requests`), может задать там произвольную строку — включая последовательности path traversal (`../`, абсолютные пути, кодированные эквиваленты).
**Уязвимый паттерн:**
```go
// Сервер использует имя файла из multipart-заголовка как часть пути сохранения
filename := header.Filename // полностью контролируется клиентом
uploadPath := filepath.Join("static", "img", filename)
dst, _ := os.Create(uploadPath) // ../* в filename выведет за пределы static/img
```
`filepath.Join` нормализует путь, но не обрезает восходящие сегменты — поэтому `../../some/dir/file` относительно базы выводит результат за пределы статики. Самый «вкусный» класс целей для записи — файлы, которые приложение **читает и интерпретирует** при последующих запросах: HTML/Go-шаблоны (рендерятся при каждой подгрузке страницы), скрипты-бэкдоры (если есть исполняемый PHP/CGI слой), конфиги (если читаются на лету). При hot-reload шаблонов в Go перезаписанный файл подхватывается без рестарта сервера — атакующему достаточно одного запроса.
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Учётные данные | `demo` / `demo` |
| Эндпоинт | `POST /profile/avatar` (multipart/form-data) |
| Уязвимый канал | заголовок `Content-Disposition: ...; filename="..."` части `avatar` |
| Доступная server-side функция шаблонизатора | `{{ env "LAB_FLAG" }}` (зарегистрирована в funcMap) |
| Цель | вывести флаг через server-side рендеринг после подмены контента в одном из директорий, читаемых шаблонизатором |