花式栈溢出之Stack smash
/Stack smash
在程序加了 canary 保护之后,如果我们读取的 buffer 覆盖了对应的值时,程序就会报错,而一般来说我们并不会关心报错信息。而 stack smash 技巧则就是利用打印这一信息的程序来得到我们想要的内容。这是因为在程序启动 canary 保护之后,如果发现 canary 被修改的话,程序就会执行
__stack_chk_fail
函数来打印 argv[0] 指针所指向的字符串,正常情况下,这个指针指向了程序名。其代码如下:
1 | void __attribute__ ((noreturn)) __stack_chk_fail (void) |
所以说如果我们利用栈溢出覆盖 argv[0] 为我们想要输出的字符串的地址,那么在
__fortify_fail
函数中就会输出我们想要的信息。
EKOPARTY PRE-CTF 2015: Smashing the stack for fun and profit
这里结合ekoparty-pre-ctf-2015的一道Pwn题来学习,题目下载地址。
在本地运行前,需要在本地先建立一个flag.txt。
运行程序,先给个有趣的地址,然后给你输入用户名;file查看是静态链接文件;checksec发现开启了NX和Canary:
IDA看下main()函数,程序会打开本地的flag.txt文件,然后复制该文件描述符到一个buf中,接着输出一个指向该buf的地址并询问你的用户名,通过read()函数获取0x400长度的用户输入的内容,而0x400数值过大,由此可知read()函数存在栈溢出漏洞:
这样看应该是直接给了获取flag的地址了。
回到之前安全编译选项检测的结果发现,程序是开了Canary的,根据Canary的原理,其在一个函数入口处从fs段内获取一个随机值,一般存到EBP - 0x4(32位)或RBP - 0x8(64位)的位置,如果攻击者利用栈溢出修改到了这个值,导致该值与存入的值不一致,__stack_chk_fail()函数将抛出异常并退出程序。
先试下发送大量字符,查看到抛出异常信息,即调用了Canary实现代码中的__libc_message()将__libc_argv[0]打印了出来,这里__libc_argv[0]为本程序名,同时发现程序只是终止了而非栈溢出时的段错误:
也就是说,只要溢出的内容覆盖了Canary插入的cookie,Canary就会报错,且当前输入200个字符还未达到覆盖到argv[0]的位置。整个栈结构及溢出原理如下图所示:
下面只需要确定溢出的变量地址距离argv[0]的偏移地址。
Method1——使用pattern
之前pattern_create生成的200个字符串并未使程序出现段错误,后面尝试到400个字符串时发生段错误,没有输出Canary出错返回的异常信息而是输出Segmentation fault,即输入字符溢出覆盖了argv[0]地址导致程序无法正常读取该值从而发生错误:
pattern_offset得到偏移量为376:
Method2——断点调试
先在main()函数打断点,运行:
可以看出0x7fffffffe2bd保存的是程序名,其自然就是 argv[0],所以我们修改的内容就是这个地址。同时0x7fffffffdf48处保留着该地址,所以我们真正需要覆盖的地址是0x7fffffffdf48。
此时RBP的地址为0x7fffffffde60;在前面IDA中可以看到变量v8相对于RBP的偏移量为90h,因此可算出v8变量的地址为RBP-90h=0x7fffffffddd0。
最后可以算出v8变量到需要覆盖的地址的偏移量为:|0x7fffffffddd0 - 0x7fffffffdf48|=178h=376(d)
编写payload:
1 | from pwn import * |
运行getflag: