Препълване на буфера: причини, ефективни решения и необходима защита

Всички програмисти са наясно с потенциалната опасност от препълване на буфера в програмите си. Има много заплахи, свързани с него, както в новия, така и в стария софтуер, независимо от броя на направените поправки. Атакуващите могат да се възползват от тази грешка, като инжектират код, специално проектиран да предизвиква препълване в началото на набор от данни, и след това запишат остатъка на адрес в паметта, съседен на адреса на препълване.

Данните могат да съдържат изпълним код, който позволява на нападателите да стартират по-големи и по-сложни програми или да им даде достъп до системата. Грешката е много трудна за откриване и отстраняване, тъй като кодът се състои от милиони редове. Коригирането на тези грешки е доста сложно, а също и склонно към грешки, което усложнява процеса на коригирането им.

Откриване на препълване на буфера

Определение за препълване на буфера

Преди да потърсим препълване, Необходимо е да знаете, Какво представлява. Както подсказва името, тези уязвимости са свързани с буфери или заделяне на памет в езици, осигуряващи директен достъп на ниско ниво за четене и запис.

В езиците C и Assembler четенето или писането на такива разпределения не води до автоматична проверка на границите. Така че, ако в дадено приложение се открие препълване на буфера на стека, не се проверява дали броят на байтовете може да бъде поставен във въпросния буфер. В такива случаи програмата може да "препълни" капацитета си. Това води до това, че данните, записани след наводнението, презаписват съдържанието на следващите адреси в стека и четат допълнителни. Препълване може да възникне непреднамерено поради грешка на потребителя.

Тя може да бъде причинена от злонамерен участник, който изпраща внимателно подготвен злонамерен вход към програма, която след това се опитва да го съхрани в недостатъчен буфер. Ако в това приложение се открие препълване на буфера на стека, данните от препълването се записват в съседния буфер, където презаписват всички съществуващи данни.

Обикновено те съдържат указател за връщане към използваната функция - адресът, на който процесът трябва да скочи след това. Атакуващият може да зададе нови стойности, които да сочат към избран от него адрес. Нападателят обикновено задава нови стойности, за да посочи къде се намира полезният товар. Това променя пътя на изпълнение на процеса и незабавно предоставя контрол на зловредния код.

Използването на препълване на буфера позволява на атакуващия да контролира или прекратява даден процес или да променя вътрешните му променливи. Това конкретно нарушение се нарежда сред 25-те най-опасни грешки при програмирането в света (2009 CWE/SANS Top 25 Most Dangerous Programming Errors) и е идентифицирано като CWE-120 в Речника на системните уязвимости (Dictionary of System Vulnerabilities). Въпреки че е добре проучена, тя продължава да е проблем за популярните програми.

Прост вектор за използване на буфера

Когато работите с изходния код, трябва да обърнете специално внимание на това къде се използват и модифицират буфери. Особено внимание трябва да се обърне на функциите, свързани с входни данни, предоставени от потребителя или друг външен източник, тъй като те осигуряват прост вектор, който да се използва при откриване на препълване на буфера на стека. Например, когато потребителят задава въпрос с "да" или "не", е подходящо да се съхраняват данните за потребителския низ в малък буфер за низ "да", както е показано в следния пример.

Прост вектор на използването на буфера

При прегледа на кода се вижда, че не се извършва проверка на границите. Ако потребителят въведе "може би", програмата ще спре, вместо да поиска отговор, който се записва в буфера независимо от дължината. В този пример, тъй като отговорът на потребителя е единствената декларирана променлива, следващите стойности в стека ще бъдат стойността на адреса за връщане или мястото в паметта, на което програмата ще се върне след изпълнението на функцията за задаване на въпрос.

Това означава, че ако потребителят въведе четири байта данни, които са достатъчни, за да препълнят буфера за команди на клиента, ще последва валиден адрес за връщане, който ще бъде променен. Това ще накара програмата да излезе от функцията на място, различно от първоначално предвиденото в кода, и може да доведе до опасно и непредвидено поведение на софтуера.

