stringtranslate.com

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

В программном обеспечении переполнение буфера стека или переполнение буфера стека происходит, когда программа записывает данные по адресу памяти в стеке вызовов программы за пределами предполагаемой структуры данных, которая обычно является буфером фиксированной длины . [1] [2] Ошибки переполнения буфера стека возникают, когда программа записывает больше данных в буфер, расположенный в стеке, чем фактически выделено для этого буфера. Это почти всегда приводит к повреждению смежных данных в стеке, а в случаях, когда переполнение было вызвано ошибкой, часто приводит к сбою программы или ее неправильной работе. Переполнение буфера стека — это тип более общей программной ошибки, известной как переполнение буфера (или переполнение буфера). [1] Переполнение буфера в стеке с большей вероятностью сорвет выполнение программы, чем переполнение буфера в куче, поскольку стек содержит адреса возврата для всех активных вызовов функций.

Переполнение буфера стека может быть вызвано преднамеренно как часть атаки, известной как разбивание стека . Если затронутая программа работает с особыми привилегиями или принимает данные от ненадежных сетевых хостов (например, веб-сервера ), то ошибка является потенциальной уязвимостью безопасности . Если буфер стека заполнен данными, предоставленными ненадежным пользователем, то этот пользователь может повредить стек таким образом, чтобы внедрить исполняемый код в работающую программу и получить контроль над процессом. Это один из старейших и наиболее надежных методов, с помощью которых злоумышленники могут получить несанкционированный доступ к компьютеру. [3] [4] [5]

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

Канонический метод эксплуатации переполнения буфера на основе стека заключается в перезаписи адреса возврата функции указателем на контролируемые злоумышленником данные (обычно на самом стеке). [3] [6] Это проиллюстрировано strcpy()в следующем примере:

#include <строка.h> void foo ( char * bar ) { char c [ 12 ];     strcpy ( c , bar ); // проверка границ отсутствует }  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 ; // Адрес = 0x0023FF4C char c [ 28 ]; // Адрес = 0x0023FF30           // Выведет 10.500000 printf ( "My Float value = %f \n " , My_Float );   /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Карта памяти:  @ : c выделенная память  # : My_Float выделенная память *c *My_Float  0x0023FF30 0x0023FF4C  | |  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####  foo("моя строка слишком длинная !!!!! XXXXX"); memcpy поместит 0x1010C042 (прямой порядок байтов) в значение My_Float.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ memcpy ( c , bar , strlen ( bar )); // проверка границ не производится...    // Выведет 96.031372 printf ( "Мое значение Float = %f \n " , My_Float ); }  int main ( int argc , char ** argv ) { foo ( "моя строка слишком длинная !!!!! \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"). Это означает, что для выполнения шелл-кода из стека злоумышленник должен либо найти способ отключить защиту выполнения из памяти, либо найти способ поместить полезную нагрузку шелл-кода в незащищенную область памяти. Этот метод становится все более популярным теперь, когда аппаратная поддержка флага no-execute доступна в большинстве процессоров для настольных ПК.

Хотя этот метод предотвращает канонический эксплойт, разрушающий стек, переполнение стека может эксплуатироваться другими способами. Во-первых, обычно находят способы хранить шелл-код в незащищенных областях памяти, таких как куча, и поэтому очень мало нужно менять в способе эксплуатации. [12]

Другая атака — так называемый метод возврата к libc для создания шелл-кода. В этой атаке вредоносная полезная нагрузка загрузит стек не шелл-кодом, а надлежащим стеком вызовов, так что выполнение будет направлено на цепочку вызовов стандартной библиотеки, обычно с эффектом отключения защиты выполнения памяти и разрешения шелл-коду работать как обычно. [13] Это работает, потому что выполнение на самом деле никогда не направляется на сам стек.

