Control-flow integritás

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2022. május 6-án felülvizsgált verziótól ; az ellenőrzések 3 szerkesztést igényelnek .

A Control-flow integrity ( CFI ) a számítógépes biztonsági technikák általános elnevezése, amelynek célja a programvégrehajtás lehetséges útvonalainak korlátozása egy előre megjósolt vezérlőfolyamat gráfon belül a biztonság növelése érdekében [1] . A CFI megnehezíti a támadó számára, hogy átvegye a program végrehajtásának irányítását, mivel lehetetlenné teszi a gépi kód már meglévő részeinek újrafelhasználását. Hasonló technikák közé tartozik a kódmutató szétválasztás (CPS) és a kódmutató integritása (CPI) [2] [3] .

A CFI-támogatás megtalálható a Clang [4] és a GCC [5] fordítóprogramokban , valamint a Control Flow Guard [6] és Return Flow Guard [7] a Microsofttól, valamint a Reuse Attack Protector [8] a PaX csapattól.

Történelem

A tetszőleges kódok végrehajtása elleni védekezési módok feltalálása, mint például az adatvégrehajtás megakadályozása és az NX-bit , olyan új módszerek megjelenéséhez vezetett, amelyek lehetővé teszik a program feletti irányítás megszerzését (például visszatérés-orientált programozás ) . 8] . 2003-ban a PaX Team közzétett egy dokumentumot, amelyben leírja a program feltöréséhez vezető lehetséges helyzeteket, és ötleteket az ellenük való védekezésre [8] [9] . 2005-ben a Microsoft kutatóinak egy csoportja formalizálta ezeket az elképzeléseket, és megalkotta a Control-flow integritás kifejezést, amely a program eredeti vezérlőfolyamatának változásaival szembeni védelem módszereire utal. Ezen túlmenően a szerzők egy módszert javasoltak a már lefordított gépi kódok műszerezésére [1] .

Ezt követően a kutatók a CFI ötlete alapján számos különféle módot javasoltak a program támadásokkal szembeni ellenállásának növelésére. A leírt megközelítéseket nem alkalmazták széles körben, többek között a programok jelentős lelassulása vagy a további információk iránti igény miatt (például profilalkotás révén ) [10] .

2014-ben a Google kutatóinak egy csoportja publikált egy tanulmányt, amely a CFI megvalósítását vizsgálta a GCC és az LLVM ipari fordítók számára a C++ programok műszerezésére. A hivatalos CFI támogatást 2014-ben a GCC 4.9.0 [5] [11] , 2015-ben pedig a Clang 3.7 [12] [13] adta . A Microsoft 2014-ben kiadta a Control Flow Guard programot Windows 8.1 -hez, amely az operációs rendszer támogatásával egészítette ki a Visual Studio 2015-öt [6] .

Leírás

Ha a programkódban közvetett ugrások vannak , akkor lehetséges, hogy a vezérlést bármilyen címre át lehet vinni, ahol a parancs található (például x86 -on ez bármilyen lehetséges cím lesz, mivel a parancs minimális hossza egy bájt [14] ). Ha egy támadó valamilyen módon módosítani tudja azt az értéket, amellyel az irányítást átadja egy ugrásutasítás végrehajtásakor, akkor a meglévő programkódot újra felhasználhatja saját igényeire.

A valós programokban a nem lokális ugrások általában a függvények elejéhez vezetnek (például eljáráshívási utasítás használata esetén), vagy a hívó utasítást követő utasításhoz (procedure return). Az átmenetek első típusa egy közvetlen (angolul forward-edge ) átmenet, mivel azt a vezérlőfolyamat grafikonon egy közvetlen ív jelöli. A második típust vissza (eng. back-edge ) átmenetnek nevezzük, az elsőhöz hasonlóan - az átmenetnek megfelelő ív fordított lesz [15] .

Közvetlen átmenetek

Közvetlen ugrások esetén a lehetséges címek száma, amelyekre a vezérlés átvihető, megfelel a programban lévő funkciók számának. Továbbá, ha figyelembe vesszük a forráskódot tartalmazó programozási nyelv típusrendszerét és szemantikáját, további korlátozások is lehetségesek [16] . Például a C++ nyelvben egy helyes programban az indirekt hívásban használt függvénymutatónak tartalmaznia kell egy olyan függvény címét, amelynek típusa megegyezik a mutató típusával [17] .