Ако първата стъпка за откриване на препълване на буфера в изходния код е да се разбере как работят те, втората стъпка е да се проучи външният вход и манипулирането на буфера, третата стъпка е да се открие кои функции са уязвими към тази уязвимост и кои могат да действат като "червени знамена". Функцията gets е чудесна за писане извън зададения буфер. Всъщност това качество се разпростира върху цялото семейство свързани функции, включително strcpy, strcmp и printf/sprintf, когато се използва някоя от тези уязвимости при препълване.

Премахване от базата с кодове

Ако в изходния код се открие препълване на буфера на стека, ще е необходимо координирано премахване от базата данни. За целта трябва да сте запознати с безопасните методи на работа. Най-лесният начин да предотвратите тези уязвимости е да използвате език, който не ги позволява. Езикът C има тези уязвимости поради директния си достъп до паметта и липсата на строго типизиране на обектите. Езиците, които не споделят тези аспекти, обикновено са неуязвими. Това са Java, Python и .NET, както и други езици и платформи, които не изискват специални проверки или модификации.

Разбира се, не винаги е възможно да се промени изцяло езикът за разработка. В този случай се използват сигурни методи да работи с препълване на буфера на командата. В случая с функциите за обработка на низове има много дискусии за това кои методи са налични, кои са безопасни за използване и кои трябва да се избягват. Функциите strcpy и strcat копират низ в буфер и добавят съдържанието на единия към другия. Тези два метода имат несигурно поведение, тъй като не проверяват границите на целевия буфер и записват извън тях, ако има достатъчно байтове за това.

Алтернативна защита

Една често предлагана алтернатива са обвързаните версии, които записват до максималния размер на целевия буфер. На пръв поглед това изглежда като перфектно решение. За съжаление тези функции имат малък нюанс, който създава проблеми. При достигане на лимита, ако завършващият символ не се побира в последния байт, има сериозни грешки, когато четене на буфер.

Алтернативна защита

Този опростен пример показва опасностите, свързани с ненулеви терминиращи низове. Когато foo се постави в нормалния буфер, той ще излезе с нула, защото има допълнително място. Това е най-добрият сценарий. Ако байтовете в препълнения буфер на стека се намират в друг символен буфер или друг печатаем низ, функцията за печат ще продължи да чете, докато достигне последния символ на този низ.

Недостатъкът е, че езикът C не предлага стандартна и сигурна алтернатива на тези функции. Въпреки това има положителна странична полза от наличието на множество специфични за платформата имплементации на. OpenBSD предлага функциите strlcpy и strlcat, които работят подобно на функциите strn, с изключение на това, че съкращават низа с един символ по-рано, за да освободят пространство за нулев терминатор.

По подобен начин Microsoft предоставя свои собствени защитени реализации на често използвани функции за обработка на низове: strcpy_s, strcat_s и sprintf_s.

Предпочита се използването на безопасните алтернативи, изброени по-горе. Когато това не е възможно, при обработката на низовите буфери се извършва ръчна проверка на границите и нулево прекратяване.

Уязвимости при компилиране

Уязвимости при компилиране

Ако една несигурна функция оставя възможност за препълване на буфера на C, всичко не е загубено. При изпълнение на програмата компилаторите често създават случайни стойности, известни като канарчета, и ги поставят в стека, така че те са опасни. Проверката на стойността на канарчето спрямо първоначалната му стойност може да определи дали е възникнало препълване на буфера на Windows. Ако стойността е променена, програмата ще излезе или ще премине към състояние на грешка вместо към потенциално променения адрес на връщане.

Някои съвременни операционни системи осигуряват допълнителна защита срещу препълване на буфера под формата на неизпълними стекове и рандомизация на разпределението на адресното пространство (ASLR). Неизпълними стекове - Data Execution Prevention (DEP) - маркира стека, а в някои случаи и други структури, като области, в които кодът няма да бъде изпълняван. Това означава, че нападателят не може да инжектира код за експлойт в стека и да очаква успешното му изпълнение.