Вариантом return-to-libc является возвратно-ориентированное программирование (ROP), которое устанавливает ряд адресов возврата, каждый из которых выполняет небольшую последовательность тщательно отобранных машинных инструкций в существующем программном коде или системных библиотеках, последовательность, которая заканчивается возвратом. Эти так называемые гаджеты выполняют некоторые простые манипуляции с регистрами или аналогичное выполнение перед возвратом, и их объединение достигает целей злоумышленника. Можно даже использовать "возвратно-ориентированное" возвратно-ориентированное программирование, эксплуатируя инструкции или группы инструкций, которые ведут себя во многом как инструкция возврата. [14]

Рандомизация

Вместо того чтобы отделять код от данных, другой метод смягчения заключается в том, чтобы ввести рандомизацию в пространство памяти исполняемой программы. Поскольку злоумышленнику необходимо определить, где находится исполняемый код, который может быть использован, либо предоставляется исполняемая полезная нагрузка (с исполняемым стеком), либо она создается с использованием повторного использования кода, например, в ret2libc или возвратно-ориентированном программировании (ROP). Рандомизация структуры памяти, как концепция, не позволит злоумышленнику узнать, где находится какой-либо код. Однако реализации, как правило, не будут рандомизировать все; обычно сам исполняемый файл загружается по фиксированному адресу, и, следовательно, даже когда ASLR (рандомизация структуры адресного пространства) сочетается с неисполняемым стеком, злоумышленник может использовать эту фиксированную область памяти. Поэтому все программы должны быть скомпилированы с PIE (позиционно-независимые исполняемые файлы), так что даже эта область памяти будет рандомизирована. Энтропия рандомизации различается от реализации к реализации, и достаточно низкая энтропия сама по себе может стать проблемой с точки зрения перебора рандомизированного пространства памяти.

Обход контрмер

Предыдущие меры по смягчению усложняют этапы эксплуатации. Но все еще возможно эксплуатировать переполнение буфера стека, если присутствуют некоторые уязвимости или если выполнены некоторые условия. [15]

Стек канареечный обход

Утечка информации с использованием уязвимости форматной строки

Злоумышленник может использовать уязвимость форматной строки для раскрытия местоположений памяти в уязвимой программе. [16]

Неисполняемый обход стека

Когда включено предотвращение выполнения данных , чтобы запретить любой доступ к стеку для выполнения, злоумышленник все равно может использовать перезаписанный адрес возврата (указатель инструкций), чтобы указать на данные в сегменте кода ( .text в Linux) или любом другом исполняемом разделе программы. Цель состоит в том, чтобы повторно использовать существующий код. [17]

Цепь канатная

Заключается в перезаписи указателя возврата немного перед инструкцией возврата (ret в x86) программы. Инструкции между новым указателем возврата и инструкцией возврата будут выполнены, а инструкция возврата вернет полезную нагрузку, контролируемую эксплуататором. [17] [ необходимо разъяснение ]

Цепь Джоп

Программирование, ориентированное на переходы, — это метод, который использует инструкции перехода для повторного использования кода вместо инструкции возврата. [18]

Обход рандомизации

Ограничением реализации ASLR на 64-битных системах является то, что она уязвима для атак раскрытия памяти и утечки информации. Злоумышленник может запустить ROP, раскрыв один адрес функции, используя атаку утечки информации. В следующем разделе описывается аналогичная существующая стратегия для взлома защиты ASLR. [19]

Известные примеры

Смотрите также

