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()函数的输入参数。

  • 相关阅读:
    Netty(一、初步了解)
    nginx(三、keepalived高可用)
    nginx(二、配置文件)
    nginx(一、安装与启动)
    ElasticSeach(六、springboot集成ES high level client)
    ElasticSeach(五、命令操作)
    ElasticSeach(四、mapping)
    ElasticSeach(三、IK分词器配置)
    ElasticSeach(二、部署运行)
    ElasticSeach(一、基本概念)
  • 原文地址:https://www.cnblogs.com/bianmu-dadan/p/11016709.html
Copyright © 2011-2022 走看看