Kontrollflödesintegritet

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 6 maj 2022; kontroller kräver 3 redigeringar .

Kontrollflödesintegritet ( CFI ) är ett allmänt namn för datorsäkerhetstekniker som syftar till att begränsa de möjliga vägarna för programexekvering inom en förutspådd kontrollflödesgraf för att öka dess säkerhet [1] . CFI gör det svårare för en angripare att ta kontroll över exekveringen av ett program genom att göra det omöjligt för vissa sätt att återanvända redan befintliga delar av maskinkoden. Liknande tekniker inkluderar kodpekarseparation (CPS) och kodpekarintegritet (CPI) [2] [3] .

CFI-stöd finns i Clang [4] och GCC [5] kompilatorer , såväl som Control Flow Guard [6] och Return Flow Guard [7] från Microsoft och Reuse Attack Protector [8] från PaX Team.

Historik

Uppfinningen av sätt att skydda mot exekvering av godtycklig kod, såsom Data Execution Prevention och NX-bit , har lett till uppkomsten av nya metoder som låter dig få kontroll över programmet (till exempel returorienterad programmering ) [ 8] . 2003 publicerade PaX Team ett dokument som beskrev möjliga situationer som leder till hacking av programmet, och idéer för att skydda mot dem [8] [9] . 2005 formaliserade en grupp Microsoft-forskare dessa idéer och myntade termen Control-flow Integrity för att hänvisa till metoder för att skydda mot ändringar i ett programs ursprungliga kontrollflöde. Utöver detta föreslog författarna en metod för instrumentering av redan kompilerad maskinkod [1] .

Därefter föreslog forskare, baserat på idén om CFI, många olika sätt att öka programmets motståndskraft mot attacker. De beskrivna tillvägagångssätten har inte använts i stor utsträckning av skäl, inklusive stora programnedgångar eller behovet av ytterligare information (till exempel erhållen genom profilering ) [10] .

2014 publicerade ett team av forskare från Google en artikel som tittade på implementeringen av CFI för industriella kompilatorer GCC och LLVM för instrumentering av C++-program. Officiellt CFI-stöd lades till 2014 i GCC 4.9.0 [5] [11] och 2015 i Clang 3.7 [12] [13] . Microsoft släppte Control Flow Guard 2014 för Windows 8.1 och lade till stöd från operativsystemet till Visual Studio 2015 [6] .

Beskrivning

Om det finns indirekta hopp i programkoden är det potentiellt möjligt att överföra kontrollen till vilken adress som helst där kommandot kan finnas (till exempel på x86 kommer det att vara vilken adress som helst, eftersom den minsta kommandolängden är en byte [14] ). Om en angripare på något sätt kan ändra värdet med vilket kontroll överförs när en hoppinstruktion utförs, kan han återanvända den befintliga programkoden för sina egna behov.

I verkliga program leder icke-lokala hopp vanligtvis till början av funktioner (till exempel om en proceduranropsinstruktion används) eller till instruktionen efter den anropande instruktionen (procedurretur). Den första typen av övergångar är en direkt (engelska framåtkant ) övergång, eftersom den kommer att betecknas med en direktbåge på kontrollflödesgrafen. Den andra typen kallas back (eng. back-edge ) transition, analogt med den första - den båge som motsvarar övergången kommer att vara omvänd [15] .

Direkta övergångar

För direkthopp kommer antalet möjliga adresser till vilka kontroll kan överföras att motsvara antalet funktioner i programmet. Dessutom, när man tar hänsyn till typsystemet och semantiken för det programmeringsspråk som källkoden är skriven på, är ytterligare begränsningar möjliga [16] . Till exempel, i C++ , i ett korrekt program , måste en funktionspekare som används i ett indirekt anrop innehålla adressen till en funktion med samma typ som själva pekaren [ 17] .

