Пример переполнения буфера в Debian 2.6 не работает
Я пытаюсь заставить пример использования буфера (example3.c из http://insecure.org/stf/smashstack.html) работать над версией Debian Lenny 2.6. Я знаю, что версия gcc и версия ОС отличается от версии, используемой Aleph One. Я отключил все механизмы защиты стека, используя аргументы -fno-stack-protector и sysctl -w kernel.randomize_va_space=0. Чтобы учесть различия в моих настройках и настройках Aleph One, я ввел два параметра: offset1 -> Offset от переменной buffer1 до адреса возврата и offset2 -> сколько байтов нужно пропустить, чтобы пропустить оператор. Я пытался выяснить эти параметры, анализируя ассемблерный код, но безуспешно. Итак, я написал сценарий оболочки, который в основном запускает программу переполнения буфера с одновременными значениями offset1 и offset2 из (1-60). Но, к моему большому удивлению, я все еще не могу сломать эту программу. Было бы замечательно, если бы кто-то мог направить меня к тому же. Я приложил код и вывод сборки для рассмотрения. Извините за действительно длинный пост:)
Благодарю.
// Modified example3.c from Aleph One paper - Smashing the stack
void function(int a, int b, int c, int offset1, int offset2) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = (int *)buffer1 + offset1;// how far is return address from buffer ?
(*ret) += offset2; // modify the value of return address
}
int main(int argc, char* argv[]) {
int x;
x = 0;
int offset1 = atoi(argv[1]);
int offset2 = atoi(argv[2]);
function(1,2,3, offset1, offset2);
x = 1; // Goal is to skip this statement using buffer overflow
printf("X : %d\n",x);
return 0;
}
-----------------
// Execute the buffer overflow program with varying offsets
#!/bin/bash
for ((i=1; i<=60; i++))
do
for ((j=1; j<=60; j++))
do
echo "`./test $i $j`"
done
done
-- Assembler output
(gdb) disassemble main
Dump of assembler code for function main:
0x080483c2 <main+0>: lea 0x4(%esp),%ecx
0x080483c6 <main+4>: and $0xfffffff0,%esp
0x080483c9 <main+7>: pushl -0x4(%ecx)
0x080483cc <main+10>: push %ebp
0x080483cd <main+11>: mov %esp,%ebp
0x080483cf <main+13>: push %ecx
0x080483d0 <main+14>: sub $0x24,%esp
0x080483d3 <main+17>: movl $0x0,-0x8(%ebp)
0x080483da <main+24>: movl $0x3,0x8(%esp)
0x080483e2 <main+32>: movl $0x2,0x4(%esp)
0x080483ea <main+40>: movl $0x1,(%esp)
0x080483f1 <main+47>: call 0x80483a4 <function>
0x080483f6 <main+52>: movl $0x1,-0x8(%ebp)
0x080483fd <main+59>: mov -0x8(%ebp),%eax
0x08048400 <main+62>: mov %eax,0x4(%esp)
0x08048404 <main+66>: movl $0x80484e0,(%esp)
0x0804840b <main+73>: call 0x80482d8 <printf@plt>
0x08048410 <main+78>: mov $0x0,%eax
0x08048415 <main+83>: add $0x24,%esp
0x08048418 <main+86>: pop %ecx
0x08048419 <main+87>: pop %ebp
0x0804841a <main+88>: lea -0x4(%ecx),%esp
0x0804841d <main+91>: ret
End of assembler dump.
(gdb) disassemble function
Dump of assembler code for function function:
0x080483a4 <function+0>: push %ebp
0x080483a5 <function+1>: mov %esp,%ebp
0x080483a7 <function+3>: sub $0x20,%esp
0x080483aa <function+6>: lea -0x9(%ebp),%eax
0x080483ad <function+9>: add $0x30,%eax
0x080483b0 <function+12>: mov %eax,-0x4(%ebp)
0x080483b3 <function+15>: mov -0x4(%ebp),%eax
0x080483b6 <function+18>: mov (%eax),%eax
0x080483b8 <function+20>: lea 0x7(%eax),%edx
0x080483bb <function+23>: mov -0x4(%ebp),%eax
0x080483be <function+26>: mov %edx,(%eax)
0x080483c0 <function+28>: leave
0x080483c1 <function+29>: ret
End of assembler dump.
1 ответ
Разборка для function
Вы предоставили, кажется, использовать жестко закодированные значения offset1
а также offset2
вопреки вашему C-коду.
Адрес для ret
должен быть рассчитан с использованием смещения байтов / символов: ret = (int *)(buffer1 + offset1)
иначе вы получите удар по математике указателя (особенно в этом случае, когда ваш buffer1
не в хорошем выровненном смещении от обратного адреса).
offset1
должно быть равно 0x9 + 0x4
(смещение используется в lea
+ 4 байта для push %ebp
). Однако это может непредсказуемо измениться при каждой компиляции - компоновка стека может отличаться, компилятор может создать дополнительное выравнивание стека и т. Д.
offset2
должно быть равно 7
(длина инструкции, которую вы пытаетесь пропустить).
Обратите внимание, что вам здесь повезло - function
использует cdecl
соглашение о вызовах, которое означает, что вызывающая сторона отвечает за удаление аргументов из стека после возврата из функции, которая обычно выглядит следующим образом:
push arg3
push arg2
push arg1
call func
add esp, 0Ch ; remove as many bytes as were used by the pushed arguments
Ваш компилятор решил объединить это исправление с последующим printf
, но он также может решить сделать это после вызова вашей функции. В этом случае add esp, <number>
Инструкция будет присутствовать между вашим обратным адресом и инструкцией, которую вы хотите пропустить - вы можете себе представить, что это не закончится хорошо.