mind blowers
TJCTF 2026
Task: Python pickle deserialization server with RestrictedUnpickler that whitelists builtins module and blocklists dangerous names (eval, exec, __import__, open). Solution: getattr is not blocked — chain through license.__class__.__init__.__globals__ to recover __import__, import os, and execute commands via os.popen.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
mind blowers — TJCTF 2026
Description
Rick has open sourced his mind blowers program! Now you can upload your own mind blowers and view them! Don't upload any malicious mind blowers!
Connection: nc tjc.tf 31422 Source file: server.py
A Python server accepts base64-encoded pickle data and deserializes it using a RestrictedUnpickler. The goal is to bypass the restrictions and achieve remote code execution to read the flag.
Analysis
RestrictedUnpickler
The server implements a custom RestrictedUnpickler with two layers of defense:
- Module whitelist: Only
builtinsmodule is allowed — any other module infind_classraises an error - Name blocklist: Specific dangerous builtins are blocked:
BLOCKED_NAMES = { "eval", "exec", "compile", "__import__", "open", "breakpoint", "input", "exit", "quit", }
class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module != "builtins": raise pickle.UnpicklingError("banned") if name in BLOCKED_NAMES: raise pickle.UnpicklingError("blocked") return super().find_class(module, name)
Critical Oversight: getattr is Not Blocked
The blocklist only covers 9 names. Crucially, getattr is not among them. This is the key vulnerability because:
- The blocklist only applies to pickle's
find_classmethod, which is invoked byGLOBAL/STACK_GLOBALopcodes during deserialization - Once we have
builtins.getattras a callable, we can use pickle'sREDUCEopcode to call it at runtime - Runtime
getattrcalls do not go throughfind_class— they are normal Python attribute lookups - This means we can access
__import__viagetattreven though it's blocked infind_class
Object Graph Traversal Chain
Starting from builtins.license (a _sitebuiltins._Printer instance available in the builtins namespace), we can traverse the Python object graph to reach __import__:
...
$ grep --similar
Similar writeups
- [misc][free]mind-blasters— TJCTF 2026
- [misc][free]build-a-builtin-revenge— b01lersc
- [web][Pro]Соленый огурец (Salty Pickle)— hackerlab
- [web][Pro]Lab 13 — WebForge — Insecure Deserialization in Config Import— hackadvisor
- [misc][Pro]Ergastulum— 0xl4ugh