kir[A]'s 小黑屋

pwn-imdb

字数统计: 963阅读时长: 5 min
2018/01/30 Share

blog已经搭了一段时间了,拖延症发作,一直没写。最近没做什么有意思的题,就写一下某CTF群的入群题解题思路。

程序伪代码

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
void __fastcall main(__int64 a1, char **a2, char **a3)
{
char v3; // [rsp+0h] [rbp-18h]

setbuf(stdout, 0LL);
signal(14, handler);
puts("*** Welcome to IMDB ***");
puts(" -- search for movies and TVs here\n----------------------------------------");
while ( 1 )
{
menu();
get_input((__int64)&v3, 0xFuLL, 10);
switch ( v3 )
{
case '1':
add_tv();
break;
case '2':
add_movie();
break;
case '3':
remove();
break;
case '4':
show();
break;
case '5':
exit(0);
return;
default:
puts("Invalid choice");
break;
}
}
}

程序基本功能是添加tv或者movie,可以删除指定name的,以及显示所有记录。

add_tv

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
int add_tv()
{
__int64 v0; // rbx
float v1; // xmm1_4
int result; // eax
__int64 v3; // [rsp+0h] [rbp-88h]

v0 = operator new(0xD0uLL);
memset((void *)v0, 0, 0xD0uLL);
*(_QWORD *)v0 = printf_tv;
printf("TV name? ");
get_input((__int64)&v3, 0x3FuLL, 10);
snprintf((char *)(v0 + 8), 0x40uLL, "%s", &v3);
printf("Season? ");
get_input((__int64)&v3, 0xFuLL, 10);
*(_DWORD *)(v0 + 204) = strtol((const char *)&v3, 0LL, 10);
printf("Rating? ", 0LL);
get_input((__int64)&v3, 0xFuLL, 10);
v1 = strtod((const char *)&v3, 0LL);
*(float *)(v0 + 200) = v1;
printf("TV introduction? ", 0LL);
get_input((__int64)&v3, 0x7FuLL, 10);
snprintf((char *)(v0 + 72), 0x80uLL, "%s", &v3);
if ( (unsigned __int8)put_in_list(v0, 128LL) )
result = puts("New TV added.");
else
result = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v0 + 16LL))(v0);
return result;

add_movie

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
int add_movie()
{
__int64 v0; // rbx
float v1; // xmm1_4
int result; // eax
__int64 v3; // [rsp+0h] [rbp-88h]

v0 = operator new(216uLL);
memset((void *)v0, 0, 0xD8uLL);
*(_QWORD *)v0 = printf_movie;
printf("Movie name? ");
get_input((__int64)&v3, 0x3FuLL, 10);
snprintf((char *)(v0 + 8), 0x40uLL, "%s", &v3);
printf("Actors? ");
*(_QWORD *)(v0 + 208) = sub_400CC0(); //这里会额外申请一块内存放数据
printf("Rating? ");
get_input((__int64)&v3, 0xFuLL, 10);
v1 = strtod((const char *)&v3, 0LL);
*(float *)(v0 + 200) = v1;
printf("Movie introduction? ", 0LL);
get_input((__int64)&v3, 0x7FuLL, 10);
snprintf((char *)(v0 + 72), 0x80uLL, "%s", &v3);
if ( (unsigned __int8)put_in_list(v0, 128LL) )
result = puts("New movie added.");
else
result = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v0 + 16LL))(v0);
return result;
}

remove

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
int remove()
{
__int64 v0; // rbx
signed __int64 v1; // r12
const char *v2; // rbp
__int64 v4; // [rsp+0h] [rbp-58h]

v0 = 0LL;
v1 = -1LL;
printf("TV/Movie name to remove? ");
get_input((__int64)&v4, 0x3FuLL, 10);
do
{
v2 = (const char *)list_table[v0];
if ( v2 && !strcmp(v2 + 8, (const char *)&v4) )
{
v1 = (signed int)v0;
(*(void (__fastcall **)(const char *, __int64 *))(*(_QWORD *)v2 + 16LL))(v2, &v4);
}
++v0;
}
while ( v0 != 32 );
if ( (_DWORD)v1 == -1 )
return puts("Not found.");
list_table[v1] = 0LL; //这里只清0了最后一个
return puts("Removed successfully");
}

漏洞点是:remove那里可以批量删除name一样的,但是只清空了最后一个指针。

show

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int show()
{
int result; // eax
int (**v1)(const char *, ...); // rbx

result = 1;
v1 = (int (**)(const char *, ...))list_table;
do
{
if ( *v1 )
{
(**(void (***)(void))*v1)();
result = 0;
}
++v1;
}
while ( v1 != &printf );
if ( (_BYTE)result )
result = puts("No Movie/TV exists!");
return result;
}

利用步骤

先创建3个名字一样的tv,然后都删掉,再创建一个movie,其中movie的actor会在堆中申请一片内存写入,我们可以通过actor,伪造一个movie,可以控制actor的指针,然后show的时候就可以任意地址读了。下面是movie打印信息的函数:

1
2
3
4
5
6
7
8
9
int __fastcall sub_4011B0(__int64 a1)
{
return printf(
"Movie <%s>: %s rating: %.2f actors: %s\n",
a1 + 8,
a1 + 72,
*(_QWORD *)(a1 + 208), //这里可以用来任意地址读
*(float *)(a1 + 200));
}

通过这个方法可以泄露出malloc地址,以及list_table地址。(可以计算magic gadget)

getshell

伪造movie的虚表vtable,使其指向我们可控的内存(泄露了heap地址,将magic gadget地址写入heap中就行了)

1
2
3
4
5
6
7
8
9
10
11
12
13
.rodata:00000000004015A0 ; `vtable for'Movie
.rodata:00000000004015A0 _ZTV5Movie dq 0 ; offset to this
.rodata:00000000004015A8 dq offset _ZTI5Movie ; `typeinfo for'Movie
.rodata:00000000004015B0 printf_movie dq offset sub_4011B0 ; DATA XREF: add_movie+24↑o //改成magic地址
.rodata:00000000004015B8 dq offset nullsub_2
.rodata:00000000004015C0 dq offset j___ZdlPv ; operator delete(void *)
.rodata:00000000004015C8 align 20h
.rodata:00000000004015E0 ; `vtable for'TV
.rodata:00000000004015E0 _ZTV2TV dq 0 ; offset to this
.rodata:00000000004015E8 dq offset _ZTI2TV ; `typeinfo for'TV
.rodata:00000000004015F0 printf_tv dq offset sub_4011E0 ; DATA XREF: add_tv+24↑o
.rodata:00000000004015F8 dq offset nullsub_1
.rodata:0000000000401600 dq offset j___ZdlPv_0 ; operator delete(void *)

show的时候,list_table的开头存放这vtable的地址,vtable的开头是打印函数,如果伪造vtable的指向magic,就可以直接getshell了。

本题没有提供libc,需要通过泄露地址的后3位来查找。
https://libc.blukat.me/

EXP

暂时不放

END

菜鸟乱写一通,看不懂不要喷,欢迎纠正。完成,撒花~~~

CATALOG
  1. 1. 程序伪代码
    1. 1.1. menu
    2. 1.2. add_tv
    3. 1.3. add_movie
    4. 1.4. remove
    5. 1.5. show
  2. 2. 利用步骤
  3. 3. getshell
  4. 4. EXP
  5. 5. END