16.2.2 Внедрение кода Большая часть программного обеспечения не является вредоносной, но, тем не менее, она может представлять серьезную угрозу безопасности из-за атаки, в которую вводится или модифицируется исполняемый код. Даже иное доброкачественное программное обеспечение может скрывать уязвимости, которые, в случае использования, позволяют злоумышленнику захватить программный код, подорвать существующий поток кода или полностью перепрограммировать, предоставив новый код. Атаки с использованием кода почти всегда являются результатом плохих или небезопасных парадигм программирования, обычно на языках низкого уровня, таких как C или C ++, которые обеспечивают прямой доступ к памяти через указатели. Этот прямой доступ к памяти в сочетании с необходимостью тщательно выбирать размеры буферов памяти и принимать меры предосторожности, чтобы не превысить их, может привести к повреждению памяти, когда буферы памяти не обрабатываются должным образом. В качестве примера рассмотрим простейший вектор внедрения кода - переполнение буфера. Программный рисунок 16.2 иллюстрирует такое переполнение, которое возникает из-за неограниченной операции копирования, вызова strcpy ().
#include
#define BUFFER SIZE 0
int main(int argc, char *argv[]) {
int j = 0;
char buffer[BUFFER SIZE];
int k = 0;
if (argc < 2) {
return -1;
}
strcpy(buffer,argv[1]);
printf("K is %d, J is %d, buffer is %s∖n", j,k,buffer);
return 0;
}
}
Рисунок 16.2. Программа C с условием переполнения буфера.
Функция копирует безотносительно к размеру буфера, останавливаясь, только когда встречается байт NULL (∖ 0). Если такой байт происходит до того, как будет достигнут размер буфера, программа будет работать так, как ожидается. Но копия может легко превысить размер буфера - что тогда? Ответ заключается в том, что результат переполнения во многом зависит от длины потока переполнения и содержимого переполнения (рис. 16.3). Она значительно отличается от кода, сгенерированного компилятором, который может быть оптимизирован способами, которые влияют на результат: оптимизации часто включают в себя корректировки макета памяти (обычно перестановка или заполнение переменных).
Если переполнение будет очень маленьким (только меньше, чем размер буфера), это может привести к тому, что он останется незамеченным. Это связано с тем, что распределение байтов размера буфера часто будет дополнено границей, определенной архитектурой (обычно 8 или 16 байтов). Заполнение - это неиспользуемая память, и поэтому переполнение в ней, хотя и технически выходит за пределы, не оказывает вредного воздействия.
Если переполнение превышает заполнение, следующая автоматическая переменная в стеке будет перезаписана переполненным содержимым. Результат в этом случае будет зависеть от точного позиционирования переменной и ее семантики (например, если она используется в логическом условии, которое затем может быть перевернуто).
Если переполнение значительно превышает заполнение, весь кадр стека текущей функции перезаписывается. В самом верху фрейма находится адрес возврата функции, доступ к которому происходит после возврата функции. Поток программы нарушается и может быть перенаправлен злоумышленником в другую область памяти, включая память, контролируемую злоумышленником (например, сам входной буфер или стек или кучу). Затем выполняется введенный код, позволить злоумышленнику выполнить произвольный код в качестве эффективного идентификатора процесса.
Обратите внимание, что безопасный программист мог выполнить ограниченную проверку размера argv [1], используя функцию strncpy () вместо strcpy (), заменив строку «strcpy (buffer, argv [1]);» с «strncpy (буфер, argv [1], sizeof (буфер) -1);». К сожалению, хорошая проверка границ - это исключение других типов. strcpy () - один из известных классов уязвимых функций, который включает printf (), gets () и другие функции безотносительно к размеру буфера. Но даже варианты с учетом размера могут скрывать уязвимости в сочетании с арифметическими операциями над целыми числами конечной длины, что может привести к переполнению целых чисел.
На этом этапе опасности, присущие простому упущению в поддержании буфера, должны быть четко видны. Брайан Кернингем и Деннис Ритчи (в своей книге «Язык программирования C») называли возможный результат «неопределенным поведением», но злоумышленник может принудительно привести к совершенно непредсказуемому поведению, как это было впервые продемонстрировано червем Морриса (и задокументировано в RFC1135:
https : //tools.ietf.org/html/rfc1135).
Однако лишь несколько лет спустя в статье № 49 журнала Phrack («Разбивая стек ради удовольствия и прибыли» http://phrack.org/issues/49/14.html) была введена техника эксплуатации массы, высвобождая поток подвигов. Чтобы добиться внедрения кода, сначала должен быть введен код. Сначала злоумышленник записывает короткий фрагмент кода, например:
void func (void) {
execvp («/ bin / sh», «/ bin / sh», NULL);
}
Используя системный вызов execvp (), этот сегмент кода создается как адский процесс. Если атакованная программа запускается с правами root, эта вновь созданная оболочка получит полный доступ к системе. Конечно, сегмент кода может делать все, что позволено привилегиям атакующего процесса. Сегмент кода затем компилируется во встроенную сборку двоичного кода и обрабатывается для преобразования в двоичный поток. Скомпилированная форма часто предлагает интерпретировать код оболочки, потому что он выполняет функции, вызывая отказ, даже потому, что он иногда вызывается из-за неадекватного набора команд, даже потому, что ему пришлось даже иногда вызывать неприятные ситуации. Эксплойт с шелл-кодом показан на рисунке 16.4. Код, который кратко используется только для перенаправления выполнения в другое место, во многом похож на батут, «подпрыгивающий» поток кода из одного места в другое.
Фактически, существуют компиляторы шелл-кода (примером является проект «MetaSploit»), которые также заботятся о таких особенностях, как обеспечение того, чтобы код был компактным и не содержал пустых байтов (в случае использования через копирование строки, что заканчиваться на NULL). Такой компилятор может даже маскировать шелл-код как буквенно-цифровые символы. Если злоумышленнику удалось перезаписать адрес возврата (или любой указатель на функцию, например, указатель на VTable), то все, что требуется (в простом случае), - это перенаправить адрес, чтобы он указывал на предоставленный шелл-код, который обычно загружается как часть пользовательского ввода, через переменную среды или через какой-либо файл или сетевой ввод. Если предположить, что сексист не будет смягчен (как описано ниже), этого достаточно для выполнения кода оболочки и успеха хакера в атаке. Вопросы выравнивания часто обрабатываются путем добавления последовательности инструкций NOP перед кодом оболочки. Результат известен как сани NOP, поскольку он заставляет выполнение «скользить» вниз по инструкциям NOP до тех пор, пока полезная нагрузка не будет обнаружена и выполнена. Этот пример атаки с переполнением буфера показывает, что для распознавания эксплуатируемого кода, а затем для его использования необходимы значительные знания и навыки программирования. К сожалению, для запуска атак безопасности не требуется больших программ. Скорее всего, один хакер может определить ошибку и затем написать эксплойт. Любой с рудиментарными компьютерными навыками и доступом к эксплойту - так называемый скрипт-детектив - может затем попытаться запустить атаку на целевые системы. Атака переполнения буфера особенно пагубна, потому что она может быть запущена между системами и может проходить через разрешенные каналы связи. Такая атака может происходить в рамках протокола, который предполагается использовать для связи с целевой машиной, и поэтому их трудно обнаружить и предотвратить. Они могут даже обойти защиту, добавленную брандмауэрами (Раздел 16.6.6). Обратите внимание, что переполнение буфера - это только один из нескольких векторов, которыми можно манипулировать для внедрения кода. Переполнения также могут быть использованы, когда они происходят в куче. Использование буферов памяти после их освобождения, а также переопределение их (вызов функции free () дважды) также может привести к внедрению кода.
Достарыңызбен бөлісу: |