A vezérlés-folyamat integritás megvalósításának egyik módja a közvetlen ugrásoknál az, hogy elemezheti a programot, és meghatározhatja a különböző ági utasítások jogi címeinek halmazát [1] . Egy ilyen halmaz felépítéséhez általában statikus kódelemzést alkalmaznak az absztrakció valamilyen szintjén ( forráskód , az analizátor belső reprezentációja vagy gépi kód [1] [10] ). Ezután a kapott információ felhasználásával a közvetett elágazás utasításai mellé kód kerül beillesztésre, hogy ellenőrizze, hogy a futás közben kapott cím megegyezik-e a statikusan számított címmel. Divergencia esetén a program általában összeomlik, bár az implementációk lehetővé teszik a viselkedés testreszabását az előre jelzett vezérlési folyamat megsértése esetén [18] [19] . Így a vezérlőfolyamat gráf csak azokra az élekre (függvényhívásokra) és csúcsokra (függvénybelépési pontokra) korlátozódik [1] [16] [20] , amelyek kiértékelésre kerülnek a statikus elemzés során, tehát amikor megpróbáljuk módosítani az indirekt ugráshoz használt mutatót. , a támadó megbukik.

Ezzel a módszerrel megakadályozható az ugrásorientált programozás [21] és a hívásorientált programozás [22] , mivel az utóbbiak aktívan használnak direkt indirekt ugrásokat.

Fordított átmenetek

A visszafelé történő átmenetekhez a CFI megvalósításának többféle megközelítése lehetséges [8] .

Az első megközelítés ugyanazokon a feltételezéseken alapul, mint a CFI a közvetlen ugrások esetében, vagyis azon, hogy egy függvényből ki lehet számítani a visszatérési címeket [23] .

A második megközelítés a visszaküldési cím speciális kezelése. Amellett, hogy egyszerűen elmentjük a verembe , egy speciálisan erre kijelölt helyre (például a processzor regisztereinek egyikébe) is elmentésre kerül, esetleg némi módosítással. Ezenkívül a return utasítás előtt egy kód kerül hozzáadásra, amely visszaállítja a visszatérési címet, és összeveti a veremben lévővel [8] .

A harmadik megközelítés további támogatást igényel a hardvertől. A CFI-vel együtt árnyékveremet használnak - egy speciális, a támadó számára elérhetetlen memóriaterületet, amelybe a függvények hívásakor visszatérési címek kerülnek tárolásra [24] .

A visszaugrásos CFI-sémák implementálásakor lehetőség nyílik a könyvtárba visszatérő támadások megelőzésére, valamint a verem visszatérési címének megváltoztatásán alapuló visszatérés - orientált programozásra [ 23] .

Példák

Ebben a részben a vezérlés-folyamat integritás-megvalósításainak példáit tekintjük át.

Clang Indirekt függvényhívás ellenőrzése

A közvetett függvényhívás-ellenőrzés (IFCC) magában foglalja a program közvetett ugrásainak ellenőrzését, néhány "speciális" ugrás kivételével, mint például a virtuális függvényhívások. Amikor olyan címkészletet állítunk össze, amelyre áttérés történhet, figyelembe veszi a függvény típusát. Ennek köszönhetően nemcsak a hibás értékek használata, amelyek nem mutatnak a funkció elejére, megakadályozható, hanem a forráskódban a hibás típusú öntvény is. Az ellenőrzések engedélyezéséhez a fordítóban van egy opció -fsanitize=cfi-icall[4] .

// clang-ifcc.c #include <stdio.h> int összeg ( int x , int y ) { vissza x + y _ } int dbl ( int x ) { return x + x ; } void call_fn ( int ( * fn )( int )) { printf ( "Eredmény értéke: %d \n " , ( * fn )( 42 )); } void erase_type ( void * fn ) { // A viselkedés definiálatlan, ha az fn dinamikus típusa nem egyezik meg az int-vel (*)(int). call_fn ( fn ); } int main () { // Az erase_type meghívásakor a statikus típusinformáció elveszik. erase_type ( összeg ); return 0 ; }

