LOAD_NAME / LOAD_CONST opcode OOB Read
Last updated
Last updated
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ove informacije su preuzete iz ovog izveštaja.
Možemo koristiti OOB read funkciju u LOAD_NAME / LOAD_CONST opcode da dobijemo neki simbol u memoriji. Što znači korišćenje trika kao što je (a, b, c, ... stotine simbola ..., __getattribute__) if [] else [].__getattribute__(...)
da dobijemo simbol (kao što je ime funkcije) koji želite.
Zatim samo kreirajte svoj exploit.
Izvorni kod je prilično kratak, sadrži samo 4 linije!
Možete uneti proizvoljni Python kod, i on će biti kompajliran u Python kod objekat. Međutim, co_consts
i co_names
tog kod objekta će biti zamenjeni praznom tupelom pre nego što se eval-uju taj kod objekat.
Tako da na ovaj način, sve izraze koji sadrže konstante (npr. brojevi, stringovi itd.) ili imena (npr. promenljive, funkcije) mogu na kraju izazvati segmentacijski grešku.
Kako se dešava segfault?
Hajde da počnemo sa jednostavnim primerom, [a, b, c]
bi mogao da se kompajlira u sledeći bajtkod.
Али шта ако co_names
постане празан кортеж? LOAD_NAME 2
опкод се и даље извршава и покушава да прочита вредност са те адресе у меморији са које је првобитно требало. Да, ово је "карактеристика" читања ван граница.
Основна концепција решења је једноставна. Неки опкодови у CPython, на пример LOAD_NAME
и LOAD_CONST
, су подложни (?) OOB читању.
Они преузимају објекат из индекса oparg
из consts
или names
кортежа (то су co_consts
и co_names
под хаубом). Можемо се позвати на следећи кратак исечак о LOAD_CONST
да видимо шта CPython ради када обрађује LOAD_CONST
опкод.
Na ovaj način možemo koristiti OOB funkciju da dobijemo "ime" iz proizvoljnog memorijskog ofseta. Da bismo bili sigurni koje ime ima i koji je njegov ofset, samo nastavite da pokušavate LOAD_NAME 0
, LOAD_NAME 1
... LOAD_NAME 99
... I mogli biste pronaći nešto u vezi sa oparg > 700. Takođe možete pokušati da koristite gdb da pogledate raspored memorije, naravno, ali ne mislim da bi to bilo lakše?
Kada dobijemo te korisne ofsete za imena / konstante, kako dobijamo ime / konstantu iz tog ofseta i koristimo je? Evo jednog trika za vas:
Pretpostavimo da možemo dobiti __getattribute__
ime iz ofseta 5 (LOAD_NAME 5
) sa co_names=()
, onda samo uradite sledeće:
Обратите пажњу да није неопходно да се назива
__getattribute__
, можете га назвати нечим краћим или чуднијим
Можете разумети разлог иза тога једноставним прегледом његовог байткода:
Napomena da LOAD_ATTR
takođe preuzima ime iz co_names
. Python učitava imena sa iste pozicije ako je ime isto, tako da se drugi __getattribute__
i dalje učitava sa offset=5. Koristeći ovu funkciju možemo koristiti proizvoljno ime kada je ime u memoriji u blizini.
Za generisanje brojeva bi trebalo da bude trivijalno:
0: ne [[]]
1: ne []
2: (ne []) + (ne [])
...
Nisam koristio konstante zbog ograničenja dužine.
Prvo, ovde je skripta za pronalaženje tih offset-a imena.
А следеће је за генерисање правог Python експлоита.
U suštini, to radi sledeće stvari, za te stringove dobijamo ih iz __dir__
metode:
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)