当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-085797

漏洞标题:360压缩3.2.0.2030栈缓冲区溢出+空指针引用

相关厂商:奇虎360

漏洞作者: blast

提交时间:2014-12-03 23:32

修复时间:2015-03-03 23:34

公开时间:2015-03-03 23:34

漏洞类型:设计错误/逻辑缺陷

危害等级:高

自评Rank:14

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-12-03: 细节已通知厂商并且等待厂商处理中
2014-12-05: 厂商已经确认,细节仅向厂商公开
2014-12-08: 细节向第三方安全合作伙伴开放
2015-01-29: 细节向核心白帽子及相关领域专家公开
2015-02-08: 细节向普通白帽子公开
2015-02-18: 细节向实习白帽子公开
2015-03-03: 细节向公众公开

简要描述:

360压缩处理ZIP文件时数据连接不当导致栈缓冲区溢出PoC1 (BufferOverrun.zip,记得往下点两层)。另附一个空指针引用的PoC2 (NullPointerDef.zip,打开就崩了)。
(这么弄会掉粉的……- -)
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
001289e8 7c98d144 360zip+0x7127e
00128a50 00410041 ntdll!RtlDebugAllocateHeap+0x281
00128a60 00410041 360zip+0x10041
00128a64 0041005c 360zip+0x10041
00128a68 00410041 360zip+0x1005c
00128a6c 00410041 360zip+0x10041
00128a70 00410041 360zip+0x10041
00128a74 00410041 360zip+0x10041
00128a78 00410041 360zip+0x10041
00128a7c 00410041 360zip+0x10041
00128a80 00410041 360zip+0x10041
00128a84 00410041 360zip+0x10041
00128a88 00410041 360zip+0x10041
00128a8c 00410041 360zip+0x10041

详细说明:

虽然考虑了一层路径长度最大应该是MAX_PATH,但是完全没有考虑到上下两层文件夹的名字加起来那就不一定是这个数字了,所以在360zip+0x7124b 处两个字符串拷贝函数处扑街了,拷贝完以后发生栈缓冲区溢出。还好有Security Cookies罩着。
问题在:

0:000> g
Breakpoint 2 hit
eax=03190748 ebx=00000002 ecx=00120000 edx=03190562 esi=00000060 edi=00a64670
eip=0047108b esp=001289d4 ebp=03190560 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
360zip+0x7108b:
0047108b 2bc2 sub eax,edx


上述有问题的函数整理如下(当是伪代码吧):

inline BOOL IsWordValid(WORD wd)
{return !(wd < 0x30 || wd > 0x39);}
inline ULONG Min(ULONG a, ULONG b)
{return a>b?b:a;}
ULONG ListItemValueProc(WCHAR* strFileName, WCHAR* strFullPath)
{
if ( strFileName && strFullPath ) // strFileName 目录名字, strFullPath 上层目录名和当前层的文件名字组合在一起的内容
{
ULONG nLenFileName = wcslen(strFileName);
ULONG nLenFullPath = wcslen(strFullPath);

if(nLenFileName > 0)
{
ULONG nPos1 = 0;
ULONG nPos2 = 0;

ULONG nPosTemp = 0;

WCHAR wTemp = 0;

while(1)
{
if(nPos2 > nLenFileName) goto some mark //break;
if(!IsWordValid(pszStrFileName[pos]) ||
!IsWordValid(pszStrFullPath[pos2]))
break;

nPosTemp = nPos1;
pos2++;

if(nPos1 + 1 < nLenFullPath)
{
while(nPosTemp++ < nLenFullPath && IsWordValid(pszStrFileName[nPosTemp]));
}

//where's if
while(nPos2++ < nLenFullPath && IsWordValid(pszStrFullPath[nPos2]));

ULONG nCounter1 = 0;
ULONG nCounter2 = 0;
//WCHAR* szString1 = 0;
WCHAR szSomeBuffer[MAX_PATH] = {0};
swscanf(pszStrFileName[nPos1], L"%d%s", &nCounter1, szSomeBuffer); //szString1);
swscanf(pszStrFullPath[nPos1], L"%d%s", &nCounter2, szSomeBuffer); //szString1); //szString1应该就是buffer。
if ( nCounter1 > nCounter2 ) return 1;
if ( nCounter1 < nCounter2 ) return -1;

nPos1 = nPosTemp;

SOMELABELUNKNOWN:
if ( nPos >= nLenFullPath) goto endOfScanning;

} // end of while(1)

ULONG nPosA = nPos1 + 1;
ULONG nPosB = nPos2 + 1;
if ( pos + 1 < nLenFileName )
{
while(nPosA++ < nLenFullPath && IsWordValid(pszStrFileName[nPosA])); //找到最后一个有效字符
}
//同上,一样的操作
while(nPosB++ < nLenFullPath && IsWordValid(pszStrFullPath[nPosB]));


ULONG nDiff = nPosA - nPos1;
ULONG nDiff2= nPosB - nPos2;
ULONG nDiff3= nLenFileName - nPos1;
ULONG nDiff4= nLenFullPath - nPos2;

if(nDiff < nDiff2)
{
nDiff4 = Min(nDiff2, nDiff3);
}
else
{
if(nDiff4 > nDiff)
nDiff4 = nDiff;
}

WCHAR szSomeBufferA[MAX_PATH] = {0};
WCHAR szSomeBufferB[MAX_PATH] = {0};
wcsncpy(szSomeBufferA, pszStrFileName[nPos1], nDiff4); //!!!WARNING!!!
wcsncpy(szSomeBufferB, pszStrFullPath[nPos2], nDiff4); //!!!WARNING!!!

ULONG nCompareResult = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, szSomeBufferA, -1, szSomeBufferB, -1);
//-1: 字符串是Null Terminated

if ( nCompareResult )
{
if ( nCompareResult == 2 )
{
LABEL_42:
pos += nDiff4;
pos2 += nDiff4;
goto SOMELABELUNKNOWN;
}
if(nCompareResult == 1)
result = -1;
else
result = 1;
//2 * (nCompareResult != 1) - 1;
}
else
{
result = _wcsicmp(szSomeBufferA, szSomeBufferB);
}

if(result)
return result;
else
goto LABEL_42;

}
endOfScanning:
result = 0;
if ( nLenFileName != nLenFullPath )
{
if(nLenFileName > nLenFullPath)
result = 1;
else
result = -1;
}


}
else //if ( strFileName && strFullPath ) 的else部分
{
result = 0;
}
return result;
}

漏洞证明:

0:000> pct
eax=001289f0 ebx=00000000 ecx=00000000 edx=00128bf8 esi=00000128 edi=00000000
eip=0047127e esp=001289b4 ebp=0222029e iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
360zip+0x7127e:
0047127e ff1590a14e00 call dword ptr [360zip+0xea190 (004ea190)] ds:0023:004ea190={kernel32!CompareStringW (7c80a3ee)}
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
001289e8 7c98d144 360zip+0x7127e
00128a50 00410041 ntdll!RtlDebugAllocateHeap+0x281
00128a60 00410041 360zip+0x10041
00128a64 0041005c 360zip+0x10041
00128a68 00410041 360zip+0x1005c
00128a6c 00410041 360zip+0x10041
00128a70 00410041 360zip+0x10041
00128a74 00410041 360zip+0x10041
00128a78 00410041 360zip+0x10041
00128a7c 00410041 360zip+0x10041
00128a80 00410041 360zip+0x10041
00128a84 00410041 360zip+0x10041
00128a88 00410041 360zip+0x10041
00128a8c 00410041 360zip+0x10041
00128a90 00410041 360zip+0x10041
00128a94 00410041 360zip+0x10041


