Пытаюсь воспроизвести Buffer Overflow. Для этого использую этот туториал:
phrack.org/issues/49/14.html
Но никак не могу даже сделать простейший пример (в том гайде он идет под названием example3.c). Для начала хочу сказать, что всё делаю под виртуальной машиной (убунту 13.04) с такими параметрами:
$ uname -a
Linux ubuntu-VirtualBox 3.8.0-19-generic #29-Ubuntu SMP Wed Apr 17 18:19:42 UTC 2013 i686 i686 i686 GNU/Linux
Также, перед тем как что-либо делать выключил ASLR:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Компилирую, выключив защиту стека:
gcc -fno-stack-protector -z execstack -o example3 example3.c
Вот собственно код этого примера (example3.c):
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
Из main вызывается функция, в которой перезаписывается return адресс, т.е. в конечном итоге мы должны перепрыгнуть инструкцию
x=1;
и на экран должно вывестить "0". Логика примера проста: перед buffer1 (выше по стеку) находится return адрес. Так как buffer1[5] занимает 8 байт (2 слова по 4 байта) и так как SFP занимает еще 4 байта (находится перед buffer1), мы находим этот return адрес, прибавив 12 к адресу buffer1:
ret = buffer1 + 12;
Далее к значению получившегося адреса прибавляем 8 (инструкция x=1), чтобы эту инструкцию не выполнять.
Всё бы хорошо, но на экран всё-таки выводится "1" (без Segmentation Fault). Тогда я решил посмотреть asm код, который генерирует компилятор:
gcc -S -o example3.s example3.c
Получилось следущее:
example3.s.file "example3.c"
.text
.globl function
.type function, @function
function:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $40, %esp
movl %gs:20, %eax
movl %eax, -12(%ebp)
xorl %eax, %eax
leal -22(%ebp), %eax
addl $18, %eax
movl %eax, -28(%ebp)
movl -28(%ebp), %eax
movl (%eax), %eax
leal 8(%eax), %edx
movl -28(%ebp), %eax
movl %edx, (%eax)
movl -12(%ebp), %eax
xorl %gs:20, %eax
je .L2
call __stack_chk_fail
.L2:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size function, .-function
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $0, 28(%esp)
movl $3, 8(%esp)
movl $2, 4(%esp)
movl $1, (%esp)
call function
movl $1, 28(%esp)
movl 28(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
.section .note.GNU-stack,"",@progbits
В самой функции function, резервируется 40 байт вместо 20 как в примере:
pushl %ebp
movl %esp, %ebp
subl $40, %esp
Как в примере получилось 20 мне понятно: (buffer1[5] = 8) + (buffer2[10] = 12) = 20. Но как у меня получилось 40 не совсем ясно. Тогда я попробовал в функции оставить просто buffer1[5] и увидел что резервируется 24 байта (откуда это число мне тоже не понятно). Ну да ладно, 24 + 4 = 28, т.е.
ret = buffer1 + 28;
Но увы, это тоже не работает. В связи с этим у меня 2 вопроса: почему мои значения отличаются от тех что из примера (или "почему не работает?!") и как сделать так, чтобы можно было воспроизвести хотябы простейшую buffer overflow атаку ?