标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2025-2384]   作者: ecawen 发表于: [2019-05-17]

本文共 [748] 位读者顶过

1.bindiff安装java环境:需要安装java环境。我用的jre-8u171-windows-x64。bindiff下载:需要翻墙到官网下载。这里用的是bindiff420-win-x86,不断下一步就可以了。注意:安装路径要设置到自己的IDA目录
2.bindiff使用
详见安装目录的:file:///C:/Program%20Files%20(x86)/zynamics/BinDiff%204.2/doc/index.html[出自:jiwo.org]
bindiff是结合IDA一起使用的,首先使用IDA打开一个可执行文件(如打补丁之后的exe),保存配置idb,保持这个ida;然后再用ida打开要比较的文件(如打补丁之前的文件) ,保存配置idb,关掉这个ida 。
之后在打补丁之后的exe 的IDA窗口,使用快捷键crtl+6 , 弹出bindiff插件窗口,选择Diff Database选择要比较的idb文件 ,这里就是OldEnqEdit.idb
  bindiff就会比较两个可执行文件之间函数的差异。从上到下看,可以看到差异较大的函数是哪个。
在关闭IDA是可以选择保存bindiff生成的信息,之后再次打开直接选择loadresults   可以达到一样的效果。
3.wps 公式3?
各位已经看到了上面用来做演示的程序了,是wps的公式对象程序,分别是wpsoffice2010_10.1.0.6930和当前官网上最新的。关注厄运公式漏洞的朋友知道在office中这个模块有两个漏洞一个是11882,而另外一个是0802,而这个模块在office中已经被取消了(也就没有0802的补丁了)。
那么,wps office有没有类似的公式对象呢?如果有,有类似的漏洞吗?都被正确的修补了吗(尤其是第二个)?
要找到wps公式对象的位置很简单 ,打开wps,
弹出公式编辑器
在任务管理器中找到程序路径即可。
4.小试牛刀
这里比较这两个版本的公式对象程序
接着第二步的往后讲, 看最下面三个函数
同时也用IDA打开OldEqnEdit.exe
4.1 sub_4200E2 
new:
1
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int16 __cdecl sub_4200E2(char *a1, __int16 a2, int a3, __int16 a4)
{
  unsigned int v4; // ecx@1
  __int16 v6; // [sp+8h] [bp-8h]@1
  char *v7; // [sp+Ch] [bp-4h]@1
 
  v6 = sub_420362();
  v7 = (char *)&unk_459480 + 49 * (v6 - 1);
  v4 = strlen(a1) + 1;
  if ( v4 >= 0x21 )
    v4 = 32;
  qmemcpy(v7, a1, v4);
  ...
}

New中v7是局部变量,从传入a1参数拷贝到v7使用的是 qmemcpy(v7, a1, v4);v4肯定小于等于32,嘿嘿和office 公式对象字体名称最大长度一样啊(不明白的可以搜搜CVE-2017-11882相关的文章看看)
Old
1
2
3
4
5
6
7
8
9
10
__int16 __cdecl sub_4200E2(char *a1, __int16 a2, int a3, __int16 a4)
{
  __int16 v5; // [sp+8h] [bp-8h]@1
  char *v6; // [sp+Ch] [bp-4h]@1
 
  v5 = sub_420362();
  v6 = (char *)&unk_459480 + 49 * (v5 - 1);
  strcpy((char *)&unk_459480 + 49 * (v5 - 1), a1);
 ...
}

Old中是直接从 a1 拷贝字符串到 v6 ,可以造成栈溢出 。
从sub_4200E2来看,感觉就是厄运公式的补丁嘛,嘿嘿。 

