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;
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; float v1; int result; __int64 v3;
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; float v1; int result; __int64 v3;
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; signed __int64 v1; const char *v2; __int64 v4;
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; 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; int (**v1)(const char *, ...);
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
菜鸟乱写一通,看不懂不要喷,欢迎纠正。完成,撒花~~~