一种被忽视的构造和整数溢出重现

原创 2004年08月18日 15:51:00

Michael Howard
Secure Windows Initiative

摘要:Michael Howard 研究了一种常常被忽略的代码构造,这种构造可能会导致严重的缓冲区溢出问题,然后介绍了一种在没有溢出副作用的情况下执行算术运算的替代方法。

*

谈谈构造

很奇怪,有如此之多的安全指导文档提示人们注意危险的函数。在 C 和 C++ 中,很少有危险的函数,不过,有一件事是肯定的,有许多危险的开发人员正在使用 C 和 C++。

因此,您可能会问,“Michael,您究竟要讨论什么?”

我得承认,我听腻了一些文档说的所谓某些函数是危险的,您应该使用更安全的类型来代替它们。例如,“不要使用 strcpy,它是危险的。您应该改用 strncpy,因为它是安全的。”没有什么比这更远离实际情况的了。有可能使用 strcpy 的代码是安全的,而调用 strncpy 的却是不安全的代码。

strcpy 这样的函数是有潜在 危险的,因为源数据比目标缓冲区大,并且它来自不受信任的源。如果源数据来自一个受信任的源,并且在复制之前经过了有效性测试,则调用 strcpy 就是安全的:

void func(char *p) {
   const int MAX = 10;
   char buf[MAX + 1];
   memset(buf,0,sizeof(buf));

   if (p && strlen(p) <= MAX) 
      strcpy(buf,p);
}

信不信由您,我正好要在某处用到这个例子。有一种常常被忽略的构造可能会导致缓冲区溢出,它不是函数调用。它是这样的:

while () 
   *d++ = *s++;

此处没有函数调用,这是 DCOM 中导致出现 Blaster worm 蠕虫病毒的编码构造。在 Buffer Overrun In RPC Interface Could Allow Code Execution 中,您可以读到更多关于此病毒的修复程序的内容。

该代码如下所示:

HRESULT GetMachineName(WCHAR *pwszPath) {
    WCHAR  wszMachineName[N + 1]) 
    LPWSTR pwszServerName = wszMachineName;
    while (*pwszPath != L'//' )
        *pwszServerName++ = *pwszPath++;   
    ...
}

这里的问题在于,while 循环是以源字符串中的一些字符为界的。它没有为目标缓冲区的大小所限制。换句话说,如果源数据不受信任,就会出现缓冲区溢出。

我编写了一段简单的 Perl 脚本来搜索 C 和 C++ 代码中这些类型的构造。请注意,这段脚本标记的每个实例并不是一个缺陷,您需要确定是否源数据是受信任的。

use strict;
use File::Find;

my $RECURSE = 1;

