Кіріспе
Бағдарламалық қамтамасыз ету қауіпсіздігі техникалары. Буфердің асып кетуінен қорғау – бұл орындалатын бағдарламалардың қауіпсіздігін арттыру үшін бағдарламалық жасақтаманы әзірлеу кезінде қолданылатын әртүрлі техникалардың бірі. Бұл техника стекке бөлінген айнымалылардағы буфердің асып кетуін анықтау және олардың бағдарламаның қате жұмыс істеуіне немесе қауіпсіздікке қатысты ауыр осалдықтарға әкелуіне жол бермейді. Стек буферінің асып кетуі, бағдарлама шақыру стегіндегі жад мекенжайына, әдетте белгілі бір ұзындықтағы буфер болатын, жоспарланған дерек құрылымынан тыс жазғанда пайда болады. Буфердің асып кетуіне себеп – бағдарламаның буферге оның көлемінен артық деректерді жазуы. Бұл көбінесе стектегі жақын орналасқан деректердің бұзылуына әкеледі, бұл бағдарламаның құлауына, дұрыс емес жұмыс істеуіне немесе қауіпсіздік мәселелеріне соқтыруы мүмкін. Әдетте, буфердің асып кетуінен қорғау стекке бөлінген деректердің ұйымдастырылуын өзгертеді, осылайша, стек буферінің асып кетуімен жойылғанда, оның алдындағы буфердің асып кеткенін көрсететін «канар» (canary) мәнін қамтиды. «Канар» мәнін тексеру арқылы зардап шеккен бағдарламаның орындалуы тоқтатылуы мүмкін, оның қате жұмыс істеуіне немесе шабуылшының бақылауын алуына жол бермейді. Буфердің асып кетуінен қорғаудың басқа да техникаларына шекаралық тексеру кіреді, ол әр бөлінген жад блогына қол жеткізуді тексереді, осылайша олар бөлінген орыннан тысқары шығу мүмкіншілігін болдырмайды, сондай-ақ таңбалау, ол деректерді сақтау үшін бөлінген жадтың орындалатын кодты қамтымауын қамтамасыз етеді. Стекте бөлінген буферді асырып толтыру, бағдарламаның орындалуына ықпал етуі мүмкін, себебі стекте барлық белсенді функция шақыруларының қайтару адрестері сақталады. Дегенмен, үймеге (heap) негізделген ағындыларға қарсы да осыған ұқсас іске асыруға қатысты қорғау шаралары бар. Буфердің асып кетуінен қорғаудың бірнеше іске асырылуы бар, соның ішінде GNU Compiler Collection, LLVM, Microsoft Visual Studio және басқа компиляторлар үшін жасалғандары.
Buffer overflow protection is any of various techniques used during software development to enhance the security of executable programs by detecting buffer overflows on stack allocated variables, and preventing them from causing program misbehavior or from becoming serious security vulnerabilities. A stack buffer overflow occurs when a program writes to a memory address on the program's call stack outside of the intended data structure, which is usually a fixed length buffer. Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than what is actually allocated for that buffer. This almost always results in corruption of adjacent data on the stack, which could lead to program crashes, incorrect operation, or security issues. Typically, buffer overflow protection modifies the organization of stack allocated data so it includes a canary value that, when destroyed by a stack buffer overflow, shows that a buffer preceding it in memory has been overflowed. By verifying the canary value, execution of the affected program can be terminated, preventing it from misbehaving or from allowing an attacker to take control over it. Other buffer overflow protection techniques include bounds checking, which checks accesses to each allocated block of memory so they cannot go beyond the actually allocated space, and tagging, which ensures that memory allocated for storing data cannot contain executable code. Overfilling a buffer allocated on the stack is more likely to influence program execution than overfilling a buffer on the heap because the stack contains the return addresses for all active function calls. However, similar implementation specific protections also exist against heap based overflows. There are several implementations of buffer overflow protection, including those for the GNU Compiler Collection, LLVM, Microsoft Visual Studio, and other compilers.
Шолу
Стек буферінің толып кетуі, бағдарламаның шақыру стегіндегі жад мекенжайына, әдетте белгілі бір ұзындықтағы буфер болып табылатын, жоспарланған дерек құрылымынан тыс жазғанда пайда болады. Стек буферінің толып кетуіне себеп – бағдарламаның буферге оның көлемінен артық дерек жазуы. Бұл көбінесе стектегі жақын орналасқан деректердің бұзылуына әкеледі, ал қателік оқиғаның нәтижесінде бағдарлама құлап қалуы немесе қате жұмыс істеуі мүмкін. Стек буферінің толып кетуі – буфердің толып кетуі (немесе буфердің асып кетуі) деп аталатын жалпы бағдарламалық қателіктің бір түрі. Стектегі буферді толып кету, үймедегі буферді толып кетуге қарағанда бағдарламаның жұмысын бұзуы мүмкін, себебі стекте барлық белсенді функция шақыруларының қайтару адрестері сақталады. Стек буферінің толып кетуі, «стек бұзу» деп аталатын шабуылдың бір бөлігі ретінде әдейі жасалуы мүмкін. Егер зардап шеккен бағдарлама арнайы құқықтармен жұмыс істесе немесе сенімсіз желілік хосттардан (мысалы, қоғамдық веб-серверден) деректерді қабылдаса, онда бұл қателік шабуылшыға іске асырылатын кодты бағдарламаға енгізіп, процесті басқаруға мүмкіндік беретін қауіпсіздік осалдығы болып табылады. Бұл – шабуылшылардың компьютерге рұқсатсыз кіруінің ең көне және сенімді әдістерінің бірі. Әдетте, буфердің толып кетуінен қорғану үшін функция шақыруының стегіндегі деректердің құрылымы өзгертіліп, «канар» деп аталатын мән қосылады. Егер бұл мән жойылса, онда оған дейінгі буфер толып кеткенін көрсетеді. Бұл шабуылдың толық класын болдырмауға мүмкіндік береді. Кейбір зерттеушілердің пікірінше, мұндай техникалардың өнімділікке әсері ескерілмейді. Стек бұзудан қорғану кейбір шабуыл түрлеріне қарсы тиімді емес. Мысалы, ол үймедегі буфердің толып кетуінен қорғай алмайды. Құрылымдағы деректердің орналасуын өзгертудің дұрыс жолы жоқ, өйткені құрылымдар модульдер арасында, әсіресе ортақ кітапханаларда бірдей болуы керек. Буферден кейін орналасқан құрылымдағы деректерді «канар» арқылы қорғау мүмкін емес, сондықтан бағдарламашылар өздерінің айнымалыларын қалай ұйымдастырып, құрылымдарын қалай қолданатынына ерекше назар аударуы керек.
Канарлық құстар
Канарлар немесе канарлық сөздер немесе стек печеньелері – буфер ағып кетуін бақылау үшін буфер мен стектегі басқару деректерінің арасына орналастырылған белгілі мәндер. Буфер ағып кеткен кезде, әдетте, бұзылатын алғашқы дерек – канар болады, сондықтан канар деректерін тексеру сәтсіз аяқталса, ағып кету туралы ескерту беріледі, содан кейін бұзылған деректерді жарамсыз деп тану арқылы оны өңдеуге болады. Канар мәнін күзетші мәнмен шатастыруға болмайды. Бұл терминология канарийлерді көмір шахталарында пайдаланудың тарихи практикасына сілтеме жасайды, себебі олар шахтерлерге қарағанда уытты газдарға ертерек сезімтал болып, биологиялық ескерту жүйесі ретінде қызмет ететін. Канарлар стек печеньелері деп те аталады, бұл мән бұзылғанда "ұсқынсыз печенье" (broken cookie) бейнесін тудыру үшін жасалған. Канардың үш түрі бар: терминатор, кездейсоқ және кездейсоқ XOR. StackGuard-тың қазіргі нұсқалары осы үш түрін де қолдайды, ал ProPolice терминатор және кездейсоқ канарларды қолдайды.
Терминатор канарилері
Терминатор канарилері буферлік ағызу шабуылдарының көпшілігі белгілі бір жол операцияларына негізделгенін, олар жол аяқтағыштарында тоқтатылатынын пайдаланады. Осыған жауап ретінде канарилер нөлдік аяқтағыштардан, CR, LF және FF-тен құрылады. Нәтижесінде, шабуылшы канариді өзгертпеу үшін қайтару адресін жазу алдында нөлдік символ жазуы керек. Бұл strcpy және нөлдік символ көшірілгенде тоқтатылатын басқа әдістерді пайдаланатын шабуылдарды болдырмайды, бірақ канаридің мәні белгілі болуы қолайсыз салдар болып табылады. Қорғаныс бар болғанымен, шабуылшы канариді оның белгілі мәнімен және үйлесімсіз мәндермен басқару ақпаратымен жаза алады, осылайша канари тексеру кодын асып өтеді, ол процессор шақырудан қайтар алдында орындалады.
Қаңқалар
Кездейсоқ канарилер әдетте ентропия жинағыш демонынан кездейсоқ түрде жасалады, бұл шабуылшының олардың мәнін білуіне жол бермеу үшін жасалған. Көбінесе, канарды пайдалану үшін оқу логикалық тұрғыдан мүмкін емес немесе қиын. Канар – тек қана оны білуі қажет болғандарға ғана белгілі, қауіпсіз мән, осы жағдайда – буферлік асығудан қорғау коды. Әдетте, кездейсоқ канар бағдарлама басталысында жасалады және жаһандық айнымалыда сақталады. Бұл айнымалы көбінесе карталанбаған беттермен толтырылады, сондықтан RAM-ды оқуға арналған қателерді пайдаланатын әртүрлі тәсілдермен оны оқуға тырысу сегментациялық қатеге алып келіп, бағдарламаны тоқтатады. Егер шабуылшы оның қайда екенін білсе немесе бағдарламаны стекпен оқуға мәжбүрлей алса, канарды оқу мүмкін болып қалады.
Кездейсоқ XOR канарилері
Кездейсоқ XOR канарлары – бақылау деректерінің толық немесе бір бөлігімен XOR шифрланған кездейсоқ канарлар. Осылайша, канар немесе бақылау деректері зақымданса, канардың мәні бұрыс болады. Кездейсоқ XOR канарлары кездейсоқ канарлардың әлсіздіктерімен бірдей, бірақ канарды алудың "стектен оқу" әдісі біршама күрделірек. Шабуылшы қорғанысты бұрмалау үшін қажетті бастапқы канарды қайта құру үшін канарды, алгоритмді және бақылау деректерін алуы керек. Сонымен қатар, кездейсоқ XOR канарлары құрылымдағы буферді артығын шашу арқылы басқару деректеріне нұсқауын өзгертуге бағытталған шабуылдың белгілі бір түрінен қорғай алады. XOR шифрлаудың арқасында, егер бақылау деректері немесе қайтару мәні өзгерсе, канар дұрыс болмайды. Нұсқаушының арқасында бақылау деректерін немесе қайтару мәнін канарға құйылып кетпестен өзгертуге болады. Бұл канарлар бақылау деректерін зақымдалған нұсқаулармен өзгертуден қорғайды, бірақ басқа деректерді немесе нұсқаулардың өзін қорғамайды. Функциялық нұсқаулар, әсіресе, олар шақырылған кезде shellcode-ты ашып, орындай алатындықтан, мұнда мәселе тудырады.
Шекараларды тексеру
Шекараларды тексеру – компиляторға негізделген әдіс, ол әр бөлінген жад блогы үшін орындалу кезіндегі шекара туралы ақпаратты қосады және барлық көрсеткіштерді орындалу кезінде осы шекаралармен тексеруге мүмкіндік береді. C және C++ үшін шекараларды тексеру көрсеткіш есептеу кезінде немесе дерекке қол жеткізу кезінде жүзеге асырылуы мүмкін. Бұл тәсілді іске асыру үшін әр бөлінген жад блогын сипаттайтын орталық репозиторий немесе жадтағы деректердің түрін белгілеуге арналған компиляторға негізделген немесе аппараттық негізделген (таңбаланған архитектураны талап ететін) техника қолданылады, бұл негізінен түрді тексеру үшін пайдаланылады. Жадың белгілі бір бөліктерін орындалмайтын деп белгілеу арқылы дерек сақтау үшін бөлінген жадта орындалатын кодтың болуына кедергі жасалады. Сонымен қатар, жадтың кейбір бөліктерін бөлінбеген деп белгілеуге болады, бұл буфер асығуын болдырмайды. Тарихи тұрғыдан алғанда, таңбалау жоғары деңгейдегі бағдарламалау тілдерін іске асыру үшін қолданылған; операциялық жүйенің тиісті қолдауымен таңбалау буфер асығуын анықтау үшін де қолданылуы мүмкін. Мысалы, Intel, AMD және ARM процессорлары қолдайтын NX битінің аппараттық мүмкіндігін келтіруге болады.
GNU компиляторлар жинағы (GCC)
Stack smashing қорғанысын алғаш рет 1997 жылы StackGuard жүзеге асырды және 1998 жылғы USENIX қауіпсіздік симпозиумында жарияланды. StackGuard GCC 2.7-нің Intel x86 серверіне қосылған жаңартулар жиынтығы ретінде ұсынылды. StackGuard 1998 жылдан 2003 жылға дейін Immunix Linux дистрибутиві үшін қолдау көрсетілді және терминатор, кездейсоқ және кездейсоқ XOR канарларын қолдаумен кеңейтілді. StackGuard 2003 жылғы GCC саммитінде GCC 3.x құрамына енгізу ұсынылды, бірақ бұл жоба іске аспады. 2001 жылдан 2005 жылға дейін IBM стектің бұзылуына қарсы қорғау үшін ProPolice деп аталатын GCC жапсырмаларын жасады. Ол StackGuard идеясын жергілікті көрсеткіштер мен функция аргументтерінен кейін буферлерді стек фреймінде орналастыру арқылы жақсартты. Бұл көрсеткіштердің зақымдануын болдырмауға көмектесті, соның салдарынан кез келген жад орнына қол жеткізудің алдын алды. Red Hat инженерлері ProPolice-та кемшіліктерді анықтады және 2005 жылы GCC 4.1 құрамына енгізу үшін стектің бұзылуына қарсы қорғауды қайта іске қосты. Бұл жұмыс fstack protector туындысын енгізді, ол тек осал функциялардың бір бөлігін ғана қорғайды, ал fstack protector all туындысы барлық функцияларды, қажет болса да, болмаса да қорғайды. 2012 жылы Google инженерлері қауіпсіздік пен өнімділік арасындағы жақсы тепе-теңдікті қамтамасыз ету үшін fstack protector strong туындысын іске қосты. Бұл туынды fstack protector туындысына қарағанда осал функциялардың көбірек түрлерін қорғайды, бірақ барлық функцияларды емес, fstack protector all туындысынан жақсы өнімділікті қамтамасыз етеді. Ол GCC-нің 4.9 нұсқасынан бастап қолжетімді. Барлық Fedora пакеттері Fedora Core 5 нұсқасынан бастап fstack protector туындысымен құрастырылған, ал Fedora 20 нұсқасынан бастап fstack protector strong туындысымен құрастырылған. Ubuntu пакеттерінің көп бөлігі 6.10 нұсқасынан бастап fstack protector туындысымен құрастырылған. Әрбір Arch Linux пакеті 2011 жылдан бері fstack protector туындысымен құрастырылады. 2014 жылдың 4 мамырынан бастап құрастырылған барлық Arch Linux пакеттері fstack protector strong туындысын пайдаланады. Stack қорғанысы Debian жүйесінде тек кейбір пакеттер үшін ғана қолданылады, ал FreeBSD базалық жүйесі үшін 8.0 нұсқасынан бастап қолданылады. Stack қорғанысы OpenBSD, Hardened Gentoo және DragonFly BSD сияқты кейбір операциялық жүйелерде стандартты түрде қолданылады. StackGuard және ProPolice функциялар көрсеткіштеріне ағып түсетін автоматты түрде бөлінген құрылымдардағы ағып кетулерге қарсы қорғауды қамтамасыз ете алмайды. ProPolice кем дегенде мұндай құрылымдарды функциялар көрсеткіштерінен бұрын бөлу үшін бөлу тәртібін өзгертеді. PointGuard жүйесінде көрсеткіштерді қорғау үшін жеке механизм ұсынылды және ол Microsoft Windows жүйесінде қолжетімді.
Microsoft Visual Studio бағдарламасы
Microsoft компаниясының компиляторлар жиынтығы 2003 жылғы нұсқасынан бастап буферлік ағызудан қорғауды командалық жол опциясы арқылы жүзеге асырады, ол 2005 жылғы нұсқасынан бастап әдепкі бойынша қосылған. Опцияны пайдалану қорғауды өшіреді.
IBM компиляторы
Стакқа шабуылдан қорғау компилятордың -qstackprotect жалаушасы арқылы қосыла алады.
Clang/LLVM
Clang GCC сияқты fstack қорғаушыларының опцияларын және осыған ұқсас, өнімділікке аз әсер ететін, күштірек "қауіпсіз стек" жүйесін қолдайды. Clang-та үш буфер асығуын анықтау құралы бар, атап айтқанда AddressSanitizer (fsanitize=address) және ресми емес SafeCode (соңғы рет LLVM 3.0 үшін жаңартылған). Бұл жүйелердің өнімділік төмендеуі, жадты пайдалану және анықталатын қателер түрлері тұрғысынан әртүрлі ерекшеліктері бар. Стек қорғанысы OpenBSD-ді қоса алғанда, кейбір операциялық жүйелерде стандартты түрде қолданылады.
and the unofficial SafeCode (last updated for LLVM 3.0). These systems have different tradeoffs in terms of performance penalty, memory overhead, and classes of detected bugs. Stack protection is standard in certain operating systems, including OpenBSD.
Intel компиляторы
Intel-дің C және C++ компиляторы GCC және Microsoft Visual Studio ұсынатын сияқты опциялармен стектің бұзылуына қарсы қорғауды қолдайды.
StackGhost (жабдыққа негізделген)
Майк Францен тапқан StackGhost – тіркелім терезесінің төгілу/толтыру процедураларына жасалған қарапайым өзгеріс, ол буферлік ағып кетуді пайдалануды әлдеқайда қиындатады. Ол Sun Microsystems SPARC архитектурасының ерекше аппараттық мүмкіндігін (яғни, кадрлық тіркелім терезесінің төгілу/толтыруы стекте кейінге шегеріледі) пайдаланып, қайтару нұсқаушыларының (эксплойттер орындалу жолын басып алудың әдеттегі тәсілі) өзгерістерін анықтап, екілік немесе бастапқы кодты өзгертпестен барлық қолданбаларды автоматты түрде қорғайды. Бұл өзгерістің өнімділікке тигізер әсері ең көп дегенде бір проценттен аспайды. Соның салдарынан туған gdb мәселелерін екі жыл өткен соң Марк Кеттенис шешіп, мүмкіндікті қосуға жол ашты. Осыдан кейін StackGhost коды OpenBSD/SPARC-ке енгізіліп, жақсартылды.