Ett sätt att implementera kontrollflödesintegritet för direkta hopp är att du kan analysera programmet och bestämma uppsättningen lagliga adresser för olika greninstruktioner [1] . För att bygga en sådan uppsättning används vanligen statisk kodanalys på någon abstraktionsnivå (på nivån för källkod , intern representation av analysatorn eller maskinkod [1] [10] ). Sedan, med hjälp av den mottagna informationen, infogas koden bredvid instruktionerna för den indirekta grenen för att kontrollera om adressen som tas emot vid körning matchar den statiskt beräknade. Vid divergens kraschar programmet vanligtvis, även om implementeringar tillåter dig att anpassa beteendet i händelse av en överträdelse av det förutsagda kontrollflödet [18] [19] . Sålunda är kontrollflödesgrafen begränsad till endast de kanter (funktionsanrop) och hörn (funktionsingångspunkter) [1] [16] [20] som utvärderas under statisk analys, så när man försöker modifiera pekaren som används för indirekt hopp , kommer angriparen att misslyckas.

Denna metod låter dig förhindra hopporienterad programmering [21] och anropsorienterad programmering [22] eftersom de senare aktivt använder direkta indirekta hopp.

Omvända övergångar

För bakåtövergångar är flera metoder för implementering av CFI möjliga [8] .

Det första tillvägagångssättet bygger på samma antaganden som CFI för direkta hopp, det vill säga möjligheten att beräkna returadresser från en funktion [23] .

Det andra tillvägagångssättet är att behandla returadressen specifikt. Förutom att helt enkelt spara den i stacken , sparas den också, eventuellt med vissa modifieringar, på en plats som är speciellt tilldelad för den (till exempel till ett av processorregistren). Före returinstruktionen läggs också kod till som återställer returadressen och kontrollerar den mot den på stacken [8] .

Det tredje tillvägagångssättet kräver ytterligare stöd från hårdvaran. Tillsammans med CFI används en skuggstack - ett speciellt minnesområde som är otillgängligt för en angripare, i vilket returadresser lagras vid anrop av funktioner [24] .

När man implementerar CFI-scheman för bakåthopp är det möjligt att förhindra en retur -till-biblioteksattack och returorienterad programmering baserat på att ändra returadressen på stacken [ 23] .

Exempel

I det här avsnittet kommer exempel på implementeringar av kontrollflödesintegritet att övervägas.

Clang Indirekt funktion Samtalskontroll

Indirekt funktionsanropskontroll (IFCC) inkluderar kontroller av indirekta hopp i ett program, med undantag för vissa "speciella" hopp, såsom virtuella funktionsanrop. När man konstruerar en uppsättning adresser till vilka en övergång kan ske, beaktas typen av funktion. Tack vare detta är det möjligt att förhindra inte bara användningen av felaktiga värden som inte pekar på början av funktionen, utan också felaktig typ av casting i källkoden. För att aktivera kontroller i kompilatorn finns ett alternativ -fsanitize=cfi-icall[4] .

// clang-ifcc.c #include <stdio.h> int summa ( int x , int y ) { returnera x + y _ } int dbl ( int x ) { returnera x + x ; } void call_fn ( int ( * fn )( int )) { printf ( "Resultatvärde: %d \n " , ( * fn )( 42 )); } void erase_type ( void * fn ) { // Beteende är odefinierat om den dynamiska typen av fn inte är samma som int (*)(int). call_fn ( fn ); } int main () { // När erase_type anropas går information om statisk typ förlorad. radera_typ ( summa ); returnera 0 ; }

Ett program utan kontroller kompilerar utan några felmeddelanden och körs med ett odefinierat resultat som varierar från körning till körning:

$ clang -Vägg -Wextra clang-ifcc.c $ ./a.ut Resultatvärde: 1388327490

Sammanställt med följande alternativ får du ett program som avbryter när call_fn anropas.