###################################################
foreach(@ARGV) {
  next if /^-./;
  if ($RECURSE) {
      finddepth(/&processFile,$_);
  } else {
      find(/&processFile,$_);
  }
}
###################################################
sub processFile {
  my $FILE;
  my $filename = $_;
  
  if (!$RECURSE && ($File::Find::topdir ne $File::Find::dir)) {
    $File::Find::prune = 1;
    return;
  } 
  
  # Only accept C/C++ and header extensions
  return if (!(//.[ch](?:pp|xx)?$/i));
     
  warn "$!/n" unless open FILE, "<" . $filename;
  
  # reset line number
  $. = 0;

  while () {
    chomp;
    s/^/s+//;
    s//s+$//;

  if (//*/w+/+/+/s{0,}=/s{0,}/*/w+/+/+) {
   print $filename . " " . $_ . "/n";
  }
}

这段脚本只查找 *p++ 构造,而不查找 *++p 构造。

假定您发现了一个缺陷,使代码更安全的一种方法是限制被复制的数据不大于目标缓冲区:

HRESULT GetMachineName(WCHAR *pwszPath) {
    WCHAR  wszMachineName[N + 1]) 
    LPWSTR pwszServerName = wszMachineName;

    size_t cbMachineName = N;
    while (*pwszPath != L'//' && --cbMachineName)
        *pwszServerName++ = *pwszPath++;   
    ...
}

最后,对不为目标缓冲区的大小所限制的任何内存复制函数或构造都应该进行严格检查。

返回页首返回页首

关于整数溢出的更多介绍

在前面的文章 Reviewing Code for Integer Manipulation Vulnerabilities 中,我讨论了与所谓整数溢出 的简单数学运算相关的安全性缺陷。

最近,作为正在进行的可信赖计算工程系列 (Trustworthy Computing Engineering Series) 的一部分,我给 Microsoft 的工程师做了一次关于整数溢出的讲座。在讲座中,我概述了如何发现整数溢出以及如何修复整数溢出。让我感到吃惊的是,我接收到的许多电子邮件都说我的补救方法很好,但是充满危险。请允许我做一些解释。

在该专栏中,我提到过的代码如下所示:

if (A + B > MAX) return -1;

应该改成这样:

if (A + B >= A && A + B < MAX) {
   // cool!
}

三年前就有人指出,一些人会看到这段代码,但是不知道它有什么用,从而可能删除 A+B >= A 部分,因为它看起来纯属多余,而现在,整数溢出又重新出现在您面前。不会吧!

作为回应,我写了下面的头文件,它的意图再明白不过了。是的,它看起来像乱码,但这段乱码却是 x86 汇编语言。我之所以使用汇编语言,是因为它可以使我直接访问 jc 操作数,即按进位转移 (jump-on-carry)。换句话说,它检测数学运算是否会导致溢出。

#ifndef _INC_INTOVERFLOW_
#define _INC_INTOVERFLOW_

#ifdef _X86_

inline bool UAdd(size_t a, size_t b, size_t *r) {

   __asm {
      mov         eax,dword ptr [a] 
      add         eax,dword ptr [b] 
      mov         ecx,dword ptr [r] 
      mov         dword ptr [ecx],eax 
      jc         short j1
      mov         al,1 
      jmp         short j2
j1:

#ifdef _DEBUG
      int         3
#endif
      xor         al,al
j2:
   };
}

inline bool UMul(size_t a, size_t b, size_t *r) {

   __asm {
      mov         eax,dword ptr [a] 
      mul         dword ptr [b] 
      mov         ecx,dword ptr [r] 
      mov         dword ptr [ecx],eax 
      jc         short j1
      mov         al,1 
      jmp         short j2
j1:

#ifdef _DEBUG
      int         3
#endif
      xor         al,al
j2:
   };
}

inline bool UMulAdd(size_t mul1, size_t mul2, size_t add, size_t *r) {

   size_t t = 0;
   if (UMul(mul1,mul2,&t))
      return UAdd(t,add,r);
   return false;
}

#else
#   error "This code compiles only on 32-bit x86
#endif // _X86_

#endif // _INC_INTOVERFLOW_

请查看这个文件,它包括解释这些函数的简单文档。

返回页首返回页首

发现安全漏洞

虽然花了一点时间,但是许多人都找到了漏洞。当通过比较字符串来做出安全决策时,这种比较就应该是区域性不变的比较或字节方式的比较。在这个例子中,我编写的代码可能允许访问土耳其语的敏感数据,因为在土耳其语中,字母“I”有四个实例,两个小写字母,两个大写字母。您可以在 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemstringclasscomparetopic5.asp 上阅读这方面的内容。

现在,让我们转到本月的错误。这段代码有什么问题?

void func(char *p) {
   char buf[10+1];
   memset(buf,0,sizeof(buf));
   
   // limit string to 10 chars
   sprintf(buf,"%10s",p);
   printf("Hello, %s/n",buf);
}

一个小智力游戏

这个智力游戏实际上与安全性毫无关系,但是当我想到人们为整数溢出检测代码所困扰时,我就把它从我记忆的深处拉了出来。这段代码有什么用呢?

int a = 0x42;
int b = 0x69;
a ^= b;
b ^= a;
a ^= b;

这个游戏的规则非常简单,您无法编译或解释这段代码。试着仅仅通过观察来确定它有什么用。

整数溢出漏洞

http://blog.csdn.net/habla/article/details/1834658 整数溢出 2007-10-20 18:39 1021人阅读 评论(...
  • wuxiaobingandbob
  • wuxiaobingandbob
  • 2015年03月25日 10:59
  • 1944

安全漏洞--整数溢出漏洞(IOV)分析

一 漏洞简介         整数溢出漏洞(integer overflow):在计算机中,整数分为无符号整数以及有符号整数两种。其中有符号整数会在最高位用0表示正数,用1表示负数,而无符号整数则...
  • bjtbjt
  • bjtbjt
  • 2017年04月19日 15:10
  • 1625

缓冲区溢出分析第11课:整数溢出的原理

《缓冲区溢出分析》这一系列的内容是我为“i春秋”(www.ichunqiu.com)所录制的同名视频课程的讲稿汇总。每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解。而本系列的内容也是从零开...
  • ioio_jy
  • ioio_jy
  • 2016年04月12日 11:34
  • 6306

整数溢出漏洞攻击

几天前,Bluebox Security刚曝出了Android存在安全漏洞。小分队立刻就掌握了其技术细节。最近几天经过对Android的研究,小分队又发现了一个类似的漏洞。攻击者可以对原apk进行修改...
  • pi9nc
  • pi9nc
  • 2013年09月06日 09:56
  • 2189

遇到不可重现问题怎么办

1、回忆操作步骤、尝试重现 尽量回忆当时的操作步骤,并且最大可能的复原当时的操作环境。确认当时的操作步骤是否有误。如果确认无误,可以多次尝试重现;即使发现有操作错误的情况,也不要认为没问题了,要...
  • u012938881
  • u012938881
  • 2015年05月22日 10:02
  • 763

如何检测整型相加溢出(overflow)

前言: 本文主要讨论如何判断整型相加溢出(overflow)的问题. 我们知道计算机里面整型一般是有限个字节(4 bytes for int)表示, 正是因为只能用有限个字节表示一个整型变量, ...
  • guang_jing
  • guang_jing
  • 2015年07月13日 17:25
  • 2561

Oracle 11g 导出数据报 “ORA-01455: 转换列溢出整数数据类型”的问题

Oracle 11g 导出数据报 “ORA-01455: 转换列溢出整数数据类型”的问题 最近发现云服务器上的Oracle 11g在导出时报错,如下: ... . 正在导出后期表...
  • victory_nj
  • victory_nj
  • 2017年05月09日 10:53
  • 744

整数溢出——无符号和有符号整数

在《C陷阱和缺陷》里有谈到整数溢出的问题。里面说到,无符号整数是不存在“溢出的,有符号整数才会”溢出”。一般理解,溢出就是运算结果超出了本身的表示范围。那么其实无符号整数也是会溢出的。之所以说没有所谓...
  • gongchangwangqi
  • gongchangwangqi
  • 2015年08月02日 20:20
  • 1198

整数溢出,如何判断整数溢出

c语言中存在两类整数算术运算,有符号运算和无符号运算。在无符号运算里,没有了符号位,所以是没有溢出的概念的。 所有的无符号运算都是以2的n次方为模。如果算术运算符的一个操作数是有符号书,另一个是无符...
  • shangyaowei
  • shangyaowei
  • 2013年12月19日 20:46
  • 286

优秀到不能被忽视读书笔记

被推荐《优秀到不能被忽视》这本书,正好自己这段时间相当迷茫。因为这样那样的一些原因苦恼希望能找到一个答案来帮我走出这个思维困局。买了这本书正好过年就当过年无聊时候的读物。 这本书的作者卡尔.纽波特是...
  • hpy1990
  • hpy1990
  • 2017年02月05日 11:28
  • 788
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一种被忽视的构造和整数溢出重现
举报原因:
原因补充:

(最多只允许输入30个字)