Sec

网络安全研究员,专注于工业互联网安全领域。求职中。

导航

[工控安全][原创]某工控软件由wcslen()函数引起的内存访问越界漏洞(一)

mailto: wangkai0351@gmail.com
【未经同意禁止转载】

某工控软件由wcslen()函数引起的内存访问越界漏洞(一)

wcslen()函数原型

参考微软网站https://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strlen-wcslen-mbslen-mbslen-l-mbstrlen-mbstrlen-l?view=vs-2019

wcslen()函数C runtime library中用于计算款字符串长度的函数,定义在<string.h> 或 <wchar.h>。

其在VS2019的标准库中的原型如下

size_t wcslen(
   const wchar_t *str
);

参数

输入参数:
str:以为 null 结尾的字符串。
输出参数:
返回str中的字符数,不包括为 null 终端。

微软已经提示:

安全说明这些函数会引发由缓冲区溢出问题带来的潜在威胁。 缓冲区溢出问题是常见的系统攻击方法,使权限的提升不能确保。 有关详细信息,请参阅 避免缓冲区溢出

某软件dll中的wcslen()函数

某软件的dll中包含的wcslen()函数经过IDA Pro v7.0(Version 7.0.170914 macOS x86_64 (32-bit address size))反编译后得到的伪代码如下

size_t __cdecl wcslen(const wchar_t *a1)
{
  const wchar_t *v1; // eax
  wchar_t v2; // cx

  v1 = a1;
  do
  {
    v2 = *v1;
    ++v1;
  }
  while ( v2 );
  return v1 - a1 - 1;
}

wcslen()函数缓冲区访问越界

参考以上微软网站,我们编写一个wcslen()函数正常调用示例程序

#include <stdlib.h>
int main()
{
    wchar_t* wstr1 = L"Count.";
  
    wprintf(L"Length of '%s' : %d\n", wstr1, wcslen(wstr1) );
}

根据https://book.2cto.com/201312/38514.html网站所说,wcslen()函数产生缓冲区溢出是由计算机底层对字符串编码的弱点带来的。

1)如果字符数组不是正确地以空字符结尾的,strlen()函数可能会返回一个错误的超大的数值,使用它时,就可能会导致漏洞。

2)如果传入一个非以空字符结尾的字符串,strlen()函数可以越过动态分配的数组的边界读取,并导致程序停止运行。

第二点最有可能带来信息安全隐患,触发漏洞的条件是构造一块以非空字符结尾的连续字符串空间

我们把反汇编的wcslen()代入到示例程序中,并构造shellcode,如下

#include <stdlib.h>
size_t wcslen_(const wchar_t *a1)
{
    const wchar_t *v1; // eax
    wchar_t v2; // cx

    v1 = a1;
    do
    {
        v2 = *v1;
        printf("v2 = %d\n", v2);
        ++v1;
    }
    while ( v2 );
    return v1 - a1 - 1;
}


int main()
{
    char* str1 = "Count.";
    wchar_t* wstr1 = L"Count.";

    wchar_t wstr2[6];
    wstr2[0] = L'C';
    wstr2[1] = L'o';
    wstr2[2] = L'u';
    wstr2[3] = L'n';
    wstr2[4] = L't';
    wstr2[5] = L'.';

    wchar_t *wstr3_p = (&wstr2[5]+1);
    *wstr3_p = L'.';

    wprintf(L"Length of '%s' : %d\n", wstr1, wcslen_(wstr2) );
}

运行结果如下(Macos+Apple LLVM version 10.0.1 (clang-1001.0.46.4))

v2 = 67
v2 = 111
v2 = 117
v2 = 110
v2 = 116
v2 = 46
v2 = 46
v2 = 642464122
v2 = -504575440
v2 = 32766
v2 = 1788097493
v2 = 32767
v2 = 0
Length of 'C' : 12
[1]    84188 abort      ./a.out

可以看出已经越界读取了。

如果我们进一步构造再长一点的字符串空间

    wchar_t *wstr3_p = (&wstr2[5]+1);
    wstr3_p[0] = L'.';
    wstr3_p[1] = L'.';
    wstr3_p[2] = L'.';
    wstr3_p[300] = L'.';

运行结果如下

[1]    84740 segmentation fault  ./a.out

已经发生了段错误。

某软件dll中调用wcslen()的函数

某工控软件的dll中,_cxxxx()函数调用了wcslen(),经过IDA Pro v7.0反编译得到

int __cdecl _cxxxx(wchar_t *a1)
{
  int result; // eax
  size_t v2; // eax
  __int16 v3; // ax
  signed int v4; // [esp+10h] [ebp-20h]
  size_t v5; // [esp+14h] [ebp-1Ch]

  v4 = 0;
  if ( a1 != 0 )
  {
    v5 = wcslen(a1); //调用wsclen()函数处
    result = v4;
  }
  else
  {
    /*
    错误处理
    */
    result = -1;
  }
  return result;
}

下面就是寻找哪个函数或者动作调用了这个_cxxxx()函数,关键是我们如何自行构造_cxxxx()函数的输入参数。

posted on 2019-06-13 15:21  大单GreatDane  阅读(616)  评论(0编辑  收藏  举报