$ clang -flto -fvisibility=dolda -fsanitize=cfi -fno-sanitize-trap=alla clang-ifcc.c $ ./a.ut clang-ifcc.c:12:32: runtime error: kontrollflödesintegritetskontroll för typen 'int (int)' misslyckades under indirekt funktionsanrop (./a.out+0x427a20): not: (okänd) definieras här

Clang Forward-Edge CFI för virtuella samtal

Denna metod syftar till att kontrollera integriteten för virtuella samtal i C++-språket. För varje klasshierarki som innehåller virtuella funktioner byggs bitmappar som visar vilka funktioner som kan anropas för varje statisk typ. Om tabellen över virtuella funktioner för något objekt är skadad under körning i programmet (till exempel felaktig typ som kastar ner hierarkin eller helt enkelt minneskorruption av en angripare), kommer den dynamiska typen av objektet inte att matcha någon av de statiskt förutsagda [10] [25] .

// virtual-calls.cpp #include <cstdio> struktur B { virtuell void foo () = 0 ; virtuell ~ B () {} }; struct D : public B { void foo () åsidosätta { printf ( "Höger funktion \n " ); } }; struct Bad : public B { void foo () åsidosätta { printf ( "Fel funktion \n " ); } }; int main () { Dåligt dåligt ; // C++-standarden tillåter casting så här: B & b = static_cast < B &> ( bad ); // Härledd1 -> Bas -> Härledd2. D & normal = static_cast < D &> ( b ); // Som ett resultat är den dynamiska typen av objektet normal normal . foo (); // kommer att vara dåligt och fel funktion kommer att anropas. returnera 0 ; }

Efter kompilering utan kontroller aktiverade:

$ clang++ -std=c++11 virtual-calls.cpp $ ./a.ut Fel funktion

I programmet, istället för att fooklassimplementeringen Danropas foofrån Bad. Detta problem kommer att fångas om du kompilerar programmet med -fsanitize=cfi-vcall:

$ clang++ -std=c++11 -Vägg -flto -fvisibility=dold -fsanitize=cfi-vcall -fno-sanitize-trap=alla virtuella-samtal.cpp $ ./a.ut virtual-calls.cpp:24:3: runtime error: kontrollflödesintegritetskontroll för typ 'D' misslyckades under virtuellt samtal (vtable-adress 0x000000431ce0) 0x000000431ce0: notera: vtable är av typen "Bad" 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 ^

