Авторские курсы Михаила Тарасова

Command Injection в YAML/INI — когда конфиг это RCE

Command Injection в YAML/INI - когда конфиг это RCE

Суть атаки (почему это вообще работает)

YAML поддерживает теги объектов (типа !!python/object), которые позволяют десериализовать не только данные, но и КЛАССЫ. Если backend использует yaml.load() без safe_load() — ты можешь передать pickle-объект, который выполнится при загрузке. Аналогично, CloudFormation-подобные парсеры с !Ref!Sub!GetAtt могут тянуть переменные окружения или выполнять команды, если девелоперы накостыляли свой велосипед.

Пример из жизни: CI/CD пайплайн читает .gitlab-ci.yml, парсит через PyYAML без safe mode → атакующий пушит коммит с !!python/object/apply:os.system ['rm -rf /'] → build-агент летит в тартарары.

Точка входа

Что вижу:

  • Эндпоинт принимает YAML/INI (типа /api/config/upload или /webhooks/deploy).

  • В документации упоминаются «dynamic variables» или «template substitution».

  • В ошибках видно трейсы типа yaml.load() или ConfigParser.read().

  • CloudFormation-like синтаксис в конфигах (!Ref AWS::StackName).

Как поймал:

Чем пахнет:

  • Класс: Deserialization RCE (10/10, это критично нах).

  • Если PyYAML < 5.4 без safe_load — ты root.

  • Если custom-парсер с eval() для переменных — аналогично.

Че почем

Эксплойт (с градацией по сложности):

1. Классика PyYAML (Python backend)

Отправляешь:

Если видишь входящий запрос на своём listener:

2. Reverse Shell через subprocess

На атакующей стороне:

3. CloudFormation-style инъекции

Если парсер поддерживает !Sub или !Ref с custom-логикой:

Или через environment variables:

Пример атаки:

Это CVE-2013-0156 style — длинный, но работает на старых Rails.

5. INI-инъекции (реже, но бывает)

Если backend делает:

То payload выполнится.

Обход защиты:

  1. safe_load() блочит теги? Попробуй anchor-overflow:

Это вызывает DoS через exponential expansion → парсер может упасть и fallback на небезопасный режим.

  1. Фильтруют !!python? Используй алиасы:

  1. WAF блочит os.system? Обфусцируй:

  1. Нет интернета на сервере? Пиши файлы:

Доказательство:

  1. Для bug bounty: запись с proof-of-execution:

  1. Скриншот: Burp-запрос + response с результатом команды.

  2. Для critical-репорта: создай файл в webroot:

Потом покажи https://target.com/pwned.txt.

План атаки (пошаговый)

Шаг 1: Найди точку загрузки конфигов

Типичные пути:

  • /api/v1/config/upload

  • /admin/settings/import

  • /webhooks/gitlab (CI/CD endpoints)

  • /api/deploy/template

Шаг 2: Определи парсер

Отправь валидный YAML и смотри ошибки:

Если видишь:

  • yaml.scanner.ScannerError → PyYAML

  • Psych::SyntaxError → Ruby

  • SnakeYAML → Java

Шаг 3: Попробуй безопасный payloads

Начни с ping-теста:

Слушай ICMP:

Шаг 4: Эскалируй до shell

Если ping работает:

Или через staged:

На evil.com/s.sh:

Шаг 5: Закрепись

После получения shell:

Real-world примеры

CVE-2020-14343 (PyYAML < 5.4):
Множество проектов использовали yaml.load() для конфигов. Атакующие пушили троянские .gitlab-ci.yml или .travis.yml с RCE-payload → compromised CI/CD runners.

Kubernetes ConfigMaps (2019):
Админы создавали ConfigMaps через kubectl с YAML-файлами. Если внутри был !!python/object — kubelet десериализовывал и выполнял код на ноде.

AWS CloudFormation custom resources (2021):
Кастомные Lambda-функции парсили templates с eval() для !Sub. Payload:

Все account ID утекли в логи атакующего.

Советы (3 вектора добивания)

  1. Если safe_load() включен — ищи template injection в значениях:

  1. Проверь YAML Anchors для DoS → Fallback:

  1. Если всё плохо — лови race condition:

Мои курсы