0:000> dd esp L200
001289b4 00000400 00000001 001289f0 ffffffff
001289c4 00128bf8 ffffffff 00a75a70 00000002
001289d4 00a64708 00128e34 0000018a 0222004c
001289e4 00000128 ffffffff 7c98d144 00580045
001289f4 00520054 004c0041 004e004f 00410047
00128a04 00410041 00410041 00410041 00410041
00128a14 00410041 00410041 00410041 00410041
00128a24 00410041 00410041 00410041 00410041
00128a34 00410041 00410041 00410041 00410041
00128a44 00410041 00410041 00410041 00410041
00128a54 00410041 00410041 00410041 00410041
00128a64 00410041 0041005c 00410041 00410041
00128a74 00410041 00410041 00410041 00410041
00128a84 00410041 00410041 00410041 00410041
00128a94 00410041 00410041 00410041 00410041
00128aa4 00410041 00410041 00410041 00410041
00128ab4 00410041 00410041 00410041 00410041
00128ac4 00410041 00410041 00410041 00410041
00128ad4 00410041 00410041 00410041 00410041
00128ae4 00410041 00410041 00410041 00410041
00128af4 00410041 00410041 00410041 00410041
00128b04 00410041 00410041 00410041 00410041
00128b14 00410041 00410041 00410041 00410041
00128b24 00410041 00410041 00410041 00410041
00128b34 00410041 00410041 00410041 00410041
00128b44 00410041 00410041 00410041 00410041
00128b54 00410041 00410041 00410041 00410041
00128b64 00410041 00410041 00410041 00410041
00128b74 00410041 00410041 00410041 00410041
00128b84 00410041 00410041 00410041 00410041


and。。poc2:

(59c.3a4): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=02267cf4 ecx=00000002 edx=028d0016 esi=00000005 edi=015a3ee8
eip=01517ca5 esp=02417ba4 ebp=015a3ee4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\360\360zip\360zipc.dll -
360zipc!DeleteExtractObject+0x1f4a5:
01517ca5 394858 cmp dword ptr [eax+58h],ecx ds:0023:00000058=????????

修复方案:

使用_s拷贝函数
或者好好校验要写入缓冲区的数据内容

版权声明:转载请注明来源 blast@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:10

确认时间:2014-12-05 13:29

厂商回复:

感谢白帽子报告此问题,这是360压缩在处理畸形格式文件时发生的溢出漏洞,我们将尽快修复此漏洞。

最新状态:

暂无


漏洞评价:

评论

  1. 2014-12-03 23:36 | blast ( 普通白帽子 | Rank:348 漏洞数:57 | 五仁委员会)

    orz 贴的poc连接没了= = 刚编辑了 麻烦审核下= =

  2. 2014-12-03 23:38 | blast ( 普通白帽子 | Rank:348 漏洞数:57 | 五仁委员会)

    = =..厂家看不到的话请私信我....

  3. 2014-12-03 23:47 | 路西法 ( 路人 | Rank:2 漏洞数:2 | 堕落天使路西法)

    @blast 请私信我POC O(∩_∩)O哈哈~

  4. 2014-12-04 00:53 | 小龙 ( 普通白帽子 | Rank:1208 漏洞数:316 | 乌云有着这么一群人,在乌云学技术,去某数...)

    师傅@说好的给徒儿一份呢

  5. 2014-12-04 08:14 | 其实我是路人甲 ( 路人 | 还没有发布任何漏洞 | 生活要多一点快乐!)

    师傅@说好的给徒儿一份呢

  6. 2014-12-04 08:59 | blast ( 普通白帽子 | Rank:348 漏洞数:57 | 五仁委员会)

    师傅@说好的给徒儿一份呢

  7. 2014-12-09 20:56 | laoyao ( 路人 | Rank:14 漏洞数:3 | ด้้้้้็็็็็้้้้้็็็็...)

    师傅@说好的给徒儿一份呢

  8. 2014-12-10 18:17 | 轨迹 ( 路人 | Rank:5 漏洞数:3 | 321)

    师傅@说好的给徒儿一份呢