Разница между переполнением буфера и возвратом к атаке libc

Я хочу понять точную разницу между этими двумя типами атак. Из того, что я прочитал:

Переполнение буфера: он перезаписывает адрес ret в стеке, чтобы указать на другой раздел кода, в который вставлен вредоносный код. Так эффективно - здесь нам нужно изменить исходный код программы, чтобы фактически выполнить атаку.

Вернитесь к Libc- Вместо того, чтобы изменять исходный код, используются вызовы времени выполнения, предоставляемые библиотекой C (чтобы открыть оболочку). Здесь параметры, используемые для вызова функции, также передаются в буфере перезаписи, заканчивающемся после ret-части стека.

Является ли приведенное выше точное описание?

И еще один связанный с этим вопрос - возможно ли иметь переполнение буфера без фактического изменения исходного кода исходной программы? Возможно, если мы напишем новую программу и разрешим это изменять некоторые разделы памяти (это новый адрес ret в поврежденном стеке исходной программы). С другой стороны, я думаю, что это может быть невозможно из-за защиты памяти между процессами в ядре.

Ответ 1

В классическом переполнении буфера переполнение буфера стека заполнялось как исполняемым машинным кодом (называемым шеллкодом, так как он обычно вызывал процесс оболочки) и новый обратный адрес. Новый обратный адрес будет создан, чтобы указать обратно в буфер переполненного стека. Очевидно, что это требует знания или угадывания адреса этого стекового буфера в атакованном процессе.

В те дни макет памяти процессов, как правило, был очень детерминированным: расположение буфера стека обычно может быть хорошо предсказано злоумышленником (особенно, если они точно знали, на какую версию целевого программного обеспечения нападают). Чтобы улучшить шансы на успех, когда есть какие-то догадки, активный шелл-код часто предшествует большому количеству исполняемого машинного кода, который не выполнял никакой полезной операции - назывался "NOP-санный" или "NOP-слайд", где "NOP" является типичным названием инструкции машинного кода, которая выполняет "Без операции". Возвращение к точке в любом месте салазки NOP будет иметь желаемый эффект.

С другой стороны, эксплойт "return-to-libc" не приводит к тому, что захваченный процесс возвращается непосредственно к шеллкоду. Вместо этого он заставляет процесс возвращаться один за другим к началу цепочки библиотечных функций. Эти функции библиотеки могут непосредственно выполнять операции, которые требуется злоумышленнику, но чаще они будут использоваться для косвенного выполнения shellcode злоумышленника.

Ответ 2

Часть перезаписывания адреса ret распределяется между обеими атаками. Как видно из приведенного выше ответа, вы просто возвращались в код сборки, который вы написали. Этот ассемблерный код тогда, скажем, породил корневую пользовательскую оболочку.

В любой атаке вы не будете переписывать исходный код. Просмотр его с точки зрения сборки, исходный код находится в сегменте .text и всегда (или, по крайней мере, все время, когда я знаю) защищен от записи. То, что вы использовали, чтобы было использовать, было написать код, который вы предварительно собрали в сегменты памяти, а затем перейти к этому коду. Код наиболее типично находился в сегменте стека или рядом с ним, и, переполнив все, что вы выбрали, вы перенаправили трафик с адреса ret (скажем) там. Другие места атаки включали переменную окружения, которую вы предварительно создали и заполнили своим шеллкодом; или кучи, или указатели функций, или PLT. Введенный таким образом код обычно использует вызов system() для выполнения желаемого результата, но для этого вам нужно иметь возможность "выполнять" на областях памяти (или иметь записи перемещения, которые вы собираетесь использовать, объявляемые для записи).

Разница между двумя атаками заключается в том, что когда память была сделана в значительной степени невыполнимой, тип атаки a (переполнение стека) был довольно сильно завинчен. Попытка обойти этот тип атаки была тогда, когда вы пишете, вместо этого напрямую обращаться к функциям разделяемой библиотеки - иначе вам больше не нужно было писать свой код сначала в сегмент стека или в другом месте и выполнять его там. Тем не менее, я считаю, что атаки типа libc также в значительной степени исправлены; У меня нет удобных деталей; и, возможно, я ошибаюсь.

Если вы хотите увидеть, как в наши дни какие-либо из этих атак были сорваны или, по крайней мере, прочитали некоторые ключевые идеи, google "Smashing the Stack 2011" (или 2010), чтобы начать работу.

Ответ 3

Я бы сказал, что переполнение буфера - это класс ошибки программирования, а возврат к libc - это техника эксплуатации. Лучше всего не смешивать концепции вместе.

Например, вы можете использовать return to libc для использования уязвимости переполнения буфера. Или вы можете использовать другие методы, такие как возврат в .text или возврат к шеллкоду. И наоборот, вы также можете использовать return to libc для использования других ошибок, таких как строка формата.

Ответ 4

На самом деле, в атаке bufferoverflow вы вставляете вредоносный код, переопределяя указатель ret. Вам не нужно ничего менять для этого, так как вывод я не вижу разницы между упомянутыми атаками.

Пример:

char* str[5];
cin << str;

Это код, который можно разглядеть, если пользователь вставляет строку размером более 5 символов, все в следующем стеке будет перезаписано. И из-за того, что ret-ptr "находится ниже" в стеке, вы можете переопределить его, если вы получите правильное расстояние. Ваше намерение при переопределении - это указать на начало вашего ввода, в котором вы вставили вредоносный (ассемблерный) код, который будет выполнен, как только будет вызван ret-ptr, и будет выполнен "прыжок".