Agile Alliance 由一次需求边界值测试引发的【整数溢出攻击】思考

1 整体思路

1.1 背景描述

某需求测试过程中:新增报价申请界面,点击【暂存】或【提交】,显示系统错误,无法提交,截图如下:

1.2 日志定位

查看日志,发现报错是:4398046512275超过int(-2147483648-2147483647)的边界范围。

 1.3 查看数据库

可以看到在数据库对应的表中,product_id被定义为bigint类型,表中的数据长度定义的足够长,所以4398046512275可以存储到数据库中,是没有问题的。

 1.4 查看代码

可以看到后端代码是声明的int类型,所以4398046512275是在代码层处理的时候也是有问题的,会将4398046512275转为为一个错误的值,导致代码处理中产生错误。

 1.5 思路总结

报错显示的4398046512275为“产品ID”,即产品对应的是产品的编号,在数据库中可以存下,而在后台代码处理中是int,导致代码运行中其他要用到的“产品ID”是个异常值,所以报错。

 1.6 解决方案

修改代码中productId的类型为long,这样可以存下4398046512275这样的比较的大的数据。

2 关于整数溢出深入代码层的思考

2.1 举个栗子

#include <stdio.h>

//返回读取的内容长度
int Read(int fd, int start, int end) {
    int length = end - start + 1;

    //Read 函数中最大只支持一次读取1024个字节,所以增加了一个判断逻辑。
    if (length > 1024)
        return -1;

    return length;
}

int main(void)
{
	//调用函数Read
	int Length = Read(0, 0, 5);

	printf("长度为:%d\n",Length);
	return 0;
}

大家看下,以上代码有没有什么问题?

2.2 调用函数

Length = Read(0, 0, 4294967295);

这里的4294967295会传参给end,而4294967295大于1024,正常会返回-1,但是真实情况是这样的吗?

但是请注意Read这个函数的参数中,startend都是int型变量,把4294967295传递给end的时候,会被当作有符号的整数解析,也就是 -1 所以在执行以下代码时,

int length = end - start + 1;

length的结果会是0!计算结果就是-1 - 0 + 1 = 0!这里计算的结果逃过了代码中对于长度大于1024的检查。

if (length > 1024)
    return -1;

2.3 思考

整数溢出在日常其他方面是否真实存在影响?

​​​​​​​

3 关于整数溢出的影响

3.1 真实栗子

漏洞编号CVE-2015-1635

漏洞编号

CVE-2015-1635

漏洞描述

windows2008+iis7,当向服务器web服务发送特殊header时,会造成IIS服务器蓝屏崩溃,特殊环境下或导致信息泄露

且这个漏洞位于IIS处理HTTP请求的HTTP.sys驱动程序中。

验证方式

wget --header=”Range: bytes=18-18446744073709551615” http://ip/welcome.png

漏洞危害

造成IIS服务器蓝屏崩溃,特殊环境下或导致信息泄露

受影响版本

IIS 7.0以上的Windows 7/8/8.1和Windows Server 2008 R2/Server 2012/Server 2012 R2等操作系统

修复建议

目前微软官方已经给出修复补丁,用户安装修复补丁即可,选择 KB3042553 安全更新进行系统升级:http://www.catalog.update.microsoft.com/Search.aspx?q=KB3042553

漏洞分析

MS15-034/CVE-2015-1635 HTTP远程代码执行漏洞分析 - 360 核心安全技术博客

这是一个微软的互联网服务器IIS中的一个漏洞,更重要的是,这个漏洞位于IIS处理HTTP请求的HTTP.sys驱动程序中。

而驱动程序是运行在操作系统内核之中,一旦内核驱动执行出现异常,那后果,轻则蓝屏崩溃,重则直接被攻击者远程执行代码,控制服务器。

3.2 HTTP协议中Range字段

HTTP 1.1版本的协议中,可以通过请求头中的Range字段,请求或上传指定资源的部分内容:

