-
-
[原创]2019看雪CTF 晋级赛Q1 第9题
-
发表于: 2019-3-23 00:26 3167
-
一、使用checksec 看下程序状况: 可知got表可写、elf文件地址固定。
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) FORTIFY: Enabled
二、main函数
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) FORTIFY: Enabled
二、main函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { __int64 v3; // rdx unsigned __int64 readCmd; // rax setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stderr, 0LL, 2, 0LL); __printf_chk(1LL, "Please input your name: ", v3); readData(g_inputName, 16LL); while ( 1 ) { printMenu(); readCmd = readInt(); LABEL_3: switch ( readCmd ) { case 0uLL: continue; case 1uLL: // malloc mallocByFun((__int64 (__fastcall *)(unsigned __int64))mallocAndClearBuf); continue; case 2uLL: // free sub_401010((__int64 (__fastcall *)(unsigned __int64))freeByIndex); continue; case 3uLL: // new mallocByFun((__int64 (__fastcall *)(unsigned __int64))newClassFun); continue; case 4uLL: // delete sub_401010((__int64 (__fastcall *)(unsigned __int64))freeNewClass); printMenu(); readCmd = readInt(); if ( readCmd > 5 ) return 0LL; goto LABEL_3; case 5uLL: // puts sub_401010((__int64 (__fastcall *)(unsigned __int64))PrintfByIndex); break; default: return 0LL; } } }
一、malloc函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { __int64 v3; // rdx unsigned __int64 readCmd; // rax setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stderr, 0LL, 2, 0LL); __printf_chk(1LL, "Please input your name: ", v3); readData(g_inputName, 16LL); while ( 1 ) { printMenu(); readCmd = readInt(); LABEL_3: switch ( readCmd ) { case 0uLL: continue; case 1uLL: // malloc mallocByFun((__int64 (__fastcall *)(unsigned __int64))mallocAndClearBuf); continue; case 2uLL: // free sub_401010((__int64 (__fastcall *)(unsigned __int64))freeByIndex); continue; case 3uLL: // new mallocByFun((__int64 (__fastcall *)(unsigned __int64))newClassFun); continue; case 4uLL: // delete sub_401010((__int64 (__fastcall *)(unsigned __int64))freeNewClass); printMenu(); readCmd = readInt(); if ( readCmd > 5 ) return 0LL; goto LABEL_3; case 5uLL: // puts sub_401010((__int64 (__fastcall *)(unsigned __int64))PrintfByIndex); break; default: return 0LL; } } }
一、malloc函数
char __fastcall mallocByFun(__int64 (__fastcall *mallocFun)(unsigned __int64)) { unsigned __int64 num; // rax __int64 findIndex; // rbx unsigned __int64 index; // rbp char result; // al signed __int64 v5; // r12 __int64 v6; // rbp signed __int64 v7; // r12 puts("Please input length of the string"); num = readInt(); // len <=0x400 if ( num > 0x400 ) LABEL_10: exit(-1); findIndex = 0LL; index = (unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3; while ( g_array_mallocBuf[findIndex] ) { if ( ++findIndex == 0x20 ) // g_array_mallocBuf[0x19]不为空,则findIndex = 0x20, // 此时会越界g_array_mallocBuf[findIndex] goto LABEL_10; } g_array_mallocBuf[findIndex] = (void *)mallocFun((unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3); g_array_mallocBufSize[findIndex] = index; result = puts("Please input the string"); if ( index ) { v5 = 3 * index; v6 = 0LL; v7 = 8 * v5; do { result = readData((char *)g_array_mallocBuf[findIndex] + v6 + 8, 16LL); if ( !result ) break; v6 += 24LL; } while ( v6 != v7 ); } return result; } char *__fastcall mallocAndClearBuf(__int64 num) { char *mallocBuf; // rax char *v2; // rdx __int64 v3; // rcx mallocBuf = (char *)malloc(24 * num); if ( num ) { v2 = mallocBuf + 8; v3 = 0LL; do { ++v3; *(_QWORD *)v2 = 0LL; *((_QWORD *)v2 + 1) = 0LL; v2 += 24; } while ( num != v3 ); } return mallocBuf; }
1、维护一个存储malloc指针数组和存储每个malloc buf的size数组。数组个数为0x20。没有发现存在越界的问题。
char __fastcall mallocByFun(__int64 (__fastcall *mallocFun)(unsigned __int64)) { unsigned __int64 num; // rax __int64 findIndex; // rbx unsigned __int64 index; // rbp char result; // al signed __int64 v5; // r12 __int64 v6; // rbp signed __int64 v7; // r12 puts("Please input length of the string"); num = readInt(); // len <=0x400 if ( num > 0x400 ) LABEL_10: exit(-1); findIndex = 0LL; index = (unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3; while ( g_array_mallocBuf[findIndex] ) { if ( ++findIndex == 0x20 ) // g_array_mallocBuf[0x19]不为空,则findIndex = 0x20, // 此时会越界g_array_mallocBuf[findIndex] goto LABEL_10; } g_array_mallocBuf[findIndex] = (void *)mallocFun((unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3); g_array_mallocBufSize[findIndex] = index; result = puts("Please input the string"); if ( index ) { v5 = 3 * index; v6 = 0LL; v7 = 8 * v5; do { result = readData((char *)g_array_mallocBuf[findIndex] + v6 + 8, 16LL); if ( !result ) break; v6 += 24LL; } while ( v6 != v7 ); } return result; } char *__fastcall mallocAndClearBuf(__int64 num) { char *mallocBuf; // rax char *v2; // rdx __int64 v3; // rcx mallocBuf = (char *)malloc(24 * num); if ( num ) { v2 = mallocBuf + 8; v3 = 0LL; do { ++v3; *(_QWORD *)v2 = 0LL; *((_QWORD *)v2 + 1) = 0LL; v2 += 24; } while ( num != v3 ); } return mallocBuf; }
1、维护一个存储malloc指针数组和存储每个malloc buf的size数组。数组个数为0x20。没有发现存在越界的问题。
2、相应的malloc函数也没有发现存在缓冲区溢出的问题。
3、程序根据用于输入数据通过计算后得到一个num 申请的buf大小为24,将后16个字节清零,然后从终端读取数据存入这16个字节中,只存储15个字符,最后一个字符清0。
4、malloc相关函数没发现漏洞。
二、free函数
int __fastcall sub_401010(__int64 (__fastcall *a1)(unsigned __int64)) { unsigned __int64 delteNum; // rax int result; // eax puts("Please input index of the string"); delteNum = readInt(); if ( delteNum > 0x1F ) exit(-1); if ( g_array_mallocBuf[delteNum] ) result = a1(delteNum); else result = puts("The string does not exit"); return result; } void __fastcall freeByIndex(__int64 index) { free(g_array_mallocBuf[index]); g_array_mallocBuf[index] = 0LL; }
free相关函数没有发现啥漏洞。
int __fastcall sub_401010(__int64 (__fastcall *a1)(unsigned __int64)) { unsigned __int64 delteNum; // rax int result; // eax puts("Please input index of the string"); delteNum = readInt(); if ( delteNum > 0x1F ) exit(-1); if ( g_array_mallocBuf[delteNum] ) result = a1(delteNum); else result = puts("The string does not exit"); return result; } void __fastcall freeByIndex(__int64 index) { free(g_array_mallocBuf[index]); g_array_mallocBuf[index] = 0LL; }
free相关函数没有发现啥漏洞。
三、new函数
struct newBuf *__fastcall newClassFun(unsigned __int64 num) { __int64 num_1; // rbx bool v2; // cf bool v3; // zf unsigned __int64 mallocLen; // rdi struct NewClass *strClass; // rax signed __int64 realNum; // rcx char *v7; // rdx struct newBuf *result; // rax num_1 = num; v2 = num < 0x550000000000000LL; v3 = num == 0x550000000000000LL; mallocLen = -1LL; if ( v2 || v3 ) mallocLen = 24 * num_1 + 8; strClass = (struct NewClass *)operator new[](mallocLen); realNum = num_1 - 1; strClass->num = num_1; v7 = strClass->newBuf.buf; result = &strClass->newBuf; if ( num_1 ) { do { --realNum; *((_QWORD *)v7 - 1) = &g_vtable; *(_QWORD *)v7 = 0LL; *((_QWORD *)v7 + 1) = 0LL; v7 += 24; } while ( realNum != -1 ); } return result; }
1、申请24*num + 8大小的buf
struct newBuf *__fastcall newClassFun(unsigned __int64 num) { __int64 num_1; // rbx bool v2; // cf bool v3; // zf unsigned __int64 mallocLen; // rdi struct NewClass *strClass; // rax signed __int64 realNum; // rcx char *v7; // rdx struct newBuf *result; // rax num_1 = num; v2 = num < 0x550000000000000LL; v3 = num == 0x550000000000000LL; mallocLen = -1LL; if ( v2 || v3 ) mallocLen = 24 * num_1 + 8; strClass = (struct NewClass *)operator new[](mallocLen); realNum = num_1 - 1; strClass->num = num_1; v7 = strClass->newBuf.buf; result = &strClass->newBuf; if ( num_1 ) { do { --realNum; *((_QWORD *)v7 - 1) = &g_vtable; *(_QWORD *)v7 = 0LL; *((_QWORD *)v7 + 1) = 0LL; v7 += 24; } while ( realNum != -1 ); } return result; }
1、申请24*num + 8大小的buf
2、前8个字节放入一个g_vtable虚函数表(包含一个空函数,和一个j___ZdlPv释放函数)
3、后16个size,清0用于存放输入数据。
4、没发现啥漏洞。
四、delete函数
void __fastcall freeNewClass(__int64 index) { __int64 *buf; // rdx __int64 *v2; // rbx void (*v3)(); // rax buf = (__int64 *)g_array_mallocBuf[index]; if ( buf ) { v2 = &buf[3 * *(buf - 1)]; while ( v2 != buf ) { while ( 1 ) { v2 -= 3; v3 = *(void (**)())*v2; if ( v3 == nullsub_1 ) break; ((void (__fastcall *)(__int64 *))v3)(v2); buf = (__int64 *)g_array_mallocBuf[index]; if ( v2 == buf ) goto LABEL_6; } } LABEL_6: operator delete[](v2 - 1); } g_array_mallocBuf[index] = 0LL; }
1、函数会定位到最后一个buf位置,读取其vtable,并执行。
void __fastcall freeNewClass(__int64 index) { __int64 *buf; // rdx __int64 *v2; // rbx void (*v3)(); // rax buf = (__int64 *)g_array_mallocBuf[index]; if ( buf ) { v2 = &buf[3 * *(buf - 1)]; while ( v2 != buf ) { while ( 1 ) { v2 -= 3; v3 = *(void (**)())*v2; if ( v3 == nullsub_1 ) break; ((void (__fastcall *)(__int64 *))v3)(v2); buf = (__int64 *)g_array_mallocBuf[index]; if ( v2 == buf ) goto LABEL_6; } } LABEL_6: operator delete[](v2 - 1); } g_array_mallocBuf[index] = 0LL; }
1、函数会定位到最后一个buf位置,读取其vtable,并执行。
2、从最后一个检索到第一个连续执行。
3、漏洞:malloc和new的buf都存储在一个数组中,没有进行区分。delete一个通过malloc申请的buf,构造vtable表,就可以执行任意函数。
五、printf函数
__int64 __fastcall PrintfByIndex(__int64 index, __int64 a2, __int64 a3) { __int64 v3; // rbp unsigned __int64 v4; // rbx char *v5; // rdx v3 = 0LL; v4 = 0LL; if ( g_array_mallocBufSize[index] ) { do { v5 = (char *)g_array_mallocBuf[index] + v3; ++v4; v3 += 24LL; __printf_chk(1LL, &unk_4010E6, v5 + 8); } while ( g_array_mallocBufSize[index] > v4 ); } return __printf_chk(1LL, &unk_4010E4, a3); }
打印没发现啥漏洞,通过构造应该能够打印出想要的地址。
__int64 __fastcall PrintfByIndex(__int64 index, __int64 a2, __int64 a3) { __int64 v3; // rbp unsigned __int64 v4; // rbx char *v5; // rdx v3 = 0LL; v4 = 0LL; if ( g_array_mallocBufSize[index] ) { do { v5 = (char *)g_array_mallocBuf[index] + v3; ++v4; v3 += 24LL; __printf_chk(1LL, &unk_4010E6, v5 + 8); } while ( g_array_mallocBufSize[index] > v4 ); } return __printf_chk(1LL, &unk_4010E4, a3); }
打印没发现啥漏洞,通过构造应该能够打印出想要的地址。
六、漏洞分析
通过上面分析只发现一个明显的漏洞,当使用delete删除的时候没有区分是通过malloc创建的还是new创建的,因此可以使用delete释放通过malloc的buf时,会首先判断24个字节buf的前8字节是否存在vtable,如果存在,则执行其函数,因此可以执行一个任意函数。
但是这里存在一些限制:
1)无法设置参数,因此想要拿到 shell,最容易的办法是通过 one-gadget。
2)要求其存放的是一个指针,指针内容为要执行的函数。因此可以执行got.plt中的函数,但是发现无论是put函数还是其他函数都无法构造合理的参数。
3)如何获取libc的地址?
七、获取libc地址
发现程序存在一个不会执行的函数,其会打印出libc的函数puts地址
__int64 sub_400E10() { signed __int64 v1; // [rsp-8h] [rbp-8h] v1 = 684069LL; return __printf_chk(0LL, &v1, &puts); }
可以通过这个函数获得libc地址
__int64 sub_400E10() { signed __int64 v1; // [rsp-8h] [rbp-8h] v1 = 684069LL; return __printf_chk(0LL, &v1, &puts); }
可以通过这个函数获得libc地址
在程序开始输入用户名时,其会将输入的名字存放到一个全局变量中。
__printf_chk(1LL, "Please input your name: ", v3); readData(g_inputName, 16LL);
此buf为16个字节,因此我们可以将g_inputName 与 g_inputName+8 作为vtable的地址,然后把想要执行的函数地址写入到
g_inputName中,就可以构建执行任意函数的通道。
此buf只有16个字节,只能写入2个函数,并且这2个函数被直接执行,无法做到控制。我们可以把要执行的函数地址写入到g_inputName中,然后将main函数地址写入到
g_inputName + 8位置,这样就可以无限循环执行任意函数了。
九、漏洞利用实现
1、使用malloc malloc一块大小为24个字节的buf。随便输入任意数据。
2、再次使用malloc ,malloc一个大块内存。
3、输入payload到第2次malloc的buf中。
4、调用delete函数,删除第一次malloc的buf,将执行任意函数。
__printf_chk(1LL, "Please input your name: ", v3); readData(g_inputName, 16LL);
此buf为16个字节,因此我们可以将g_inputName 与 g_inputName+8 作为vtable的地址,然后把想要执行的函数地址写入到
g_inputName中,就可以构建执行任意函数的通道。
此buf只有16个字节,只能写入2个函数,并且这2个函数被直接执行,无法做到控制。我们可以把要执行的函数地址写入到g_inputName中,然后将main函数地址写入到
g_inputName + 8位置,这样就可以无限循环执行任意函数了。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-3-23 13:10
被ODPan编辑
,原因:
赞赏记录
参与人
雪币
留言
时间
一笑人间万事
为你点赞~
2022-7-27 01:46
心游尘世外
为你点赞~
2022-7-26 23:40
飘零丶
为你点赞~
2022-7-17 03:22
oooAooo
为你点赞~
2019-3-25 21:57
赞赏
他的文章
看原图
赞赏
雪币:
留言: