当前位置:编程学习 > 网站相关 >>

Basic Integer Overflows

Volume 0x0b Issue 0x3c Phile #0x0a of 0x10


|=--------------------=[ Basic Integer Overflows ]=----------------------=|
|=-----------------------------------------------------------------------=|
|=-------------------=[ by blexim <blexim@hush.com> ]=-------------------=|

中文翻译整理: Sam@sst <Sam@vertarmy.org>
airsupply@sst <airsupply@vertarmy.org>

1: 目录
1.1 什么是整数?
1.2 什么是整数溢出?
1.3 为什么那是危险的?

2: 整数溢出
2.1 Widthness 溢出
2.1.1 Exploiting
2.2 运算(Arithmetic)溢出
2.2.1 Exploiting

3: 符号类型的问题
3.1 它们看起来像什么?
3.1.1 Exploiting
3.2 符号类型的问题导致的整数溢出

4: 真实的例子
4.1 整数溢出类
4.2 符号问题类

译者语:

前言:
近期的出现的一些安全问题都是关于整数溢出问题比如前段时间的ssh crc32远程溢出漏洞
apache chunked 漏洞openssh Challenge-Response机制远程溢出漏洞都是由整数溢出引发
的缓冲区溢出问题整数溢出虽然是间接的导致一些缓冲区溢出问题但是也算是一种较新的技术
而且在未来的几年中关于此类的问题会越来越多.最新的phrack60期中刚好讲到了关于整数溢出
的一些技术心血来潮就翻译了这篇文章.
由于笔者的英文水平很烂文章可能会翻译的不是很到位.希望各位帮忙斧正.

感谢:
感谢我的女朋友帮我一起翻译了这篇文章.以及sst的所有成员. :>

关于sst:
一群很热血的人因为崇拜blackhat所以走到一起来.

Sam@sst