Anteckningar

  1. ↑ 1 2 3 4 5 Martín Abadi, Mihai Budiu, Úlfar Erlingsson, Jay Ligatti. Kontrollflödesintegritet  // Proceedings of the 12th ACM Conference on Computer and Communications Security. - New York, NY, USA: ACM, 2005. - S. 340-353 . — ISBN 1595932267 . - doi : 10.1145/1102120.1102165 .
  2. Volodymyr Kuznetsov, László Szekeres, Mathias Payer, George Candea, R. Sekar. Code-pointer Integrity  // Proceedings of the 11th USENIX Conference on Operating Systems Design and Implementation. - Berkeley, CA, USA: USENIX Association, 2014. - P. 147-163 . — ISBN 9781931971164 .
  3. ↑ Om skillnader mellan CFI- , CPS- och CPI-egenskaperna  . nebelwelt.net. Hämtad 22 december 2017. Arkiverad från originalet 22 december 2017.
  4. ↑ 1 2 Kontrollflödesintegritet - Clang 5-dokumentation . releases.llvm.org. Hämtad 22 december 2017. Arkiverad från originalet 23 december 2017.
  5. ↑ 1 2 vtv - GCC Wiki . gcc.gnu.org. Hämtad 22 december 2017. Arkiverad från originalet 11 juli 2017.
  6. 1 2 Kontrollera flödesvakt (Windows  ) . msdn.microsoft.com. Hämtad 22 december 2017. Arkiverad från originalet 22 december 2017.
  7. ↑ Return Flow Guard - Tencents Xuanwu Lab  . xlab.tencent.com. Hämtad 22 december 2017. Arkiverad från originalet 23 december 2017.
  8. ↑ 1 2 3 4 5 grsäkerhet  . _ www.grsecurity.net. Hämtad 22 december 2017. Arkiverad från originalet 17 februari 2018.
  9. [1] Arkiverad 5 augusti 2017 på Wayback Machine PaX future
  10. ↑ 1 2 3 Caroline Tice, Tom Roeder, Peter Collingbourne, Stephen Checkoway, Úlfar Erlingsson. Upprätthålla framåtriktad kontrollflödesintegritet i GCC & LLVM  // Proceedings of the 23th 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. Hämtad 22 december 2017. Arkiverad från originalet 15 januari 2018.
  12. Clang 3.7 Release Notes — Clang 3.7-dokumentation . releases.llvm.org. Hämtad 22 december 2017. Arkiverad från originalet 26 november 2017.
  13. LLVM-släpp . releases.llvm.org. Hämtad 22 december 2017. Arkiverad från originalet 15 december 2017.
  14. Handböcker för programvaruutvecklare för Intel® 64 och IA-32 Architectures | Intel®-  programvara . software.intel.com. Hämtad 22 december 2017. Arkiverad från originalet 25 december 2017.
  15. Säkerhet - WebAssembly . webassembly.org. Hämtad 22 december 2017. Arkiverad från originalet 23 december 2017.
  16. ↑ 1 2 Aho, Alfred W.; Seti, Ravi; Ullman, Jeffrey D. Compilers - Principles, Technologies, Tools, 2nd ed . – Williams. - 2008. - S.  1062 -1066. - ISBN 978-5-8459-1349-4 .
  17. ISO/IEC 14882:2014 - Informationsteknik - Programmeringsspråk - C++ . — ISO . - 2014. - S. 105. Arkivexemplar daterad 29 april 2016 på Wayback Machine
  18. Vtable Verification - User's Guide . docs.google.com. Hämtad 22 december 2017. Arkiverad från originalet 12 juni 2019.
  19. Kontrollflödesintegritet - Clang 5 dokumentation . releases.llvm.org. Hämtad 22 december 2017. Arkiverad från originalet 23 december 2017.
  20. Muchnick, Steven S. Avancerad kompilatordesign och implementering . - Morgan Kaufmann Publishers , 1997. - S.  609 -618. - ISBN 1-55860-320-4 .
  21. Tyler Bletsch, Xuxian Jiang, Vince W. Freeh, Zhenkai Liang. Hopporienterad programmering: En ny klass av kodåteranvändningsattack  // Proceedings of the 6th ACM Symposium on Information, Computer and Communications Security. - New York, NY, USA: ACM, 2011. - S. 30-40 . — ISBN 9781450305648 . - doi : 10.1145/1966913.1966919 .
  22. AliAkbar Sadeghi, Salman Niksefat, Maryam Rostamipour. Pure-Call Oriented Programming (PCOP): koppla ihop prylarna med samtalsinstruktioner  //  Journal of Computer Virology and Hacking Techniques. — 2017-05-15. - S. 1-18 . — ISSN 2263-8733 . - doi : 10.1007/s11416-017-0299-1 . Arkiverad från originalet den 22 december 2017.
  23. ↑ 1 2 RAP: RIP ROP - Återanvänd attackskydd (nedlänk) . PaX Team . Hämtad 22 december 2017. Arkiverad från originalet 20 maj 2020. 
  24. Förhandsgranskning av teknik för kontrollflödet . Intel Developer Zone . Hämtad 22 december 2017. Arkiverad från originalet 14 augusti 2017.
  25. Kontrollflödesintegritetsdesigndokumentation - Clang 5-dokumentation . releases.llvm.org. Hämtad 22 december 2017. Arkiverad från originalet 23 december 2017.

Litteratur

Böcker Artiklar

Länkar