Az ellenőrzések nélküli programok hibaüzenetek nélkül fordulnak le, és meghatározatlan eredménnyel futnak, amely futásonként változik:

$ clang -Wall -Wextra clang-ifcc.c $ ./a.out Eredmény értéke: 1388327490

A következő opciókkal összeállítva egy olyan programot kapunk, amely a call_fn meghívásakor leáll.

$ clang -flto -fvisibility=rejtett -fsanitize=cfi -fno-sanitize-trap=all clang-ifcc.c $ ./a.out clang-ifcc.c:12:32: futásidejű hiba: az „int (int)” típusú vezérlőfolyam integritásának ellenőrzése sikertelen volt a közvetett függvényhívás során (./a.out+0x427a20): megjegyzés: (ismeretlen) itt van megadva

Clang Forward-Edge CFI virtuális hívásokhoz

Ez a módszer a virtuális hívások integritásának ellenőrzésére irányul a C++ nyelven. Minden egyes virtuális függvényeket tartalmazó osztályhierarchiához bittérképek készülnek, amelyek megmutatják, hogy mely függvények hívhatók meg az egyes statikus típusokhoz. Ha a programban való végrehajtás során bármely objektum virtuális függvényeinek táblázata megsérül (például hibás típus , ami ledobja a hierarchiát, vagy egyszerűen memóriasérülés a támadó által), akkor az objektum dinamikus típusa nem fog megegyezni a statikusan előrejelzett értékekkel. [10] [25] .

// virtual-calls.cpp #include <cstdio> struct B { virtuális void foo () = 0 ; virtuális ~ B () {} }; struct D : public B { void foo () override { printf ( "Jobb függvény \n " ); } }; struct Rossz : public B { void foo () override { printf ( "Rossz függvény \n " ); } }; int main () { Rossz rossz ; // A C++ szabvány így engedélyezi az öntést: B & b = static_cast < B &> ( rossz ); // Származtatott1 -> Alap -> Származtatott2. D & normál = static_cast < D &> ( b ); // Ennek eredményeként az objektum dinamikus típusa normál normál . foo (); // rossz lesz, és rossz függvény lesz meghívva. return 0 ; }

Az ellenőrzések engedélyezése nélküli fordítás után:

$ clang++ -std=c++11 virtual-calls.cpp $ ./a.out Hibás funkció

A programban ahelyett, hogy az osztály foomegvalósítását Da . Ezt a problémát akkor fogja észlelni, ha a programot a következővel fordítja : fooBad-fsanitize=cfi-vcall

$ clang++ -std=c++11 -Wall -flto -fvisibility=rejtett -fsanitize=cfi-vcall -fno-sanitize-trap=all virtual-calls.cpp $ ./a.out virtual-calls.cpp:24:3: futásidejű hiba: a „D” típusú vezérlőfolyam integritásának ellenőrzése nem sikerült a virtuális hívás során (vtable cím: 0x000000431ce0) 0x000000431ce0: Megjegyzés: A vtable "rossz" típusú 00 00 00 00 30 a2 42 00 00 00 00 00 e0 a1 42 00 00 00 00 00 60 a2 42 00 00 00 00 00 00 00 00 00 ^

