zoukankan      html  css  js  c++  java
  • [工控安全][原创]某工控软件由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
    ", 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
    ", 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
    ", 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()函数的输入参数。

  • 相关阅读:
    hdu 1269 迷宫城堡 (并查集)
    hdu 1272 小希的迷宫 (深搜)
    hdu 1026 Ignatius and the Princess I (深搜)
    hdu 1099 Lottery
    hdu 1068 Girls and Boys (二分匹配)
    几个基础数位DP(hdu 2089,hdu 3555,uestc 1307 windy 数)
    hdu 1072 Nightmare (广搜)
    hdu 1398 Square Coins (母函数)
    hdu 1253 胜利大逃亡 (深搜)
    hdu 1115 Lifting the Stone (求重心)
  • 原文地址:https://www.cnblogs.com/bianmu-dadan/p/11016709.html
Copyright © 2011-2022 走看看