Преди да отстраните препълването на буфера, разопаковайте ASLR на компютъра. Тя е създадена за защита срещу ориентирано към връщане програмиране като заобиколен вариант на неизпълними стекове, при които съществуващите фрагменти от код се свързват заедно въз основа на техните адресни отмествания.

Работи чрез рандомизиране на областите от паметта на структурите, така че да е по-трудно да се открият техните отмествания. Ако тази защита съществуваше в края на 80-те години на миналия век, червеят Morris можеше да бъде избегнат. Това се дължи на факта, че той функционираше отчасти чрез запълване на буфера в протокола за пръстови идентификатори на UNIX с код на експлойта, след което го препълваше, за да промени адреса на връщане и да посочи към запълнения буфер.

ASLR и DEP затрудняват точното определяне на адреса, който трябва да се посочи, като правят тази област от паметта напълно нефункционална. Понякога дадена уязвимост се изплъзва през пукнатините, отворени за атака с препълване на буфера, въпреки наличието на контроли на ниво разработка, компилатор или операционна система.

Анализ на статичното покритие

В ситуация на препълване има две решаващи задачи. Първо, трябва да се идентифицира уязвимостта и да се определи базата от кодове за решаване на проблеми. Второ, да се гарантира, че всички версии на кода за уязвимост от препълване на буфера са заменени. В идеалния случай това ще започне с автоматично актуализиране на всички системи, свързани с интернет.

Не може да се предположи, че подобна актуализация ще осигури достатъчен обхват. Организациите или отделните лица могат да използват софтуер в системи с ограничен достъп до интернет, който изисква ръчно актуализиране. Това означава, че новината за актуализацията трябва да бъде разпространена до всички администратори, които използват софтуера, и че кръпката трябва да бъде лесно достъпна за изтегляне. Създаването и разпространението на кръпки се извършва възможно най-близо до откриването на уязвимостта, което гарантира, че времето на уязвимост е сведено до минимум.

Чрез използване на безопасни функции за обработка на буфери и подходящи функции за сигурност на компилатора и операционна система може да се разработи силна защита срещу препълване на буфера. Като се имат предвид тези стъпки, последователното идентифициране на недостатъците е важна стъпка за предотвратяване на експлойт.

Комбинирането на редове от изходния код в търсене на потенциални заплахи може да бъде досадно. Освен това винаги има вероятност човешкото око да пропусне нещо важно. Инструментите за статичен анализ се използват за осигуряване на качеството на кода, разработени са специално за откриване на уязвимости в сигурността по време на разработката.

Анализът на статичното покритие установява "червени следи" за потенциални препълвания на буфера. След това те се обработват и поправят отделно, така че да не се търси ръчно. Тези инструменти, съчетани с редовни проверки и познания за отстраняване на препълвания, позволяват да се идентифицират и отстранят по-голямата част от недостатъците, преди да е завършена разработката на софтуера.

Извършване на атака чрез root

Грешките в кодирането обикновено са причина за препълване на системата. Често срещани бъгове при разработването на приложения, които могат да доведат до него, включват невъзможността да се заделят достатъчно големи буфери и липсата на механизъм за проверка на тези проблеми. Тези грешки са особено проблематични в езиците C/C++, които нямат вградена защита срещу препълване и често са обект на атаки за препълване на буфера.

В някои случаи нападателят вкарва зловреден код в паметта, която е била повредена от препълване на буфера на стека. В други случаи просто се възползвате от съседното увреждане на паметта. Например програма, която изисква паролата на даден потребител, за да му даде достъп до системата. В кода по-долу правилната парола дава привилегии на root. Ако паролата е неправилна, програмата не предоставя привилегии на потребителя.

Програмата не предоставя привилегии на потребителя

В този пример тя дава привилегии на root, дори ако потребителят е въвел невалидна парола. В този случай атакуващият предоставя вход, който е по-дълъг, отколкото буферът може да побере, създавайки препълване, което презаписва паметта на целочисления пас. Следователно, въпреки невалидната парола, pass се задава на ненулева стойност и нападателят получава root привилегии.

