Developer Executes Code on Server by Modifying Only Python Script Comments

A participant of the information security competition UIUCTF 2025 detailed in a technical blog how they successfully completed a task that required executing arbitrary code on a server, using only the ability to modify the content of a comment within a Python script.

According to information from OpenNET, the challenge involved sending a network request to a Python script that generated a new Python file with a random name, added user input data as comments while removing the characters «\n» and «\r», and executed this script with the command «python3 filename.py». By controlling only the comment content, the participant aimed to extract a string from the file «/home/ctfuser/flag». The script was created with the following code:

The data provided by the participant replaced «{comment}», leading to the execution of the following code:

The challenge was inspired by a vulnerability in the CPython parser that treated the null character as a line terminator (this vulnerability could be exploited to conceal malicious activity within comment text). The issue was fixed in CPython releases 3.12.0 and 3.11.4. In the event’s script handler, only the «\n» and «\r» characters were removed, but a participant could use the «\0» character as a delimiter in vulnerable versions of CPython. However, this trick did not succeed because the competition utilized a patched version of CPython, anticipating that similar errors could remain in the parser that participants might discover.

The developer refrained from searching for new parser vulnerabilities that could split strings and instead leveraged the content-based execution feature of Python files. For example, it is possible to place cached bytecode in a file with a «.py» extension instead of source code, and such a file will be executed. In this competition, the participant could only control content within the body of the file and could not add a header to alter the MIME type.

The task was ultimately solved by utilizing the fact that since version 2.6, Python can execute the contents of ZIP archives for delivering Python packages in compressed form. Like the bytecode cache, a ZIP archive’s validity is determined by its contents rather than by file extension; thus, one can place a ZIP archive into a «file.py» and execute it with the command “python file.py”, where it will be treated as a compressed Python package. Furthermore, ZIP archives in Python are indexed not by a header at the beginning of the file but by the EOCD (End of Central Directory Record) section at the end of the file. If a “__main__.py” file is present in the archive, it runs automatically when the archive is executed directly with the command “python archive”.

The competition challenge was solved by generating a similar ZIP archive and embedding it into the comment text. To maintain the integrity of the file structure given the presence of the ‘print(“Thanks for playing!”)’ call at the end of the original file, the comment area in the EOCD section, which is placed at the very end, was utilized.