--[ 1.0 目录

在这篇文章中我将会讲述2种由于不安全编程引发的问题导致一些恶意的用户修改受影响的进程
改变程序执行流程.这2种类型的问题都是由于某一程序变量包含一个不可预料的值因此这种类型
的问题不同于那些程序内存被改写的问题比如:缓冲区溢出格式化溢出.所有文章中给出的程序例
子都是用C语言编写所以需要读者熟悉C语言.一些整数在内存中存储方法的知识也是会有所帮助
的但不是全部.

----[ 1.1 什么是整数?

一个整数 在计算范围内 是一个变量可能是一个没有小数部分的实数的.在系统上被编译处理后
整型和指针的尺寸一般是相同的(比如: 在32位的系统中例如i386 一个整数是32字节长在64位的
系统中例如SPARC一个整数是64字节长).然而一些编译器不使整型和指针为同样尺寸 所以为了
通俗易懂所有这里谈到的例子是在32位的系统环境和32位的整数长度和指针.

整数如同所有的变量只是内存的一个区域 当我们谈及关于整数我们通常用10进制来表示它们.
换句话说也就是人们经常使用的一种编码方式.计算机是基于数字的不能直接处理10进制所以在
计算机中整数是以2进制的方式存储的.2进制是另一种编码方式它们只有2个数字1和0与之不同
的10进制是用10个数字来表示的.2进制和10进制16进制是广泛的被使用的在电脑中能够很简单的
转换2进制和16进制.

因为存储负数通常是必要的这样就需要一种机制仅仅用位来代表负数这种方法已经完成了通过
一个变量的最高为来决定正负.如果最高位置1这个变量就被解释为负数; 如果置0这个变量就解释
为整数.这会导致一些混淆这可以说明一些符号类型问题的概念因为不是所有的变量都是有符号
之分的意思就是说并不是所有的类型都需要使用MSB来区分正负.这些变量被定义为无符号它只能
被赋予正数值.如果变量可正可负可以被称做是无正负的。

----[ 1.2 什么是整数溢出?

既然一个整数是一个固定的长度 (在本篇文章中使用32位)它能存储的最大值是固定的当
尝试去存储一个大于这个固定的最大值时将会导致一个整数溢出.在ISO C99的标准中讲
到整数溢出将会导致"不能确定的行为"也就是说编译器遵从了这个的规则那就是完全忽略
溢出而退出这个程序.很多编译器似乎忽略了这个溢出结果是一个意想不到的错误值被存储.

----[ 1.3 为什么那是危险的?

整数溢出是不能被立即察觉因此没有办法去用一个应用程序来判断先前计算的结果是否实
际上也是正确的.如果是用来计算缓冲区的大小或者计算数组索引排列的距离这会变的危险.
当然很多整数溢出并不是都是可利用的因为并没有直接改写内存但是有时他们可导致其他
类型的bugs缓冲区溢出等.而且整数溢出很难被发现因此就算是审核过的代码也会产生意外。

--[ 2.0 整数溢出

所以当一个整数溢出已经发生时会发生什么呢? ISO C99 是这样说的:

"A computation involving unsigned operands can never overflow
because a result that cannot be represented by the resulting unsigned
integer type is reduced modulo the number that is one greater than
the largest value that can be represented by the resulting type."
译者注:
大致的意思是:
涉及到无符号操作数计算的时候从不会溢出因为结果不能被无符号类型表示的时候
就会对比该类型能表示的最大值还大的数求余.这样就能用该结果来表示这种类型了.

NB:取模的运算方法是2个数相除取余数的值
例子:
10 modulo 5 = 0
11 modulo 5 = 1
所以在减轻体重法里面一个大数被和(最大的int值 + 1)取模在C语言中取模操作的符号是%.
</NB>
这里有一个字节是多余的可能是一个很好的象征性例子证明我们说的"导致不确定的行为".

我们有2个无符号的整数a和b 2个数都是32位字节长我们赋值给a 一个32为整数的最大值
b被赋值为1.然后我们让a和b相加然后存储结果到第3个无符号32位的整数r:

a = 0xffffffffff
b = 0x1
r = a + b

现在当相加起来的结果不能用32位的的值来表示结果为了和ISO 标准一致被和0x100000000
取模.

r = (0xffffffff + 0x1) % 0x100000000
r = (0x100000000) % 0x100000000 = 0

减轻体重法的取模算法只能计算低于32位的计算结果所以整数溢出导致结果被截断到一个范围
通常用一个变量来存储这个结果。这个经常被称作一个"环绕"(译者注:类似成语中"否极泰来"的
意思在这篇文章中我们理解为一个正数大到了极点就会变成负数负数小到了极点就会变成正数)
作为这里的结果就出现了环绕到0.

----[ 2.1 Widthness 溢出

所以整数溢出是尝试存储一个大数到一个变量中由于这个变量太小不足以存储该大数导致的结
果.用最简单的例子来说明这个问题存储一个大变量到一个小变量中去:

/* ex1.c - loss of precision */
#include <stdio.h>

int main(void){
int l;
short s;
char c;

l = 0xdeadbeef;
s = l;
c = l;

printf("l = 0x%x (%d bits) " l sizeof(l) * 8);
printf("s = 0x%x (%d bits) " s sizeof(s) * 8);
printf("c = 0x%x (%d bits) " c sizeof(c) * 8);

return 0;
} /* EOF */

让我们看看执行结果

nova:signed {48} ./ex1
l = 0xdeadbeef (32 bits)
s = 0xffffbeef (16 bits)
c = 0xffffffef (8 bits)

当我们把一个大的变量放入一个小变量的存储区域中结果是只保留小变量能够存储的位而其他的位
都被截短了.

有必要在这里提及整数进位.当一个计算包含大小不同的操作数时通过计算较小的操作数会被进位到
较大的操作数.如果结果将被存储在一个较小的变量里这个结果将会被重新减小直到较小的操作数
可以容纳.
这个例子里:

int i;
short s;

s = i;

这里计算结果将被赋给一个不同尺寸的操作数将发生的是变量s被提升为一个整型(32位)然后整数i的
内容被拷贝给新的提升后的s接着提升后的变量内容为了能存在s里面被降低回16位.如果超过了s能
存储的最大值降位将导致结果被截断..

------[ 2.1.1 Exploiting

整数溢出并不像普通的漏洞类型 它们不允许直接的改写内存或者直接改变程序的控制流程.而是更加精巧.
程序的所有者面临的事实是没有办法在进程里面检查计算发生后的结果所以有可能计算结果和正确
结果之间有一定的偏差.就因为这样大多数的整数溢出不能被利用即使这样在一些情况下我们还是有可能
强迫一个变量包含错误的值从而在后面的代码中出现问题.

由于这些漏洞的精巧导致有大量的地方能被利用所以我就不尝试覆盖到所有能被利用的环境.相反
我将提供一些能被利用的情况希望读者能自己来探索.我将提供一些能被利用的情况的例子.

Example 1:

/* width1.c - exploiting a trivial widthness bug */
#include <stdio.h>
#include <string.h>
int main(int argc char *argv[]){
unsigned short s;
int i;
char buf[80];
if(argc < 3){
return -1;
}
i = atoi(argv[1]);
s = i;
if(s >= 80){ /* [w1] */
printf("Oh no you dont! ");
return -1;
}
printf("s = %d " s);
memcpy(buf argv[2] i);
buf[i] = ;
printf("%s " buf);
return 0;
}

然而像这种构造可能从来不会在真实的代码里面出现.这里只是一个简单的例子让我们看看执行后
的输出:

nova:signed {100} ./width1 5 hello
s = 5
hello
nova:signed {101} ./width1 80 hello
Oh no you dont!
nova:signed {102} ./width1 65536 he

补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,