Перейти к содержимому
← Каталог golang Command Injection

Argument Injection в архиваторе файлов

Обработчик архивации файлов использует exec.Command без shell, но не фильтрует аргументы, начинающиеся с '-'. Атакующий может внедрить произвольные флаги команды tar.

hardgolangPro
Задача
# Argument Injection (RCE через флаги tar) ## Сценарий Вы проводите аудит **SecureShop**. Разработчик добавил API для архивации пользовательских файлов: `POST /api/archive` принимает массив имён файлов и кладёт их в `tar.gz`. Команда вызывается через `exec.Command("tar", args...)` — **без оболочки**, разделёнными аргументами. Архитектор уверен: «sh не участвует, значит command injection невозможен». Однако безопасное разделение аргументов решает только одну часть проблемы. Если хотя бы один из аргументов начинается с `-`, целевая утилита (`tar`, `git`, `curl`, `find`...) воспринимает его как **собственный флаг** — и многие из этих флагов запускают команды. ## Цель 1. Найдите эндпоинт архивации `/api/archive` и проверьте, что он не валидирует префиксы имён файлов. 2. Подайте в массив `files` строку, начинающуюся с `--`, чтобы передать `tar` собственный опасный флаг. 3. Используйте `tar`-флаг `--checkpoint-action=exec=...` для исполнения произвольной команды. 4. Эскалируйте до admin (например, прочитайте файл с сессионным токеном или флагом, или поднимите свою роль). 5. Получите флаг с `/admin`. ## Теория **Argument Injection** — класс уязвимостей, при котором атакующий контролирует не код команды и не оболочку, а **отдельные аргументы** программы. Достаточно, чтобы хотя бы один аргумент начинался с `-` или `--` — и утилита воспримет его как собственный флаг. Многие легитимные флаги популярных утилит фактически дают RCE или произвольный read/write: * `tar --checkpoint=1 --checkpoint-action=exec=CMD` — `tar` исполняет `CMD` при достижении контрольной точки. * `find . -exec CMD {} \;` — `find` запускает `CMD` для каждого найденного файла. * `git --upload-pack="cmd"` — `git` исполняет `cmd` при клоне. * `curl -K /etc/passwd` — `curl` читает свою конфигурацию из произвольного файла. * `wget --use-askpass=CMD` — `wget` исполняет `CMD` для получения пароля. * `zip --unzip-command="CMD"` — старый zip позволяет это поле. Корректная защита делается на двух уровнях: 1. **End-of-options marker `--`** — после него все следующие токены гарантированно интерпретируются как позиционные аргументы, а не флаги. Стандартная конструкция: `tar -czf out.tgz -- $userFiles`. 2. **Валидация префикса** — отклонять любой пользовательский аргумент, начинающийся с `-`, плюс allowlist допустимых символов в имени файла. **Уязвимый паттерн:** ```go func (h *Handler) ArchiveFiles(w http.ResponseWriter, r *http.Request) { var req struct{ Files []string `json:"files"` } json.NewDecoder(r.Body).Decode(&req) // Аргументы разделены — sh не участвует. Но префикс "-" не проверен: args := []string{"-czf", "/tmp/backup.tar.gz"} for _, f := range req.Files { args = append(args, f) // ← сюда попадает "--checkpoint-action=exec=..." } cmd := exec.Command("tar", args...) cmd.Run() } ``` ## Точка входа атаки | Параметр | Значение | |----------|----------| | Обычный пользователь | `demo` / `demo` | | Уязвимый эндпоинт | `POST /api/archive` (JSON `{"files": ["..."]}`) | | Цель | `GET /admin` (требует роль `admin`, показывает флаг) |
🚧 Сайт в разработке. Полный функционал пока недоступен. Все вопросы — support@hackandfix.ru