Stack-smashing protection

Stack Smashing Protector (SSP) funkce kompilátor pomáhá odhalit zásobníku přetečení vyrovnávací paměti tím končím, když se změní tajné hodnota na zásobníku. To slouží dvojí účel při vytváření výskytu těchto chyb viditelných a jak využívat zmírňování proti vratné orientovaného programování. SSP pouze rozpozná zásobník přetečení vyrovnávací paměti, že se není bráněno. Detekce může být poražen přípravou vstupu tak, že stoh kanárek je přepsána správnou hodnotu, a proto nenabízí dokonalou ochranu. Stack kanárek je původem slovo velikosti, a pokud vybrána náhodně, bude útočník musel uhodnout správnou hodnotu mezi 2 ^ 32 nebo 2 ^ 64 kombinací (a odhalovat chyby, pokud odhad je nesprávný), nebo resort pro chytré prostředků pro určování ji ,
Popis

Překladače implementovat tuto funkci výběrem vhodných funkcí, ukládání zásobníku kanárka během funkce prologu a kontrola hodnotu v epilogu, vyvolání popisovač selhání, pokud byl změněn. Například, zvažovat kód:
void foo ( const char * str )
{
char vyrovnávací paměti [ 16 ] ;
strcpy ( vyrovnávací paměť , str ) ;
}
SSP automaticky názorně transformuje tento kód do toto:
/ * Všimněte si, jak přetečení vyrovnávací paměti jsou nedefinované chování a kompilátory mají tendenci
optimalizovat tyto kontroly pryč, pokud jste jim napsal sám, toto jen díla
robustně, protože kompilátor to udělal sám. */
extern uintptr_t __stack_chk_guard ;
noreturn void __stack_chk_fail ( void ) ;
void foo ( const char * str )
{
uintptr_t canary = __stack_chk_guard ;
char buffer [ 16 ] ;
strcpy ( buffer , str ) ;
if ( ( canary = canary ^ __stack_chk_guard ) != 0 )
__stack_chk_fail ( ) ;
}
Všimněte si, jak se tajné hodnota uložena v globální proměnné (inicializována v době Program zatížení) a je zkopírován do rámce fronty, a jak je to bezpečně vymazat ze stohu jako součást kontroly. Vzhledem k tomu, komíny rostou směrem dolů na mnoha architekturách, kanárek bude přepsán při každém vstupu do strcpy je alespoň 16 znaků. Návratnost Volající ukazatel využívány v návratu orientované programování útoků není přístupná, dokud hodnota byla ověřena, tak zneškodnění takové útoky.
Detekce je perfektní, je nemožné, aby falešné správnou hodnotu, tedy útočník nemá plnou kontrolu nad tím, co bajtů lze zapsat. Útočník nemůže změnit další obsah zásobníku nezjištěné pokud falšování správnou hodnotu zastaví výstup. Například, v případě, že kanárek v strcpy výše uvedeném příkladu obsahuje nulový bajt, je nemožné, aby falešné tohoto bajtu stehlíkem bez zastavení výstup. To přinutí útočník buď není útočit, být zjištěn, nebo ne měnit žádné další obsah zásobníku. To neznamená, přetečení vyrovnávací paměti je vždy unexploitable: řetězec je nyní 16 znaků namísto zamýšlený limit 15 znaků, může to způsobit další nezamýšlené chování během pokračujícího provádění programu.
Všimněte si, jak je zde pouze jedna hodnota ochrany, ne každá proměnná je chráněno tímto způsobem. Heuristika je často používána, že první (směrem dolů) uloží kanárka, pak vyrovnávací paměti (která může přetéct do sebe) a nakonec všechny malé proměnné neovlivněná z překročení. To je založeno na myšlence, že to je obecně méně nebezpečné, pokud pole jsou modifikovány, ve srovnání s proměnnými, které drží vlajky, ukazatele a ukazatele na funkce, které se mohou více vážně změnily popravu.
Některé kompilátory náhodně pořadí zásobníku proměnných a náhodně rozvržení stack frame, což dále komplikuje určující správný vstup s zamýšlené škodlivý účinek.
Používání