Атакуване на зона за временно съхранение

Буферът е временна област за съхранение на данни. Когато програма или системен процес поставят повече данни, отколкото първоначално е било предвидено за съхранение, допълнителните данни преливат. Това води до изтичане на някои от тях в други системи, като повреждат или презаписват данните.

При атака с препълване допълнителните данни съдържат специални инструкции за действия, предвидени от хакер или злонамерен потребител, например предизвикват реакция, която поврежда файлове, променя данни или разкрива лична информация.

Атакуващият използва експлойт за препълване, за да се възползва от програма, която чака потребителски вход. Съществуват два вида буфери за препълване: базирани на стека и на купчината. Базираните на купчини са трудни за изпълнение и са най-рядко срещаните, като атакуват приложението чрез запълване на пространството, запазено за програмата.

Стекът е пространството в паметта, използвано от за съхраняване на потребителя вход. Тези преливания са по-вероятни за атакуващите, които използват дадено приложение, за да.

Съвременните компилатори обикновено предоставят възможност за проверка за препълване по време на компилиране/съставяне, но по време на изпълнение е доста трудно да се провери за този проблем без някакъв допълнителен механизъм за защита от изключения.

Извършване на атака чрез root

Варианти на програмата:

  1. Въвеждане: 12345678 (8 байта), програмата работи безпроблемно.
  2. Въведете: 123456789 (9 байта), появява се съобщение "Грешка при сегментиране", програмата се прекратява.

Съществува уязвимост, дължаща се на препълване, ако потребителският вход argv надхвърли 8 байта. За 32-битова система (4 байта) запълнете паметта с двойна дума (32 бита). Размерът на символа е 1 байт, така че ако заявите буфер с 5 байта, системата ще задели 2 двойни думи (8 байта). Ето защо въвеждането на повече от 8 байта ще доведе до препълване на буфера.

Съществуват подобни стандартни функции, които са технически по-малко уязвими. Например, strncpy (), strncat () и memcpy (). Проблемът с тези функции е, че, че отговорността за определяне на размера на буфера е отговорност на програмиста, а не на компилатора.

Всеки програмист на C/C++ трябва да знае проблема, преди да започне да кодира. Много генерирани проблеми в повечето случаи могат да бъдат защитени срещу препълване.

Опасности в C/C++

http://blogs.grammatech.com/eliminating-the-danger-of-uninitialized-variables

Потребителите на C трябва да избягват използването на опасни функции, които не проверяват границите, освен ако не са сигурни, че границите няма да бъдат превишени. Функциите, които трябва да се избягват в повечето случаи, за да се осигури защита, включват strcpy. Те трябва да бъдат заменени с функции като strncpy. Използването на функцията strlen трябва да се избягва, освен ако потребителят не е сигурен, че ще бъде намерен краен символ NIL. Фамилията scanf (): scanf (3), fscanf (3), sscanf (3), vscanf (3), vsscanf (3) и vfscanf (3) е опасна за използване и не се използва за изпращане на данни към низ без контрол на максималната дължина, "формат% s" е особено често срещана грешка.

Официално snprintf () не е стандартна функция на C в класификацията на ISO 1990. Тези системи не се защитават от препълване на буфера, а просто извикват sprintf директно. Известно е, че текущата версия на Linux snprintf работи правилно, т.е. действително спазва границата. Стойността на връщане на snprintf () също се променя.

Версия 2 на спецификацията на Unix (SUS) и стандартът C99 се различават по това, че връща snprintf (). Някои версии на snprintf не гарантират, че низът ще завърши с NIL, и ако низът е твърде дълъг, той изобщо няма да съдържа NIL. Библиотеката glib разполага с g_snprintf () със семантика на серийно връщане, която винаги завършва с NIL и, което е най-важно, винаги отчита дължината на буфера.

Препълване на буфера на комуникационния порт

Препълване на буфера на комуникационния порт

