Path Traversal (Базовый)
Классическая уязвимость Path Traversal через os.Open.
easygolangPro
Задача
# Path Traversal (Чтение произвольных файлов через скачивание промо-материалов)
## Сценарий
Вы проводите аудит безопасности интернет-магазина **SecureShop**. В разделе акций есть страница, позволяющая клиентам скачивать промо-материалы — баннеры, листовки, изображения. Эндпоинт принимает имя файла в GET-параметре, склеивает его с базовой директорией `/lab/static/img/` и открывает через `os.Open`. Никакой проверки, что результирующий путь действительно остался внутри базовой директории, нет — стандартная ошибка-новичок, открывающая чтение любого файла, доступного процессу приложения.
## Цель
1. Найдите эндпоинт скачивания промо-материалов и убедитесь, что он принимает имя файла без валидации.
2. С помощью последовательностей `../` выйдите из базовой директории и подтвердите path traversal на безопасной цели (например, `../../../../etc/passwd`).
3. Прочитайте содержимое файла `/tmp/flag.txt` через эту же уязвимость.
4. Сдайте флаг.
## Теория
**Path Traversal** (он же Directory Traversal, dot-dot-slash) — атака, при которой пользователь манипулирует именем файла, чтобы выйти за границы предусмотренной директории. Базовый вектор — добавление последовательностей `../` (или Windows-эквивалентов `..\`) к имени файла: каждая такая последовательность поднимает путь на уровень выше. Если приложение склеивает строки и не нормализует/не проверяет результат, атакующий читает (а иногда и пишет) любые файлы, доступные процессу.
Стандартные защитные шаги:
1. **Нормализовать путь** — `filepath.Clean(...)` схлопнет `../` и удалит дублирующиеся `/`.
2. **Проверить, что нормализованный путь начинается с базовой директории** — `strings.HasPrefix(cleaned, baseDir)` или `filepath.Rel(baseDir, cleaned)` без `..` в результате.
3. Только после этого открывать файл.
Одной нормализации **недостаточно**: `filepath.Clean` приведёт `/lab/static/img/../../../etc/passwd` в `/etc/passwd`, но `os.Open` его без проблем откроет — нормализация даёт «чистое» имя, а не безопасное.
**Уязвимый паттерн:**
```go
func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
file := r.URL.Query().Get("file")
// Конкатенация без проверки + os.Open. ../ в имени выводит за пределы базы:
fullPath := "/lab/static/img/" + file
f, err := os.Open(fullPath)
if err != nil { http.Error(w, "Not Found", 404); return }
defer f.Close()
io.Copy(w, f)
}
```
## Точка входа атаки
* **Эндпоинт:** `GET /download?file=<имя_файла>`
* **Базовая директория:** `/lab/static/img/`
* **Где лежит флаг:** `/tmp/flag.txt`