LOAD_NAME / LOAD_CONST opcode OOB Read
Last updated
Last updated
Naučite i vežbajte hakovanje AWS:HackTricks Obuka AWS Crveni Tim Stručnjak (ARTE) Naučite i vežbajte hakovanje GCP-a: HackTricks Obuka GCP Crveni Tim Stručnjak (GRTE)
Ove informacije su preuzete iz ovog teksta.
Možemo koristiti OOB čitanje funkcionalnost u LOAD_NAME / LOAD_CONST opcode-u da bismo dobili neki simbol u memoriji. To znači korišćenje trika poput (a, b, c, ... stotine simbola ..., __getattribute__) if [] else [].__getattribute__(...)
da bismo dobili simbol (kao što je ime funkcije) koji želite.
Zatim samo kreirajte svoj eksploit.
Izvorni kod je prilično kratak, sadrži samo 4 linije!
Možete uneti proizvoljni Python kod, i biće kompajliran u Python objekat koda. Međutim, co_consts
i co_names
tog objekta koda će biti zamenjeni praznim tuplom pre nego što se taj objekat koda izvrši.
Na taj način, svi izrazi koji sadrže konstante (npr. brojeve, stringove itd.) ili imena (npr. promenljive, funkcije) mogu izazvati grešku segmentacije na kraju.
Kako dolazi do greške segmentacije?
Počnimo sa jednostavnim primerom, [a, b, c]
može se kompajlirati u sledeći bajtkod.
Ali šta ako postane prazan torka co_names
? LOAD_NAME 2
opcode se i dalje izvršava, i pokušava da pročita vrednost sa adrese memorije na kojoj je originalno trebalo da bude. Da, ovo je "funkcija" čitanja van granica.
Osnovna ideja za rešenje je jednostavna. Neki opcode-ovi u CPython-u, na primer LOAD_NAME
i LOAD_CONST
, su ranjivi (?) na OOB čitanje.
Oni dobavljaju objekat sa indeksom oparg
iz torki consts
ili names
(to je kako su co_consts
i co_names
nazvani ispod haube). Možemo se pozvati na sledeći kratak isječak o LOAD_CONST
da vidimo šta CPython radi kada obrađuje LOAD_CONST
opcode.
Na ovaj način možemo koristiti OOB funkciju da dobijemo "ime" sa proizvoljnog memorijskog ofseta. Da biste bili sigurni koje ime ima i koji je ofset, jednostavno nastavite da pokušavate LOAD_NAME 0
, LOAD_NAME 1
... LOAD_NAME 99
... I možete pronaći nešto oko oparg > 700. Takođe možete pokušati da koristite gdb da pogledate raspored memorije, naravno, ali ne mislim da bi bilo lakše?
Kada dobijemo korisne ofsete za imena / konstante, kako dobijamo ime / konstantu sa tog ofseta i koristimo je? Evo trika za vas:
Pretpostavimo da možemo dobiti ime __getattribute__
sa ofseta 5 (LOAD_NAME 5
) sa co_names=()
, onda samo uradite sledeće stvari:
Primetite da nije potrebno nazvati ga kao
__getattribute__
, možete ga nazvati nečim kraćim ili čudnijim
Razlog možete razumeti samo gledajući njegov bajtkod:
Primetite da LOAD_ATTR
takođe dobavlja ime iz co_names
. Python učitava imena sa istog ofseta ako je ime isto, tako da se drugi __getattribute__
i dalje učitava sa ofsetom=5. Koristeći ovu funkciju možemo koristiti proizvoljno ime jednom kada je ime u memoriji u blizini.
Za generisanje brojeva trebalo bi da bude trivijalno:
0: not [[]]
1: not []
2: (not []) + (not [])
...
Nisam koristio konstante zbog ograničenja dužine.
Prvo, evo skripte koja nam pomaže da pronađemo te ofsete imena.
I sledeće je za generisanje pravog Python eksploata.
To uglavnom radi sledeće stvari, za one stringove koje dobijemo iz metode __dir__
:
Učite i vežbajte hakovanje AWS-a: HackTricks Training AWS Red Team Expert (ARTE) Učite i vežbajte hakovanje GCP-a: HackTricks Training GCP Red Team Expert (GRTE)