Překladače, jako GCC umožňuje tuto funkci, pokud požádal prostřednictvím volby kompilátoru, nebo v případě, že dodavatel překladač povoleno jej ve výchozím nastavení. Je to stojí za zvážení, což mu ve výchozím nastavení, pokud váš operační systém je bezpečnost při vědomí a poskytovat podporu. Je možné ji použít v celém vašem operačním systému (i jádra a standardní knihovna); snad omluvil porty s opravdu špatnou kvalitu kódu. Funkce povolena s pravou volbou -ffoo a může být zakázán pomocí -fno-foo protějšek. Několik možností existuje, které poskytují různé varianty SSP:
-fstack-chránič : Zkontrolujte, zda je zásobník rozbíjení funkcí s ohrožených objektů. To zahrnuje funkce s nárazníky větší než 8 bajtů nebo volání alloca.
-fstack-protector-silná : Like -fstack-ochránce , ale také obsahuje funkce s místními polí nebo odkazy na lokální rám adresy.
-fstack-protector-all : Zkontrolujte, zda je zásobník rozbíjení v každé funkci.
Některé operační systémy rozšířili svůj kompilátor s více příslušnými možnostmi:
-fstack-náhodné přehrávání : (Nachází se v OpenBSD) Náhodně pořadí zásobníku proměnných v době kompilace. To pomáhá najít chyby.
Když aktivujete funkci, bude kompilátor pokusí propojit v libssp a libssp_nonshared (pokud staticky propojeny) pro podporu run-time. To je zakázána, pokud minete -nostdlib jako vy při propojování jádro a budete muset zadat vlastní implementaci. U uživatelského prostoru, máte dvě možnosti:
Uveďte své vlastní implementaci v libc (takže libc mohou využít funkce) a nainstalujte prázdnou libssp a libssp_nonshared knihovny (nebo změnit svůj toolchain, že nebude používat).
Použijte provádění libssp, která přichází s GCC.
Uskutečnění

Podpora Run-time potřebuje pouze dvě složky: globální proměnné a rutinu kontrola selhání. Například, minimální provedení by mohly být:
#include <stdint.h>
#include <stdlib.h>

#if UINT32_MAX == UINTPTR_MAX
#define STACK_CHK_GUARD 0xe2dee396
#else
#define STACK_CHK_GUARD 0x595e9fbd94fda766
#endif

uintptr_t __stack_chk_guard = STACK_CHK_GUARD ;

__attribute__ ( ( noreturn ) )
void __stack_chk_fail ( void )
{
#if __STDC_HOSTED__
abort ( ) ;
#elif __is_myos_kernel
panikařit ( "Stack rozbíjet detekováno" ) ;
#endif
}
Všimněte si, jak tajné stráž hodnota je pevně spíše než být rozhodnuto v průběhu programu zátěži. Měli byste mít program zavaděč (bootloader v případě jádra) náhodné hodnoty. Můžete to udělat tím, že hodnoty strážní ve speciálním segmentu, který zavaděč ví, že náhodně. Čísla zde uvedené nejsou zvláštní, jsou to jen příklad náhodně generovaných čísel. Stále můžete využít chyb-objevování vlastností SSP, i když hodnota stráž není kryptograficky bezpečná (pokud jste dostatečně předvídat, obskurní chyby, které inteligentně obejít SSP).
Případně, mohli byste mít počáteční fázi ve svém kódu, který inicializuje hodnotu stráž, snad napsaný v sestavě, nebo v C, ale postavené bez ochrany stack rozbít. Tento přístup zvyšuje složitost kódu a rané fáze, kdy jazykové funkce nejsou online. Můžete přijmout taková přístupy se závitem-local skladování, ERRNO, stránkování, GDT, plánování, a tak dále, a najednou bootstrap je velmi složitý s mnoha závislostí mezi jazykovými funkcemi. Jakmile funkce vestavěné s hromadou-rozbíjet ochranu je spuštěn, hodnota stráž nemůže být změněn nebo falešné selhání dojde.
Bezpečnou manipulaci