Понякога серийният порт съобщава за препълване на буфера. Този проблем може да се дължи на няколко фактора. Те включват скоростта на компютъра, скоростта на трансфер на използваните данни, размера на FIFO на серийния порт и размера на FIFO на устройството, което изпраща данни към серийния порт.

Контролът на потока ще изчака определен брой байтове в буфера, преди процесорът да изпрати съобщение или сигнал до друго устройство, за да спре предаването. При по-високи скорости на предаване серийният порт ще получи няколко байта от момента, в който нивото на буферния контрол на потока бъде достигнато и устройството спре да предава.

Тези допълнителни байтове ще бъдат повече, ако процесорът с висок приоритет управлява целевия процесор в реално време. Тъй като процесът на препълване на буфера на комуникационния порт има по-висок приоритет от прекъсването на VISA, процесорът няма да предприеме никакви действия, докато не приключи в реално време.

Настройката по подразбиране на VISA и Windows за 16-байтова FIFO е 14 байта, което оставя 2 байта във FIFO, когато устройството се опитва да изпрати съобщение от източника. При по-високи скорости на предаване на данни е възможно по-бавните компютри да получават повече от 4 байта едновременно, когато серийният порт запита процесора, изпращайки сигнал за спиране на предаването.

За да разрешите проблем при откриване на препълване на буфера на стека в Windows 10, трябва да отворите мениджъра на устройства. След това намерете COM порта, за който променяте настройките, и отворете свойствата. След това щракнете върху раздела "Разширени", ще се появи плъзгач, който променя размера на препълването на буфера на стека, така че UART да активира контрола на потока по-бързо.

Стойността по подразбиране е достатъчна в повечето случаи. Ако обаче възникне грешка при препълване, намалете стойността. Това ще доведе до изпращане на повече прекъсвания към процесора с по-бавни байтове към UART.

Сигурни техники за разработка

Техники за сигурно разработване

Практиките за сигурно разработване включват редовно тестване за откриване и коригиране на препълвания. Най-безопасен Начин да се избегне или предотврати това е да се използва автоматична защита на ниво език. Друга поправка е проверката на границите по време на изпълнение, която предотвратява препълването, като автоматично проверява дали данните, записани в буфера, са в приемливи граници.

Базираната в облака услуга на Veracode идентифицира уязвимости в кода, като препълване на буфера, така че разработчиците да могат да ги отстранят, преди да бъдат използвани. Уникалната за индустрията патентована технология за статично тестване на сигурността на приложенията (SAST) анализира приложенията, включително компонентите с отворен код и тези на трети страни, без да изисква достъп до тях.

SAST допълва моделирането на заплахите и прегледите на кода, извършвани от разработчиците, като открива грешки и пропуски в кода по-бързо и на по-ниска цена чрез автоматизация. Обикновено се стартира в началото на жизнения цикъл на разработката на софтуера, тъй като е по-лесно и по-евтино да се отстраняват проблеми, преди да се пристъпи към внедряване в производството.

SAST идентифицира критични уязвимости като SQL инжекция, скриптиране на кръстосани сайтове (XSS), грешка при препълване на буфера, състояния на необработени грешки и потенциални кухини. Освен това бинарната технология SAST предоставя полезна информация, която определя приоритетите в зависимост от сериозността и осигурява подробни инструкции да поправите.

Уязвимостта от препълване съществува от почти 3 десетилетия, но все още е тежка. Хакерите по целия свят продължават да го смятат за своя тактика по подразбиране поради огромния брой уязвими уеб приложения. Разработчиците и програмистите полагат огромни усилия, за да се преборят с този бич на ИТ технологиите, като измислят все повече начини да.

Основната идея на последния подход е да се приложи инструмент за корекция, който прави множество копия на адреса на връщане в стека и след това произволно определя местоположението на всички копия в допълнение към броя на. Всички дубликати се актуализират и тестват паралелно, така че всяко несъответствие между тях показва възможен опит за атака и предизвиква изключение.

Статии по темата