Переполнение буфера стека
В программном обеспечении переполнение буфера стека или переполнение буфера стека происходит, когда программа записывает по адресу памяти программы в стеке вызовов за пределами предполагаемой структуры данных, которая обычно представляет собой буфер фиксированной длины . [1] [2] Ошибки переполнения буфера стека возникают, когда программа записывает в буфер, расположенный в стеке, больше данных, чем фактически выделено для этого буфера. Это почти всегда приводит к повреждению соседних данных в стеке, а в случаях, когда переполнение было вызвано по ошибке, часто приводит к сбою программы или ее неправильной работе. Переполнение буфера стека — это тип более общей неисправности программирования, известной как переполнение буфера (или переполнение буфера). [1] Переполнение буфера в стеке с большей вероятностью приведет к срыву выполнения программы, чем переполнение буфера в куче, поскольку стек содержит адреса возврата для всех активных вызовов функций.
Переполнение буфера стека может быть вызвано намеренно в рамках атаки, известной как разрушение стека . Если затронутая программа работает с особыми привилегиями или принимает данные от ненадежных сетевых узлов (например, веб-сервера ), то ошибка представляет собой потенциальную уязвимость безопасности . Если буфер стека заполнен данными, полученными от недоверенного пользователя, то этот пользователь может повредить стек таким образом, что внедрит исполняемый код в работающую программу и возьмет на себя управление процессом. Это один из самых старых и надежных способов получения злоумышленниками несанкционированного доступа к компьютеру. [3] [4] [5]
Использование переполнения буфера стека [ править ]
Канонический метод использования переполнения буфера стека — перезаписать адрес возврата функции указателем на данные, контролируемые злоумышленником (обычно в самом стеке). [3] [6] Это иллюстрируется strcpy()
в следующем примере:
#include <string.h>
void foo(char *bar)
{
char c[12];
strcpy(c, bar); // no bounds checking
}
int main(int argc, char **argv)
{
foo(argv[1]);
return 0;
}
Этот код принимает аргумент из командной строки и копирует его в локальную переменную стека. c
. Это отлично работает для аргументов командной строки длиной менее 12 символов (как показано на рисунке B ниже). Любые аргументы длиной более 11 символов приведут к повреждению стека. (Максимальное безопасное количество символов на единицу меньше размера буфера, поскольку в языке программирования C строки завершаются нулевым байтовым символом. Таким образом, для хранения двенадцатисимвольного ввода требуется тринадцать байтов, за которыми следует ввод. нулевым байтом-дозорным. Затем нулевой байт перезаписывает ячейку памяти, которая находится на один байт за концом буфера.)
Стек программы в foo()
с различными входами:
На рисунке C выше, когда в командной строке указан аргумент размером более 11 байт. foo()
перезаписывает локальные данные стека, сохраненный указатель кадра и, самое главное, адрес возврата. Когда foo()
возвращается, он извлекает адрес возврата из стека и переходит к этому адресу (т. е. начинает выполнять инструкции с этого адреса). Таким образом, злоумышленник перезаписал адрес возврата указателем на буфер стека. char c[12]
, который теперь содержит данные, предоставленные злоумышленником. В реальном случае использования переполнения буфера стека строка «A» вместо этого будет шелл-кодом, подходящим для платформы и желаемой функции. Если бы эта программа имела особые привилегии (например, бит SUID был установлен для запуска от имени суперпользователя ), то злоумышленник мог бы использовать эту уязвимость для получения привилегий суперпользователя на пораженной машине. [3]
Злоумышленник также может изменить значения внутренних переменных, чтобы использовать некоторые ошибки. В этом примере:
#include <string.h>
#include <stdio.h>
void foo(char *bar)
{
float My_Float = 10.5; // Addr = 0x0023FF4C
char c[28]; // Addr = 0x0023FF30
// Will print 10.500000
printf("My Float value = %f\n", My_Float);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Memory map:
@ : c allocated memory
# : My_Float allocated memory
*c *My_Float
0x0023FF30 0x0023FF4C
| |
@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####
foo("my string is too long !!!!! XXXXX");
memcpy will put 0x1010C042 (little endian) in My_Float value.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
memcpy(c, bar, strlen(bar)); // no bounds checking...
// Will print 96.031372
printf("My Float value = %f\n", My_Float);
}
int main(int argc, char **argv)
{
foo("my string is too long !!!!! \x10\x10\xc0\x42");
return 0;
}
Обычно существует два метода изменения сохраненного адреса в стеке — прямой и косвенный. Злоумышленники начали разрабатывать непрямые атаки с меньшим количеством зависимостей, чтобы обойти меры защиты, принятые для уменьшения прямых атак. [7]
[ править ]
Ряд платформ имеют небольшие различия в реализации стека вызовов, которые могут повлиять на работу эксплойта переполнения буфера стека. Некоторые машинные архитектуры хранят адрес возврата верхнего уровня стека вызовов в регистре. Это означает, что любой перезаписанный адрес возврата не будет использоваться до более поздней очистки стека вызовов. Еще одним примером особенностей машины, которые могут повлиять на выбор методов эксплуатации, является тот факт, что большинство машинных архитектур RISC -типа не допускают невыровненного доступа к памяти. [8] В сочетании с фиксированной длиной машинных кодов операций это машинное ограничение может сделать практически невозможным реализацию техники перехода в стек (за единственным исключением, когда программа фактически содержит маловероятный код для явного перехода к регистру стека). [9] [10]
Стеки, которые растут [ править ]
В рамках темы переполнения буфера стека часто обсуждается, но редко встречается архитектура, в которой стек растет в противоположном направлении. Это изменение в архитектуре часто предлагается как решение проблемы переполнения буфера стека, поскольку любое переполнение буфера стека, происходящее в одном и том же кадре стека, не может перезаписать указатель возврата. Однако любое переполнение, которое происходит в буфере из предыдущего кадра стека, все равно перезапишет указатель возврата и позволит злонамеренно использовать ошибку. [11] Например, в приведенном выше примере указатель возврата для foo
не будет перезаписан, поскольку переполнение фактически происходит внутри кадра стека для memcpy
. Однако поскольку буфер, который переполняется во время вызова memcpy
находится в предыдущем кадре стека, указатель возврата для memcpy
будет иметь числовой более высокий адрес памяти, чем адрес буфера. Это означает, что вместо указателя возврата для foo
перезаписывается, указатель возврата для memcpy
будет перезаписано. В лучшем случае это означает, что увеличение стека в противоположном направлении изменит некоторые детали того, как можно использовать переполнение буфера стека, но не уменьшит значительно количество уязвимых ошибок. [ нужна ссылка ]
Схемы защиты [ править ]
ряд схем целостности потока управления За прошедшие годы был разработан для предотвращения злонамеренного использования переполнения буфера стека. Обычно их можно разделить на три категории:
- Обнаружить, что произошло переполнение буфера стека, и таким образом предотвратить перенаправление указателя инструкции на вредоносный код.
- Предотвратите выполнение вредоносного кода из стека без непосредственного обнаружения переполнения буфера стека.
- Рандомизируйте пространство памяти так, чтобы поиск исполняемого кода стал ненадежным.
Стек канарейок [ править ]
Канарейки стека, названные по аналогии с канарейкой в угольной шахте , используются для обнаружения переполнения буфера стека до того, как может произойти выполнение вредоносного кода. Этот метод работает путем помещения небольшого целого числа, значение которого случайно выбирается при запуске программы, в память непосредственно перед указателем возврата стека. Большинство переполнений буфера перезаписывают память с нижних адресов памяти на более высокие, поэтому, чтобы перезаписать указатель возврата (и, таким образом, получить контроль над процессом), необходимо также перезаписать канареечное значение. Это значение проверяется, чтобы убедиться, что оно не изменилось, прежде чем подпрограмма использует указатель возврата в стеке. [2] Этот метод может значительно увеличить сложность использования переполнения буфера стека, поскольку он вынуждает злоумышленника получить контроль над указателем инструкции некоторыми нетрадиционными способами, такими как повреждение других важных переменных в стеке. [2]
Неисполняемый стек [ править ]
Другой подход к предотвращению использования переполнения буфера стека заключается в применении политики памяти в области памяти стека, которая запрещает выполнение из стека ( W^X , «Write XOR Execute»). Это означает, что для выполнения шеллкода из стека злоумышленник должен либо найти способ отключить защиту выполнения из памяти, либо найти способ поместить полезную нагрузку шеллкода в незащищенную область памяти. Этот метод становится все более популярным сейчас, когда аппаратная поддержка флага невыполнения доступна в большинстве процессоров настольных компьютеров.
Хотя этот метод предотвращает канонический эксплойт разрушения стека, переполнение стека можно использовать и другими способами. Во-первых, принято находить способы хранения шелл-кода в незащищенных областях памяти, таких как куча, и поэтому мало что нужно менять в способе эксплуатации. [12]
Другая атака — это так называемый метод возврата к libc для создания шеллкода. В этой атаке вредоносная полезная нагрузка загружает стек не шелл-кодом, а соответствующим стеком вызовов, так что выполнение переносится на цепочку вызовов стандартной библиотеки, обычно с эффектом отключения защиты от выполнения памяти и разрешения запуска шелл-кода в обычном режиме. [13] Это работает, потому что выполнение никогда не перенаправляется в сам стек.
Вариантом return-to-libc является возвратно-ориентированное программирование (ROP), которое устанавливает серию адресов возврата, каждый из которых выполняет небольшую последовательность выбранных машинных инструкций в существующем программном коде или системных библиотеках, последовательность которых заканчивается возвратом. Каждый из этих так называемых гаджетов перед возвратом выполняет некоторые простые манипуляции с регистрами или аналогичные действия, а объединение их вместе достигает целей злоумышленника. Можно даже использовать «безвозвратное» программирование, ориентированное на возврат, используя инструкции или группы инструкций, которые ведут себя во многом как инструкция возврата. [14]
Рандомизация [ править ]
Вместо отделения кода от данных другой метод снижения риска заключается во введении рандомизации в пространство памяти исполняемой программы. Поскольку злоумышленнику необходимо определить, где находится исполняемый код, который можно использовать, либо предоставляется исполняемая полезная нагрузка (со стеком исполняемых файлов), либо она создается с использованием повторного использования кода, например, в ret2libc или возвратно-ориентированном программировании (ROP). Концепция случайного расположения памяти не позволит злоумышленнику узнать, где находится какой-либо код. Однако реализации обычно не все рандомизируют; обычно сам исполняемый файл загружается по фиксированному адресу, и, следовательно, даже когда ASLR (рандомизация структуры адресного пространства) сочетается со стеком неисполняемых файлов, злоумышленник может использовать эту фиксированную область памяти. Следовательно, все программы должны быть скомпилированы с использованием PIE (независимых от позиции исполняемых файлов), чтобы даже эта область памяти была рандомизированной. Энтропия рандомизации различается от реализации к реализации, и достаточно низкая энтропия сама по себе может быть проблемой с точки зрения грубого перебора рандомизированного пространства памяти.
Обход контрмер [ править ]
Предыдущие меры по смягчению последствий усложняют этапы эксплуатации. Но воспользоваться переполнением буфера стека все же можно, если присутствуют какие-либо уязвимости или соблюдены некоторые условия.
Канареечный обход стека [ править ]
Утечка информации с использованием строки уязвимости форматной
Злоумышленник может воспользоваться уязвимостью строки формата для раскрытия ячеек памяти уязвимой программы. [15]
Обход неисполняемого стека [ править ]
Когда функция предотвращения выполнения данных включена и запрещает любой доступ к стеку на выполнение, злоумышленник все равно может использовать перезаписанный адрес возврата (указатель инструкции) для указания на данные в сегменте кода ( .text в Linux) или любой другой исполняемый раздел программы. Цель состоит в том, чтобы повторно использовать существующий код. [16]
Реп-цепочка [ править ]
Состоит из небольшой перезаписи указателя возврата перед инструкцией возврата (ret в x86) программы. Инструкции между новым указателем возврата и инструкцией возврата будут выполнены, и инструкция возврата вернется к полезной нагрузке, контролируемой эксплуататором. [16] [ нужны разъяснения ]
Цепочка прыжков [ править ]
Переходно-ориентированное программирование — это метод, который использует инструкции перехода для повторного использования кода вместо инструкции ret. [17]
Обход рандомизации [ править ]
Ограничением реализации ASLR в 64-битных системах является то, что она уязвима для атак раскрытия памяти и утечки информации. Злоумышленник может запустить ROP, раскрыв одиночный адрес функции, используя атаку утечки информации. В следующем разделе описывается аналогичная существующая стратегия взлома защиты ASLR. [18]
Известные примеры [ править ]
- Червь Морриса, появившийся в 1988 году, частично распространился за счет использования переполнения буфера стека на сервере Unix фингер- . [1]
- Червь Slammer в 2003 году распространился, используя переполнение буфера стека на . SQL-сервере Microsoft [2]
- Червь Blaster в 2003 году распространился, используя переполнение буфера стека в Microsoft DCOM . службе
- Червь Witty в 2004 году распространился, используя переполнение буфера стека в Internet Security Systems агенте BlackICE Desktop Agent . [3]
- Есть несколько примеров Wii, позволяющих запускать произвольный код в неизмененной системе. «Сумеречный взлом», который предполагает присвоение длинного имени лошади главного героя в The Legend of Zelda: Twilight Princess . [19] и «Smash Stack» для Super Smash Bros. Brawl , который предполагает использование SD-карты для загрузки специально подготовленного файла во внутриигровой редактор уровней. Хотя оба могут использоваться для выполнения любого произвольного кода, последний часто используется для простой перезагрузки самой Brawl с модификациями . примененными [20]
См. также [ править ]
- Киберпреступность
- Исполнительный щит
- Переполнение кучи
- Целочисленное переполнение
- NX Bit – бит запрета выполнения для областей памяти.
- Linux с повышенной безопасностью
- Stack overflow – когда сам стек переполняется
- Нарушение правил хранения
Ссылки [ править ]
- ↑ Перейти обратно: Перейти обратно: а б Фитен, Уильям Л.; Сикорд, Роберт (27 марта 2007 г.). «ВТ-МБ. Нарушение границ памяти» . СЕРТ США .
- ↑ Перейти обратно: Перейти обратно: а б с Дауд, Марк; Макдональд, Джон; Шу, Джастин (ноябрь 2006 г.). Искусство оценки безопасности программного обеспечения . Эддисон Уэсли . стр. 169–196. ISBN 0-321-44442-6 .
- ↑ Перейти обратно: Перейти обратно: а б с Леви, Элиас (8 ноября 1996 г.). «Разбиваем стек ради удовольствия и прибыли» . Фрак . 7 (49): 14.
- ^ Пинкус, Дж.; Бейкер, Б. (июль – август 2004 г.). «Не только разрушение стека: последние достижения в использовании переполнения буфера» (PDF) . Журнал IEEE по безопасности и конфиденциальности . 2 (4): 20–27. дои : 10.1109/MSP.2004.36 . S2CID 6647392 .
- ^ Буребиста. «Переполнение стека» (PDF) . Архивировано из оригинала (PDF) 28 сентября 2007 г. [ мертвая ссылка ]
- ^ Бертран, Луи (2002). «OpenBSD: исправьте ошибки, защитите систему» . MUSESS '02: Симпозиум по разработке программного обеспечения Университета Макмастера . Архивировано из оригинала 30 сентября 2007 г.
- ^ Куперман, Бенджамин А.; Бродли, Карла Э.; Оздоганоглу, Хильми; Виджайкумар, Теннесси; Джалоте, Анкит (ноябрь 2005 г.). «Обнаружение и предотвращение атак переполнения буфера стека» . Коммуникации АКМ . 48 (11): 50–56. дои : 10.1145/1096000.1096004 . ISSN 0001-0782 . S2CID 120462 .
- ^ пр1. «Использование уязвимостей переполнения буфера SPARC» .
{{cite web}}
: CS1 maint: числовые имена: список авторов ( ссылка ) - ^ Любопытно (08 января 2005 г.). «Реверс-инжиниринг — взлом PowerPC в Mac OS X с помощью GDB» . Фрак . 11 (63): 16.
- ^ Соварель, Ана Нора; Эванс, Дэвид; Пол, Нафанаил. Где ФЕБ? Эффективность рандомизации набора команд (отчет).
- ^ Жодиак (28 декабря 2001 г.). «Переполнение HP-UX (PA-RISC 1.1)» . Фрак . 11 (58): 11.
- ^ Фостер, Джеймс С.; Осипов, Виталий; Бхалла, Ниш; Хайнен, Нильс (2005). Атаки на переполнение буфера: обнаружение, использование, предотвращение (PDF) . Соединенные Штаты Америки: ISBN Syngress Publishing, Inc. 1-932266-67-4 .
- ^ Нергал (28 декабря 2001 г.). «Продвинутые эксплойты return-into-lib(c): пример PaX» . Фрак . 11 (58): 4.
- ^ Чековей, С.; Дави, Л.; Дмитриенко А.; Садеги, Арканзас; Шахам, Х.; Винэнди, М. (октябрь 2010 г.). «Возвратно-ориентированное программирование без возвратов». Материалы 17-й конференции ACM «Компьютерная и коммуникационная безопасность — CCS '10» . стр. 559–572. дои : 10.1145/1866307.1866370 . ISBN 978-1-4503-0245-6 . S2CID 207182734 .
- ^ Батт, Мухаммад Ариф; Аджмал, Зарафшан; Хан, Зафар Икбал; Идрис, Мухаммед; Джавед, Ясир (январь 2022 г.). «Углубленный обзор методов обхода предотвращения переполнения буфера» . Прикладные науки . 12 (26): 6702. дои : 10.3390/app12136702 . ISSN 2076-3417 .
- ↑ Перейти обратно: Перейти обратно: а б Батт, Мухаммад Ариф; Аджмал, Зарафшан; Хан, Зафар Икбал; Идрис, Мухаммед; Джавед, Ясир (январь 2022 г.). «Углубленный обзор методов обхода предотвращения переполнения буфера» . Прикладные науки . 12 (13): 12–13. дои : 10.3390/app12136702 . ISSN 2076-3417 .
- ^ Аппаратная безопасность систем (на французском языке). 03.09.2022.
- ^ Батт, Мухаммад Ариф; Аджмал, Зарафшан; Хан, Зафар Икбал; Идрис, Мухаммед; Джавед, Ясир (январь 2022 г.). «Углубленный обзор методов обхода предотвращения переполнения буфера» . Прикладные науки . 12 (16): 6702. дои : 10.3390/app12136702 . ISSN 2076-3417 .
- ^ «Сумеречный взлом — WiiBrew» . Wiibrew.org . Проверено 18 января 2018 г.
- ^ «Разбить стек — WiiBrew» . Wiibrew.org . Проверено 18 января 2018 г.