Dejte si pozor, jak implementovat popisovač detekce zásobník smeč: Tento kód je spuštěn pouze v případech, kdy byla chyba vyvolaných nevinně, nebo tam, kde je chyba využívána zlomyslně. Teď už se předpokládá, že útočník mít, přinejmenším, poškozený neznámé množství tohoto vlákna v zásobníku. To znamená, že životní prostředí je nepřátelský. Stack je v současné době pod kontrolou a žádná z nových lokální proměnné jsou ovlivněny. Všimněte si však, že stoh rozbít ochrany může dojít z signal handler nebo jinou nevhodnou dobu, kdy jiné vlákno drží zámek na kritickém standardní knihovny státu nebo takový. Dejte si pozor, jak pokud ukazatele volajícímu stack proměnné jsou v současné době uvnitř standardní knihovny, a pomocí standardních funkcí knihoven přistupuje, že paměť, útočník může ovládat psovoda detekční zásobník smeč dokonce.
Za předpokladu, že psovod vyvolání znamená inteligentní exploit se děje, nejlepší postup je je:
Eliminovat vliv útočníka.
Alert uživatel nebo správce systému o možné porušení.
Diagnostikovat podrobnosti o přetečení vyrovnávací paměti, takže vada může být stanovena.
Měli byste předpokládat to nejhorší, pokud chcete eliminovat vliv útočníka. Použitý využít lze dobře kombinovat s jinými využívaných zranitelností, a dostatečně zkušený útočník může dokonce ovlivňovat a využívat činnost psovoda. Existuje mnoho způsobů, jak kreativní útočník by mohl mít vliv na psovoda, nebo dokonce využít něj:
Ukazatele na dřívější proměnné zásobníku (nyní je třeba považovat za potenciálně poškozené) by mohl být uložen někde a přistupovat k nim pomocí funkcí, které používáte.
Obslužná rutina by mohl být provozován ve velmi nevhodnou dobu kde je proces křehká, možná z psovoda signálu, možná aktuální vlákno vlastní nerekurzivní zámky, které by zablokování.
Tisk trasování zásobníku (pokud je to vůbec možné) a další diagnostické informace k deskriptor stderr souboru (což nemusí ani existovat v tomto procesu, ale místo toho FD 2 se používá k jinému účelu), může vést k výstupu být poslán k útočníkovi. To je představitelný pro webový server, který možná obsahuje obsah stderr v chybové reakce. Útočník by mohl naučit věci tímto způsobem, že je neměl.
Vlákno může být multi-threaded a kdo ví, jak, které by mohly interagovat s nití, která je závadná a ohrožena. Mohlo by to mít ukazatele na proměnné na hromadu ohrožena závitu, a SSP nepomůže, pokud přistupuje ty.
Váš přístup by měl být co možná nejdříve se odstraní proces. Používejte pouze asynchronní-Signal-safe funkcí, nejlépe bez státu, která by je mohla ovlivnit. Nepište do jakéhokoli standardního proudy, ale otevřete terminál znovu nebo zapisovat do systémového protokolu. Ujistěte se, žádný z těchto operací nezdaří (například, v případě, že proces je v chroot nebo mimo popisovačů souboru).
Ideální přístup je snad mít speciální systémové volání, které dělá tyto úkoly a vyvolat ji bezpodmínečně a okamžitě. Jaderný kód nesmí věřit v uživatelském prostoru kód, nebo být nebezpečným ovlivnil ji, takže jej lze považovat za bezpečné. To pak může zastavit všechna vlákna v procesu, kde vyšetřovat problém Zdálo se, že se vyskytují v procesu, a vhodně upozornit uživatele nebo správce systému.
libssp

Alternativně, na vlastní realizaci, můžete použít implementaci, která přichází s GCC. To znamená, že musíte vybudovat libssp jako součást vašeho toolchain.
TODO : Nikdy jsem ji postaveny pro účely osdev předtím, ale myslím, že jste to , aby všechny-cíl-libssp a make install-target-libssp jako u libstdc ++. Je pravděpodobné, že závisí na libc bezdůvodně vůbec (jako GCC vývojáři dát opevnit zdrojové funkce v něm, a chce zjistit, zda pracují).
Přístup libssp je mít inicializační funkce označený jako atribut konstruktér, který je provozován mezi globálními konstruktérů při spuštění procesu. To znamená, že SSP není správně on-line během časných částí inicializace procesu (ale možná, že to není problém, pokud jsou všechny tyto C zásobníkové rámce jsou pryč, než byla použita, že bod a výchozí hodnota null stráž až do současnosti). Spouštěcí kód pak pokračuje k pokusu otvírání / dev / urandom , která by mohla selhat, pokud jste v chrootu, jsou mimo popisovačů souboru, nebo váš systém nemá takový soubor (snad již od návrhu). Pokud se to nepodaří, to padá zpátky na přiměřenou, ale známou hodnotu. Si můžete přečíst zde libssp inicializační kód .
Libssp __stack_chk_fail realizace pokusí otevřít terminál, postavit chybovou zprávu s alloca, pak použijte psát na výstup je, pokud terminál není přístupná, pokusí se systémového protokolu. Poté se pokouší zničit proces tím, že vyvolání __builtin_trap () , psaní 0 int k int na -1 (který je také nedefinované chování a nezarovnaný ukazatel, kromě asi shazovat), a nakonec se pokoušet se _exit (). Tento opuštění strategie necítí výborný robustní. Si můžete přečíst zde libssp handler kód .
Přečtěte si bezpečnou část manipulační výše a přečíst kód, pak se rozhodnout, zda chcete, aby tento propojen do vašich programů, nebo zda je čistší, aby se vaše vlastní implementaci. Můžete také změnit tento kód jako součást svého operačního systému specifické toolchain .