GET /bg-upper.png HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8180
Accept: */*
Range: bytes=0-10

其中的Range字段格式如下:

Range: bytes=start-end

微软的IIS为了提高性能,将HTTP协议的解析放在了内核驱动 HTTP.sys 中实现。

分析:而其中对range字段的处理,就是第2部分中的Read代码的那个逻辑错误,不同的是,我们上面的那个示例是一个32位整数的版本,而IIS这个真实的漏洞是64位整数产生的问题,但原理是一样的。

3.3 整数溢出攻击

通过向存在漏洞的IIS服务器发送对应的HTTP请求,即可将目标服务器打蓝屏,实现DOS——拒绝服务攻击。

 这种攻击方式就是——整数溢出攻击

3.4 思考

其他常见的整数溢出有哪些?

4 其他常见的整数溢出

4.1 有符号数溢出

4.1.1 上溢出

#include <stdio.h>
#include <limits.h> 


int main(void)
{
	int i;
	i = INT_MAX; //i = 2147483647
	i++;
	printf("i = %d\n",i); //i = -2147483648
    return 0;
}

4.1.2 下溢出

#include <stdio.h>
#include <limits.h> 


int main(void)
{
	int i;
	i = INT_MIN; //i = -2147483648
	i--;
	printf("i = %d\n",i); //i = 2147483647
}

4.2 无符号数回绕

4.2.1 上回绕

#include <stdio.h>
#include <limits.h> 


int main(void)
{
	unsigned int i;
	i = UINT_MAX; //在32位电脑上:i = 4294967295
	i++;
	printf("i = %u\n",i); //i = 0
}

4.2.2 下回绕

#include <stdio.h>


int main(void)
{
	unsigned int i;
	i = 0;
	i--;
	printf("i = %u\n",i); //在32位电脑上:i = 4294967295
}

4.3 截断

4.3.1 加法截断

#include <stdio.h>


int main(void)
{
	long a, b, c;
	a = 0xffffffff;
	b = 0x00000001;
	c = a + b; // c = 0x0000001 00000000(long long) = 0x00000000(long)
	printf("c = 0x%x\n",c); //c = 0x00000000
}

4.3.2 乘法截断

#include <stdio.h>


int main(void)
{
	long a, b, c;
	a = 0x00123456;
	b = 0x00654321;
	c = a * b; // c = 0x00000733 6bf94116(long long) = 0x6bf94116(long)
	printf("c = 0x%x\n",c); //c = 0x6bf94116
}

4.4 宽度溢出(不够的用符号位填充)

#include<stdio.h>


int main(){
	int a;
	short b;
	char c;
	a = 0xabcddcba;
	b = a;
	c = a;
	printf("a = 0x%x(%d bits)\n", a, sizeof(a) * 8);//a = 0xabcddcba(32 bits)
	printf("b = 0x%x(%d bits)\n", b, sizeof(b) * 8);//b = 0xffffdcba(16 bits)
	printf("c = 0x%x(%d bits)\n", c, sizeof(c) * 8);//c = 0xffffffba(8 bits)
	printf("b + c = 0x%x(%d bits)\n", b+c, sizeof(b+c) * 8);//b + c = 0xffffdc74(32 bits)
}

思考:为什么输出的b和c的高位用ffff填充?

解答:因为变量b和c定义的为有符号的类型,以b为例,当语句执行b = a;此时先看 低位 dcba(1101 1100 1011 1010)2个字节(16位)可以容纳b,而高位需要看b的符号位,dcba(1101 1100 1011 1010)为1,所以高位全部用1填充,即ffff(1111 1111 1111 1111),执行完语句b = a后 b = 0xffffdcba; 以c为例,当语句执行c = a;此时先看 低位 ba(1011 1010)1个字节(8位)可以容纳c,而高位需要看c的符号位, ba(1011 1010)为1,所以高位全部用1填充,即ffffff(1111 1111 1111 1111 1111 1111),执行完语句c = a后 c= 0xffffffba。

5 心得及总结

  1. 开发 or 测试 中,一定要注意变量的数据类型,特别是涉及到数据类型转换的地方要格外留神。比如符号数与无符号数的互转,32位整数和64位整数的转换等等。
  2. 涉及联调,在处理上下游系统传参数处理时,要慎之又慎,一个小小的变量类型可能就会给服务器计算机造成毁灭性打击。
  3. 测试活动中,要注意数据类型的边界值的测试(而非仅仅是PRD中定义的最大值)。
  4. 数据类型的长度范围汇总:

Type

Size

数值范围

无值型void

0 byte

无值域

布尔型bool

1 byte

true、false

有符号短整型short [int] / signed short [int]

2 byte

-32768 ~ 32767

无符号短整型unsigned short [int]

2 byte

0 ~ 65535

有符号整型int / signed [int]

4 byte

-2147483648 ~ 2147483647

无符号整型unsigned [int]

4 byte

0 ~ 4294967295

有符号长整型long int / signed long [int]

4 byte

-2147483648 ~ 2147483647

无符号长整型unsigned long [int]

4 byte

0 ~ 4294967295

long long

8 byte

0 ~ 18446744073709552000

有符号字符型char / signed char

1 byte

-128 ~ 127

无符号字符型unsigned char

1 byte

0 ~ 255

宽字符型wchar_t / unsigned short

2 byte

0 ~ 65535

单精度浮点型float

4 byte

-3.4E-38 ~ 3.4E+38

双精度浮点型double

8 byte

1.7E-308 ~ 1.7E+308

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值