Ссылки

  1. ^ ab Фитен, Уильям Л.; Сикорд, Роберт (2007-03-27). "VT-MB. Нарушение границ памяти". US CERT .
  2. ^ abc Dowd, Mark; McDonald, John; Schuh, Justin (ноябрь 2006 г.). Искусство оценки безопасности программного обеспечения . Addison Wesley . стр. 169–196. ISBN 0-321-44442-6.
  3. ^ abc Леви, Элиас (1996-11-08). «Разбиваем стек ради удовольствия и прибыли». Phrack . 7 (49): 14.
  4. ^ Pincus, J.; Baker, B. (июль–август 2004 г.). «Beyond Stack Smashing: Recent Advances in Exploiting Buffer Overruns» (PDF) . Журнал IEEE Security and Privacy . 2 (4): 20–27. doi :10.1109/MSP.2004.36. S2CID  6647392.
  5. ^ Burebista. "Stack Overflows" (PDF) . Архивировано из оригинала (PDF) 28 сентября 2007 г. [ мертвая ссылка ]
  6. ^ Бертран, Луи (2002). "OpenBSD: Fix the Bugs, Secure the System". MUSESS '02: Симпозиум по программной инженерии Университета Макмастера . Архивировано из оригинала 2007-09-30.
  7. ^ Куперман, Бенджамин А.; Бродли, Карла Э.; Оздоганоглу, Хилми; Виджайкумар, ТН; Джалоте, Анкит (ноябрь 2005 г.). «Обнаружение и предотвращение атак переполнения стекового буфера». Сообщения ACM . 48 (11): 50–56. doi :10.1145/1096000.1096004. ISSN  0001-0782. S2CID  120462.
  8. ^ pr1. «Использование уязвимости переполнения буфера SPARC».{{cite web}}: CS1 maint: числовые имена: список авторов ( ссылка )
  9. ^ Curious (2005-01-08). "Обратная разработка - взлом PowerPC на Mac OS X с помощью GDB". Phrack . 11 (63): 16.
  10. ^ Соварел, Ана Нора; Эванс, Дэвид; Пол, Натанаэль. Где FEEB? Эффективность рандомизации набора инструкций (отчет).
  11. ^ Zhodiac (28.12.2001). "HP-UX (PA-RISC 1.1) переполняется". Phrack . 11 (58): 11.
  12. ^ Фостер, Джеймс С.; Осипов, Виталий; Бхалла, Ниш; Хайнен, Нильс (2005). Атаки переполнения буфера: обнаружение, эксплуатация, предотвращение (PDF) . Соединенные Штаты Америки: Syngress Publishing, Inc. ISBN 1-932266-67-4.
  13. ^ Nergal (28.12.2001). «Расширенные эксплойты return-into-lib(c): исследование случая PaX». Phrack . 11 (58): 4.
  14. ^ Checkoway, S.; Davi, L.; Дмитриенко, A.; Sadeghi, AR; Shacham, H.; Winandy, M. (октябрь 2010 г.). "Return-Oriented Programming without Returns". Труды 17-й конференции ACM по компьютерной и коммуникационной безопасности - CCS '10 . стр. 559–572. doi :10.1145/1866307.1866370. ISBN 978-1-4503-0245-6. S2CID  207182734.
  15. ^ Шошитаишвили, Ян. «Ошибки памяти, безопасность программ». pwn college . Получено 2024-09-07 .
  16. ^ Батт, Мухаммад Ариф; Аджмал, Зарафшан; Хан, Зафар Икбал; Идрис, Мухаммад; Джавед, Ясир (январь 2022 г.). «Углубленный обзор методов обхода переполнения буфера». Прикладные науки . 12 (26): 6702. doi : 10.3390/app12136702 . ISSN  2076-3417.
  17. ^ ab Butt, Muhammad Arif; Ajmal, Zarafshan; Khan, Zafar Iqbal; Idrees, Muhammad; Javed, Yasir (январь 2022 г.). «Углубленный обзор методов обхода переполнения буфера». Прикладные науки . 12 (13): 12–13. doi : 10.3390/app12136702 . ISSN  2076-3417.
  18. ^ Sécurité matérielle des systèmes (на французском языке). 03.09.2022.
  19. ^ Батт, Мухаммад Ариф; Аджмал, Зарафшан; Хан, Зафар Икбал; Идрис, Мухаммад; Джавед, Ясир (январь 2022 г.). «Углубленный обзор методов обхода переполнения буфера». Прикладные науки . 12 (16): 6702. doi : 10.3390/app12136702 . ISSN  2076-3417.
  20. ^ "Twilight Hack - WiiBrew". wiibrew.org . Получено 18.01.2018 .
  21. ^ "Smash Stack - WiiBrew". wiibrew.org . Получено 18.01.2018 .