Jeden z účastníků soutěže UIUCTF 2025 podrobně popsal, jak se mu podařilo splnit úkol, který vyžadoval, aby vykonal svůj kód na serveru, přičemž měl pouze možnost měnit obsah textu komentáře v kódu.
Účastníci mohli poslat síťový požadavek na skript Python, který vytvořil nový skript Python s náhodným názvem, přidal data od uživatele do textu komentáře, odstranil znaky „\n“ a „\r“ a spustil tento skript příkazem „python3 jméno.py“. Ovládajíc pouze obsah komentáře, účastník měl za úkol vytáhnout řetězec ze souboru "/home/ctfuser/flag". Skript byl vytvořen tímto kódem: comment = input(„> „).replace(„\n“, „“).replace(„\r“, „“) code = f"""print(„ahoj světe!“) # To je komentář. Tady je další: # {comment} print(„Děkujeme za hru!“)"""
Místo „{comment}“ byla vložena data, která účastník zaslal, a nakonec se spustil následující kód: print(„ahoj světe!“) # To je komentář. Tady je další: # Data zaslaná účastníkem soutěže print(„Děkujeme za hru!“)
Úkol byl vytvořen na motivy zranitelnosti v parseru CPython, který zpracovával znak s nulovým kódem jako ukončení řetězce (zranitelnost mohla být například použita k maskování škodlivých akcí v textu komentáře). Problém byl vyřešen ve verzích CPython 3.12.0 a 3.11.4. V použitém zpracovateli soutěže byly odstraněny pouze znaky „\n“ a „\r“, ale při použití zranitelné verze CPython mohl účastník použít znak „\0“ jako oddělovač. Nicméně tento trik nevyšel, protože v soutěži byla použita již opravena verze CPython, s předpokladem, že v parseru mohou zůstat další podobné chyby, které by účastníci mohli odhalit.
Účastník, který úspěšně dokončil úkol, se nerozhodl hledat nové zranitelnosti v parseru, které by umožnily rozdělit řetězec na části, ale využil specifika provádění souborů v Pythonu podle jejich obsahu. Například místo původního kódu lze do souboru s příponou '.py' umístit předem zkompilovaný bajtkód, který je uchováván v souborech s příponou '.pyc', a takový soubor bude vykonán. V tomto soutěžním úkolu mohl účastník kontrolovat pouze obsah uvnitř souboru, takže nemohl přidat svůj vlastní záhlaví pro zkreslení typu MIME.
Úkol se podařilo vyřešit pomocí toho, že Python od verze 2.6 může vykonávat obsah ZIP archivů pro dodávání Python balíčků ve zkomprimované podobě. Stejně jako v případě bajtkódu určuje přítomnost zip archivu obsah, nikoliv příponu souboru, tj. do 'súbor.py' lze umístit zip archiv a při spuštění příkazem 'python súbor.py' bude zpracován jako zkomprimovaný Python balíček. Přitom jsou ZIP archivy v Pythonu indexovány nikoli na základě záhlaví na začátku souboru, ale podle sekce EOCD (End of Central Directory Record) na konci souboru. V případě, že archív obsahuje soubor '__main__.py', tento soubor se automaticky spustí při přímém spuštění archivu příkazem 'python archív'.
Soutěžní úkol byl vyřešen generováním podobného ZIP archivu a jeho vložením do textu komentáře. Pro zachování správnosti struktury souboru za podmínek přítomnosti výzvy 'print(„Thanks for playing!“)' na konci původního souboru byla využita přítomnost oblasti komentáře v sekci EOCD umístěné na samém konci.

Zdroj: opennet.ru