4.2接着看第二个 sub_41189B 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
int __cdecl sub_41189B(char *a1, char *a2, int a3)
{
  unsigned int v3; // ecx@1
  unsigned int v4; // ecx@7
  unsigned int v5; // ecx@16
  int result; // eax@18
  unsigned int v7; // ecx@24
  unsigned int v8; // ecx@28
  char v9[36]; // [sp+8h] [bp-88h]@9
  char v10; // [sp+2Ch] [bp-64h]@6
  __int16 v11; // [sp+4Dh] [bp-43h]@9
  char *v12; // [sp+54h] [bp-3Ch]@11
  __int16 v13; // [sp+58h] [bp-38h]@1
  __int16 v14; // [sp+5Ch] [bp-34h]@1
  __int16 i; // [sp+60h] [bp-30h]@3
  __int16 v16; // [sp+64h] [bp-2Ch]@3
  char v17[36]; // [sp+68h] [bp-28h]@3
  __int16 v18; // [sp+8Ch] [bp-4h]@1
 
  v18 = -1;
  v13 = -1;
  v14 = strlen(a1);
  v3 = strlen(a1) + 1;
  if ( v3 >= 0x21 )
    v3 = 32;
  qmemcpy(v17, a1, v3);
  v17[v3] = 0;
  _strupr(v17);
  v16 = sub_41F7A6();
  for ( i = 0; i < v16; ++i )
  {
    if ( sub_41F7B6(i, &v10) )
    {
      v4 = strlen(&v10) + 1;
      if ( v4 >= 0x21 )
        v4 = 32;
      qmemcpy(v9, &v10, v4);
      v9[v4] = 0;
      if ( v11 == 1 )
        _strupr(v9);
      v12 = strstr(v9, a1);
      if ( v12 || (v12 = strstr(v9, v17)) != 0 )
      {
        if ( !a2 || !strstr(v9, a2) )
        {
          if ( (signed __int16)strlen(&v10) == v14 )
          {
            v5 = strlen(&v10) + 1;
            if ( v5 >= 0x21 )
              v5 = 32;
            qmemcpy((void *)a3, &v10, v5);
            *(_BYTE *)(a3 + v5) = 0;
            return 1;
          }
          if ( v12 == v9 )
            v13 = i;
          else
            v18 = i;
        }
      }
    }
  }
  if ( v13 < 0 )
  {
    if ( v18 < 0 )
    {
      result = 0;
    }
    else
    {
      sub_41F7B6(v18, &v10);
      v8 = strlen(&v10) + 1;
      if ( v8 >= 0x21 )
        v8 = 32;
      qmemcpy((void *)a3, &v10, v8);
      *(_BYTE *)(a3 + v8) = 0;
      result = 1;
    }
  }
  else
  {
    sub_41F7B6(v13, &v10);
    v7 = strlen(&v10) + 1;
    if ( v7 >= 0x21 )
      v7 = 32;
    qmemcpy((void *)a3, &v10, v7);
    *(_BYTE *)(a3 + v7) = 0;
    result = 1;
  }
  return result;
}


Old
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
int __cdecl sub_41189B(char *a1, char *a2, int a3)
{
  int result; // eax@12
  char v4; // [sp+8h] [bp-88h]@5
  char v5; // [sp+2Ch] [bp-64h]@4
  __int16 v6; // [sp+4Dh] [bp-43h]@5
  char *v7; // [sp+54h] [bp-3Ch]@7
  __int16 v8; // [sp+58h] [bp-38h]@1
  __int16 v9; // [sp+5Ch] [bp-34h]@1
  __int16 i; // [sp+60h] [bp-30h]@1
  __int16 v11; // [sp+64h] [bp-2Ch]@1
  char v12; // [sp+68h] [bp-28h]@1
  __int16 v13; // [sp+8Ch] [bp-4h]@1
 
  v13 = -1;
  v8 = -1;
  v9 = strlen(a1);
  strcpy(&v12, a1);
  _strupr(&v12);
  v11 = sub_41F7A6();
  for ( i = 0; i < v11; ++i )
  {
    if ( sub_41F7B6(i, &v5) )
    {
      strcpy(&v4, &v5);
      if ( v6 == 1 )
        _strupr(&v4);
      v7 = strstr(&v4, a1);
      if ( v7 || (v7 = strstr(&v4, &v12)) != 0 )
      {
        if ( !a2 || !strstr(&v4, a2) )
        {
          if ( (signed __int16)strlen(&v5) == v9 )
          {
            strcpy((char *)a3, &v5);
            return 1;
          }
          if ( v7 == &v4 )
            v8 = i;
          else
            v13 = i;
        }
      }
    }
  }
  if ( v8 < 0 )
  {
    if ( v13 < 0 )
    {
      result = 0;
    }
    else
    {
      sub_41F7B6(v13, &v5);
      strcpy((char *)a3, &v5);
      result = 1;
    }
  }
  else
  {
    sub_41F7B6(v8, &v5);
    strcpy((char *)a3, &v5);
    result = 1;
  }
  return result;
}

在new中很多
  if ( v3 >= 0x21 )
    v3 = 32;
  qmemcpy(v17, a1, v3); 这种代码片断,然后在Old中对应的都是 strcpy((char *)a3, &v5);
还是针对字体名字长度的修补。

4.3
有点不清晰,这个时候不断尝试,由 sub_4200E2往上翻,熟悉的场景看到了,就是围绕 lpLogfont 的围追堵截:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
int __cdecl sub_41FE81(LPCSTR lpLogfont, __int16 a2, int a3, int a4)
{
  char v5; // [sp+8h] [bp-D8h]@16
  __int16 v6; // [sp+29h] [bp-B7h]@16
  int v7; // [sp+2Bh] [bp-B5h]@16
  HGDIOBJ h; // [sp+30h] [bp-B0h]@2
  LOGFONTA lf; // [sp+34h] [bp-ACh]@2
  HGDIOBJ ho; // [sp+70h] [bp-70h]@2
  __int16 v11; // [sp+74h] [bp-6Ch]@2
  CHAR Name; // [sp+78h] [bp-68h]@2
  struct tagTEXTMETRICA tm; // [sp+9Ch] [bp-44h]@2
  __int16 v14; // [sp+D4h] [bp-Ch]@6
  __int16 v15; // [sp+D8h] [bp-8h]@1
  int v16; // [sp+DCh] [bp-4h]@1
 
  v16 = 0;
  *(_WORD *)a4 = 0;
  v15 = sub_4202EF((char *)lpLogfont, a2);
  if ( v15 )
  {
    *(_WORD *)a4 = v15;
    v16 = 1;
  }
  else
  {
    sub_41F6A4();
    sub_42048D(lpLogfont, a2, (LPARAM)&lf);
    lf.lfHeight = -(signed __int16)(24 * word_45915A / 72);
    ho = CreateFontIndirectA(&lf);
    h = (HGDIOBJ)sub_41F52E(hdc, (int)&ho);
    GetTextFaceA(hdc, 32, &Name);
    GetTextMetricsA(hdc, &tm);
    v11 = 0;
    if ( tm.tmWeight > 550 )
      v11 |= 1u;
    if ( tm.tmItalic )
      v11 |= 2u;
    v14 = 0;
    if ( a2 & 2 && !(v11 & 2) )
    {
      v11 |= 2u;
      v14 = 16;
    }
    if ( a2 & 1 && !(v11 & 1) )
    {
      v11 |= 1u;
      v14 = 32;
    }
    if ( _strcmpi(lpLogfont, &Name) && !sub_411841(lpLogfont) ) // CVE-11882,内部有 sub_41189, 打了补丁
    {
      if ( a3 )
      {
        strcpy(&v5, &Name);
        v7 = 0;
        v6 = 0;
        sub_41F834(&v5);
        if ( !sub_41FE81(&Name, v11, 0, a4) )
          *(_WORD *)a4 = sub_4200E2(&Name, v11, (int)&tm, v14); // sub_4200E2 打了补丁
        v16 = 1;
      }
    }
    else
    {
      *(_WORD *)a4 = sub_4200E2((char *)lpLogfont, a2, (int)&tm, v14); // sub_4200E2 打了补丁
      v16 = 1;
    }
    SelectObject(hdc, h);
    DeleteObject(ho);
  }
  return v16;
}


且不考虑之前的漏洞编号,既然下面的补了,我们往上看。
瞅瞅
v15 = sub_4202EF((char *)lpLogfont, a2);  以及
  sub_42048D(lpLogfont, a2, (LPARAM)&lf); 


sub_4202EF 没啥有用的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
signed __int16 __cdecl sub_4202EF(char *a1, __int16 a2)
{
  signed __int16 i; // [sp+0h] [bp-8h]@1
  char *v4; // [sp+4h] [bp-4h]@1
 
  v4 = (char *)&unk_459480;
  for ( i = 1; i <= 20; ++i )
  {
    if ( *(_WORD *)(v4 + 35) & 1 && *(_WORD *)(v4 + 33) == a2 && !_strcmpi(a1, v4) )
      return i;
    v4 += 49;
  }
  return 0;
}


sub_42048D ,啦啦啦,   strcpy((char *)(lParam + 28), lpLogfont); office直接取消了Equation,再也没有这方面的洞了,但是wps却没有重视这个啊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LPARAM __cdecl sub_42048D(LPCSTR lpLogfont, __int16 a2, LPARAM lParam)
{
  LPARAM result; // eax@1
 
  strcpy((char *)(lParam + 28), lpLogfont);
  *(_BYTE *)(lParam + 23) = 1;
  EnumFontsA(hdc, lpLogfont, sub_420557, lParam);
  *(_DWORD *)(lParam + 4) = 0;
  *(_DWORD *)(lParam + 8) = 0;
  *(_DWORD *)(lParam + 12) = 0;
  *(_DWORD *)(lParam + 16) = (a2 & 1) != 0 ? 700 : 400;
  *(_BYTE *)(lParam + 20) = (a2 & 2) != 0;
  *(_BYTE *)(lParam + 21) = 0;
  *(_BYTE *)(lParam + 22) = 0;
  *(_BYTE *)(lParam + 24) = 0;
  result = lParam;
  *(_BYTE *)(lParam + 25) = 0;
  *(_BYTE *)(lParam + 26) = 0;
  *(_BYTE *)(lParam + 27) = 0;
  return result;
}

由于种种原因这里不方便构造样本以及调试(请在win7 32位系统下od加载运行C:\Users\xx\AppData\Local\Kingsoft\WPS Office\11.1.0.7720\office6\mui\default\resource\ksee\EqnEdit.exe,并在上面提到的关键函数下好断点,拖入文档点击公式对象,就可以断下来。)有兴趣的小伙伴可以自行实验,关于样本的构造和调试可以参考 http://www.freebuf.com/vuls/160115.html 。补充一点,wps的公式对象程序和office的是不一样的,一个厂家出的,wps版本更新点。

评论

暂无
发表评论
 返回顶部 
热度(748)
 关注微信