Един от участниците в състезанието UIUCTF 2025 подробно обясни как успя да изпълни задача, изискваща да постигне изпълнение на своя код на сървера, имайки само възможността да променя съдържанието на текстовия коментар в кода.
Участниците можеха да изпратят мрежово запитване към Python скрипт, който създаваше нов Python скрипт с произволно име, добавяше предоставените от потребителя данни в текстовия коментар, изрезвайки символите «\n» и «\r», и изпълняваше този скрипт с командата «python3 име.py». Контролирайки само съдържанието на коментара, участникът трябваше да извлече ред от файла «/home/ctfuser/flag». Скриптът се създаваше с следния код: comment = input(«> «).replace(«\n», «»).replace(«\r», «») код = f»»»print(«hello world!») # Това е коментар. Ето още един: # {comment} print(«Thanks for playing!»)»»»
Вместо «{comment}» се поставяха данните, постъпили от участника, и в крайна сметка се изпълняваше следният код: print(«hello world!») # Това е коментар. Ето още един: # Данни, постъпили от участника в състезанието print(«Thanks for playing!»)
Задачата беше формулирана вдъхновена от уязвимост в парсера CPython, който обработваше символ с нулев код като край на реда (уязвимост, например, можеше да се използва за скриване на злонамерени действия в текста на коментара). Проблемът беше отстранен в версиите CPython 3.12.0 и 3.11.4. В приложеното в състезанието обработващо устройство бяха изрезани само символите «\n» и «\r», но при използването на уязвимата версия на CPython, участникът можеше да използва символа «\0» като разделител. Все пак, този трик не сработи, тъй като в състезанието се използваше вече поправената версия на CPython с разчет, че в парсера могат да останат още някакви подобни грешки и участниците да успеят да ги открият.
Успешно справилият се с задачата участник не търсеше нови уязвимости в парсера, които да позволят разделяне на реда на части, а се възползва от особеностите на изпълнението на 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, разположена в самия край.

Източник: opennet.ru
