这是我的程序,具有易受攻击的char缓冲区,名称[400]。
void greeting(char *temp1,char *temp2)
{
char name[400];
strcpy(name,temp2);
printf("Hello %s %s\n", temp1, name);
}
int main(int argc,char *argv[])
{
greeting(argv[1],argv[2]);
return 0;
}
在禁用ASLR的Linux(64位)上编译如下:
gcc -m32 -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -o buffer buffer.c
(gdb) run Mr `perl -e 'print "A" x 400'`
Hello Mr AAAAAAA.... (truncated)
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info reg eip ebp
eip 0x41414141
ebp 0x41414141
我在这里假设添加了一个空字节,导致溢出,但我不明白的是EIP如何只有1字节溢出的0x41414141?
编辑:在使用gdb进行更多探测之后,没有添加空字节,并且只输入400个字节时根本没有溢出。那么我的EIP如何最终指向我的缓冲区内容而没有任何溢出?我假设缺少空字节会导致printf()的问题。
答案 0 :(得分:2)
C字符串被NUL
终止,因此最终会出现1字节溢出,其值为零(NUL
)。
单字节NUL
溢出将$ebp
的保存值修改为指向堆栈下方的值。这会导致将错误的值恢复为$esp
,并控制$eip
。
特别注意ebp
的价值。在调用之后,$ebp
的值仍然相同,但它指向的值(main
将从堆栈中恢复的值)已经调整,并指向我们控制的中间缓冲液中。
当greeting
返回main时,没有任何反应。但是,当main
使用leave
指令恢复堆栈帧时,堆栈指针$esp
将设置在我们受控缓冲区的中间。执行ret
指令后,我们可以控制$eip
。
请注意,我使用了由pwntools而非标准AAAAA
生成的循环模式,因为我们可以使用它来计算偏移量。例如'aaaa'=> 0,'aaab'=> 1,'aaba'=> 2。
EBP: 0xffffc6e8 --> 0xffffc6f8 --> 0x0
ESP: 0xffffc54c --> 0xffffc558 --> 0xffffc5c8 --> 0xf63d4e2e
EIP: 0x8048466 (<greeting+25>: call 0x8048320 <strcpy@plt>)
EBP: 0xffffc6e8 --> 0xffffc600 ("raabsaabtaabuaabvaabwaabxaabyaab"...)
ESP: 0xffffc54c --> 0xffffc558 ("aaaabaaacaaadaaaeaaafaaagaaahaaa"...)
EIP: 0x804846b (<greeting+30>: lea eax,[ebp-0x190])
leave
之前的main
EBP: 0xffffc600 ("raabsaabtaabuaabvaabwaabxaabyaab"...)
ESP: 0xffffc6f0 --> 0xffffc9bb ("Mister")
EIP: 0x80484b1 (<main+39>: leave)
leave
之后
EBP: 0x62616172 (b'raab')
ESP: 0xffffc604 ("saabtaabuaabvaabwaabxaabyaabzaac"...)
EIP: 0x80484b2 (<main+40>: ret)
ret
EBP: 0x62616172 (b'raab')
ESP: 0xffffc608 ("taabuaabvaabwaabxaabyaabzaacbaac"...)
EIP: 0x62616173 (b'saab')