En af deltagerne i UIUCTF 2025-konkurrencen gennemgik detaljeret, hvordan han lykkedes med at udføre en opgave, der krævede, at han fik sin kode til at køre på serveren, kun ved at ændre indholdet af en kommentar i koden.
Deltagerne kunne sende et netværksanmodning til et Python-script, der oprettede et nyt Python-script med et tilfældigt navn, tilføjede de modtagne data fra brugeren til kommentarens tekst ved at fjerne tegnene "\n" og "\r", og kørte dette script med kommandoen "python3 navn.py". Ved kun at kontrollere indholdet af kommentaren skulle deltageren udtrække en streng fra filen "\/home\/ctfuser\/flag". Scriptet blev oprettet med følgende kode: comment = input("> ").replace("\n", "").replace("\r", "") code = f"""print("hello world!") # Dette er en kommentar. Her er en anden: # {comment} print("Thanks for playing!")"""
I stedet for "{comment}" blev de modtagne data fra deltageren indsat, hvilket resulterede i, at følgende kode blev kørt: print("hello world!") # Dette er en kommentar. Her er en anden: # Data modtaget fra deltageren i konkurrencen print("Thanks for playing!")
Opgaven var baseret på en sårbarhed i CPython-parseren, der behandlede null-tegn som en afslutning af linjen (sårbarheden kunne for eksempel udnyttes til at skjule ondsindede handlinger i kommentarens tekst). Problemet blev løst i versionerne CPython 3.12.0 og 3.11.4. I den version, der blev brugt i konkurrencen, blev kun tegnene "\n" og "\r" skåret væk, men ved at bruge en sårbar version af CPython kunne deltageren bruge tegnet "\0" som separator. Alligevel lykkedes dette trick ikke, da den rette version af CPython blev brugt i konkurrencen med den forventning, at der stadig kunne være lignende fejl i parseren, og at deltagerne ville kunne opdage dem.
Deltageren, der lykkedes med opgaven, forsøgte ikke at finde nye sårbarheder i parseren, der kunne dele strengen i dele, men udnyttede Python-filer, der udføres baseret på deres indhold. For eksempel kan den cachede bytekode gemmes i filer med filtypen "pyc" i stedet for den originale kode i "py"-filen, og en sådan fil vil blive udført. I denne konkurrence kunne deltageren kun kontrollere indholdet midt i filen, derfor kunne han ikke tilføje sin egen header for at forvrænge MIME-typen.
Opgaven blev løst ved at udnytte, at Python fra version 2.6 kan udføre indholdet af ZIP-arkiver for at levere Python-pakker i komprimeret form. Ligesom med bytecode-cachen bestemmes tilstedeværelsen af zip-arkivet af indholdet og ikke filens udvidelse, dvs. man kan placere et zip-arkiv i "fil.py", og når man kører det med kommandoen "python fil.py", vil det blive behandlet som en komprimeret Python-pakke. Desuden indekseres ZIP-arkiver i Python ikke efter headeren i filens begyndelse, men efter EOCD-sektionen (End of Central Directory Record) i slutningen af filen. Hvis arkivet indeholder filen "__main__.py", vil denne fil blive kørt automatisk ved direkte at køre arkivet med kommandoen "python arkiv".
Konkurrenceopgaven blev løst ved at generere et lignende ZIP-arkiv og indsætte det i tekstkommentaren. For at bevare filens strukturelle integritet i tilfælde af, at der i slutningen af den oprindelige fil var et kald til 'print("Thanks for playing!")', blev der benyttet et kommentarområde i EOCD-sektionen, som blev anbragt i slutningen.

Kilde: opennet.ru
