LOAD_NAME / LOAD_CONST opcode OOB Read
Last updated
Last updated
AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE) GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Bu bilgi bu yazıdan alındı.
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.
Sonra sadece istismarınızı oluşturun.
Kaynak kod oldukça kısa, sadece 4 satır içeriyor!
Arbitrary Python kodu girebilirsiniz ve bu, bir Python kod nesnesi olarak derlenecektir. Ancak, bu kod nesnesinin co_consts
ve co_names
boş bir demet ile eval edilmeden önce değiştirilecektir.
Bu şekilde, tüm ifadeler sabitler (örneğin, sayılar, dizeler vb.) veya isimler (örneğin, değişkenler, fonksiyonlar) içeriyorsa, sonunda segmentasyon hatasına neden olabilir.
Segfault nasıl meydana gelir?
Basit bir örnekle başlayalım, [a, b, c]
aşağıdaki bytecode'a derlenebilir.
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 read "özelliği".
Çözümün temel konsepti basittir. CPython'daki bazı opcode'lar, örneğin LOAD_NAME
ve LOAD_CONST
, OOB read'e karşı savunmasızdır (?).
Bir nesneyi consts
veya names
demetinden oparg
indeksinden alırlar (arka planda buna co_consts
ve co_names
denir). CPython'un LOAD_CONST
opcode'unu işlerken ne yaptığını görmek için LOAD_CONST
hakkında aşağıdaki kısa kesiti inceleyebiliriz.
Bu şekilde OOB özelliğini kullanarak rastgele bellek ofsetinden bir "isim" alabiliriz. Hangi isme sahip olduğunu ve ofsetinin ne olduğunu öğrenmek için, LOAD_NAME 0
, LOAD_NAME 1
... LOAD_NAME 99
... denemeye devam edin. Yaklaşık oparg > 700'de bir şey bulabilirsiniz. Elbette gdb kullanarak bellek düzenine de bakmayı deneyebilirsiniz, ama bunun daha kolay olacağını düşünmüyorum?
Bu yararlı ofsetleri isimler / sabitler için aldıktan sonra, o ofsetten bir isim / sabit nasıl alır ve kullanırız? İşte sizin için 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:
Dikkat edin ki, bunu
__getattribute__
olarak adlandırmak zorunda değilsiniz, daha kısa veya daha garip bir isim verebilirsiniz.
Bunun arkasındaki nedeni sadece bytecode'unu görüntüleyerek anlayabilirsiniz:
LOAD_ATTR
'ın da 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 oldukça basit olmalıdır:
0: not [[]]
1: not []
2: (not []) + (not [])
...
Uzunluk sınırı nedeniyle consts kullanmadım.
İlk olarak, bu isimlerin offset'lerini bulmamız için bir script.
Ve aşağıdaki gerçek Python istismarını oluşturmak içindir.
Temelde şu işlemleri yapar, bu dizeleri __dir__
yönteminden alırız:
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE) GCP Hacking'i öğrenin ve pratik yapın: HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)