Jegyzetek

  1. ↑ 1 2 3 4 5 Martín Abadi, Mihai Budiu, Úlfar Erlingsson, Jay Ligatti. Control-flow integritás  // A 12. ACM Computer and Communication Security konferencia előadásai. - New York, NY, USA: ACM, 2005. - S. 340-353 . — ISBN 1595932267 . - doi : 10.1145/1102120.1102165 .
  2. Volodimir Kuznyecov, Szekeres László, Mathias Payer, George Candea, R. Sekar. Code-pointer integritás  // A 11. USENIX konferencia előadásai az operációs rendszerek tervezéséről és megvalósításáról. - Berkeley, CA, USA: USENIX Association, 2014. - P. 147-163 . — ISBN 9781931971164 .
  3. A CFI, CPS és CPI tulajdonságok  közötti különbségekről . nebelwelt.net. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 22..
  4. ↑ 1 2 Control Flow Integrity - Clang 5 dokumentáció . releases.llvm.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 23.
  5. ↑ 1 2 vtv - GCC Wiki . gcc.gnu.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. július 11.
  6. 1 2 Flow Guard vezérlése (Windows  ) . msdn.microsoft.com. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 22..
  7. ↑ Return Flow Guard - Tencent 's Xuanwu Lab  . xlab.tencent.com. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 23.
  8. ↑ 1 2 3 4 5 grbiztonság  . _ www.grsecurity.net. Letöltve: 2017. december 22. Az eredetiből archiválva : 2018. február 17.
  9. [1] Archivált 2017. augusztus 5. a Wayback Machine PaX future -ben
  10. ↑ 1 2 3 Caroline Tice, Tom Roeder, Peter Collingbourne, Stephen Checkoway, Úlfar Erlingsson. Forward-edge Control-flow integritás érvényesítése a GCC-ben és az LLVM-ben  // Proceedings of the 23rd USENIX Conference on Security Symposium. - Berkeley, CA, USA: USENIX Association, 2014. - S. 941-955 . — ISBN 9781931971157 .
  11. GCC 4.9 Release Series - GNU Project - Free Software Foundation (FSF  ) . gcc.gnu.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2018. január 15.
  12. Clang 3.7 Kiadási megjegyzések — Clang 3.7 dokumentáció . releases.llvm.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. november 26..
  13. LLVM kiadások . releases.llvm.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 15.
  14. Intel® 64 és IA-32 architektúrák szoftverfejlesztői kézikönyvei | Intel®  szoftver . software.intel.com. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 25..
  15. Biztonság – WebAssembly . webassembly.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 23.
  16. ↑ 1 2 Aho, Alfred W.; Seti, Ravi; Ullman, Jeffrey D. Fordítók – Alapelvek, Technológiák, Eszközök, 2. kiadás . - Williams. - 2008. - S.  1062 -1066. - ISBN 978-5-8459-1349-4 .
  17. ISO/IEC 14882:2014 - Információtechnológia - Programozási nyelvek - C++ . – ISO . - 2014. - P. 105. Archív másolat 2016. április 29-én a Wayback Machine -nél
  18. Vtable Verification – Felhasználói kézikönyv . docs.google.com. Letöltve: 2017. december 22. Az eredetiből archiválva : 2019. június 12.
  19. Control Flow Integrity - Clang 5 dokumentáció . releases.llvm.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 23.
  20. Muchnick, Steven S. Advanced Compiler Design and Implementation . - Morgan Kaufmann Publishers , 1997. - S.  609-618 . - ISBN 1-55860-320-4 .
  21. Tyler Bletsch, Xuxian Jiang, Vince W. Freeh, Zhenkai Liang. Ugrásorientált programozás: A kód újrafelhasználásának új osztálya  // Az információs, számítógépes és kommunikációs biztonságról szóló 6. ACM szimpózium előadásai. - New York, NY, USA: ACM, 2011. - P. 30-40 . — ISBN 9781450305648 . - doi : 10.1145/1966913.1966919 .
  22. AliAkbar Sadeghi, Salman Niksefat, Maryam Rostamipour. Pure-Call Oriented Programming (PCOP): a kütyük láncolása hívási utasítások segítségével  //  Journal of Computer Virology and Hacking Techniques. — 2017-05-15. - P. 1-18 . — ISSN 2263-8733 . - doi : 10.1007/s11416-017-0299-1 . Archiválva az eredetiből 2017. december 22-én.
  23. ↑ 1 2 RAP: RIP ROP - Attack Protector újrafelhasználása (downlink) . PaX csapat . Letöltve: 2017. december 22. Az eredetiből archiválva : 2020. május 20. 
  24. Control-flow Enforcement Technology előnézet . Intel fejlesztői zóna . Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. augusztus 14..
  25. Control Flow Integrity tervezési dokumentáció – Clang 5 dokumentáció . releases.llvm.org. Letöltve: 2017. december 22. Az eredetiből archiválva : 2017. december 23.

Irodalom

Könyvek Cikkek

Linkek