Разработчик запустил свой код на сервере, имея лишь возможность изменения содержимого комментария в Python-скрипте

Участник соревнования по информационной безопасности UIUCTF 2025 в техническом блоге подробно рассказал, что ему удалось успешно выполнить задание, требующее добиться исполнения стороннего кода на сервере, имея лишь возможность изменения содержимого текста комментария в коде Python-скрипта.

По информации OpenNET, в рамах этой задачи можно было отправить сетевой запрос к Python-скрипту, который создавал новый Python-скрипт cо случайными именем, добавлял поступившие от пользователя данные в текст комментария, вырезав символы «n» и «r», и запускал этот скрипт командой «python3 имя.py». Контролируя только содержимое комментария участник должен был извлечь строку из файла «/home/ctfuser/flag». Скрипт создавался следующим кодом:

Вместо «{comment}» подставлялись данные, поступившие от участника, и в итоге запускался следующий код:

Задание было сформировано по мотивам уязвимости в парсере CPython, который обрабатывал символ с нулевым кодом, как окончание строки (уязвимость, например, можно было использовать для скрытия вредоносных действий в тексте комментария). Проблема была устранена в выпусках CPython 3.12.0 и 3.11.4. В применяемом в конкурсе обработчике вырезались только символы «n» и «r», но при использовании уязвимой версии СPython участник мог использовать символ «» как разделитель. Тем не менее этот трюк не сработал так как в конкурсе использовалась уже исправленная версия CPython c расчётом, что в парсере могут оставаться ещё какие‑то похожие ошибки и участники смогут их выявить.

Разработчик не стал искать новые уязвимости в парсере, которые бы позволили разбить строку на части, а воспользовался особенностью выполнения в Python файлов по типу их содержимого. Например, вместо исходного кода в файл с расширением «.py» можно поместить прокэшированный байткод, сохраняемый в файлах с расширением «.pyc», и подобный файл будет выполнен. В рассматриваемом конкурсе участник мог контролировать только содержимое в середине файла, поэтому не мог добавить свой заголовок для искажения MIME‑типа.

Задачу удалось решить, воспользовавшись тем, что Python начиная с ветки 2.6 может исполнять содержимое ZIP‑архивов для поставки Python‑пакетов в сжатом виде. Как и в случае с кэшем байткода наличие zip‑архива определяется по содержимому, а не по расширению файла, т. е. в «файл.py» можно поместить zip‑архив и при запуске командой «python файл.py» он будет обработан, как сжатый Python‑пакет. При этом ZIP‑архивы в Python индексируются не по заголовку в начале файла, а по секции EOCD (End of Central Directory Record) в конце файла. При наличии в архиве файла «__main__.py», этот файл запускается автоматически при прямом запуске архива командой «python архив».

Конкурсная задача была решена генерацией подобного ZIP‑архива и подстановкой его в текст комментария. Для сохранения корректности структуры файла в условиях наличия в конце исходного файла вызова ‘print(«Thanks for playing!»)’, было использовано наличие в EOCD‑секции области комментария, размещаемой в самом конце.

Источник: habr.com

0 0 голоса
Рейтинг новости
1
0
Подписаться
Уведомить о
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии