up777
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
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:
__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
__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
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
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
的围追堵截:
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;
}
![](upload/attach/201808/761984_5QV7JG7XYDHCA92.jpg)
且不考虑之前的漏洞编号,既然下面的补了,我们往上看。
瞅瞅
v15 = sub_4202EF((char *)lpLogfont, a2); 以及
sub_42048D(lpLogfont, a2, (LPARAM)&lf);
sub_4202EF
没啥有用的
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却没有重视这个啊
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版本更新点。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2019-4-13 13:03
被树梢之上编辑
,原因: