Go语言漏洞研究-整数安全

0x00 整数安全

1、整数反转

由于计算机中各整数类型都有一个宽度,有最大值-最大值范围。当试图保存超过该范围的数据时,无符号数就会发生整数反转,影响标志寄存器CF位。

         以上试验,证明GO语言中存在整数反转的问题,使得程序产生了非法数字。

2、整数溢出

由于计算机中各整数类型都有一个宽度,有最大值-最大值范围。当试图保存超过该范围的数据时,有符号数就会发生整数溢出,影响标志寄存器OF位。溢出本质上和反转类似。通过如下代码进行验证:

Go语言漏洞研究-整数安全_第1张图片

输入127和1,结果如下:

从结果来看有符号数最大值加1后产生溢出。

3、整数截断

将一个较大整型转换为较小整型,并且该数的原值超出较小整型的表示范围,就会发生截断错误,原值的低位被保留而高位被丢弃。截断错误会引起数据丢失,甚至可能引发安全问题。

Go语言是一种强类型的静态语言,强类型意味着一旦定义了,它的类型就不能改变了;静态意味着类型检查在运行前就做了。所以如下的整数转换编译的时候将不通过:

Go语言漏洞研究-整数安全_第2张图片

报错信息为:

Go语言漏洞研究-整数安全_第3张图片

编译的时候就会检查两边类型是否一致,强制要求两边类型一致,保证宽度一致,避免产生了整数截断的风险。

然而,Go语言提供了类型强制转换的功能,来跳过该限制,使用后上述代码将产生整数截断的风险:

      Go语言漏洞研究-整数安全_第4张图片

编译通过,运行结果为:

这是由于高位1被截断,数据位全部为0。

4、符号问题

有时从带符号整型向无符号整型转换时,最高位会丧失作为符号位的功能,即产生符号丢失但数据不丢失的问题,从而数据失去原来的含义。尤其对于带符号整型数的值为负时,它向无符号整型转换后,结果通常是一个非常大的正数;除非程序意图如此,否则需要注意值含义转换的正确性。

上节所述Go语言是一种强类型的静态语言,编译时就会检查类型。所以有符号向无符号转换时编译是不通过的。使得Go语言更加安全。

然而,可通过强制类型转换的功能,来跳过该限制。

  除非程序本意就是如此,否则需要注意使用。

5、移位位数足够

当对整数进行左移时,如果整数的类型位数不够会丢失高位数据,产生不预期的数据。

Go对无类型的常数依照参与表达式运算数据类型进行推导。进行移位操作时,自动推导出的类型宽度有可能小于实际对移位结果进行(比较)运算所要求的宽度。例如:

Go语言漏洞研究-整数安全_第5张图片

运行结果:

本意1左移16位大小为65536,应该比65535大。但是因为1经过类型推导为uint16,左移16位溢出了最高位,值变为了0。

6、除数为0

对整数做除法、取模、取余数操作时,除数或者模为0(下面统一称除数为0),将导致系统抛出异常。除非有默认的恢复机制,否则将导致无法继续当前的操作或任务。可造成拒绝服务攻击。

Go语言中检查不出变量除数被0的情况,编译能够通过。然而运行时会使程序退出,可能造成DOS攻击。

Go语言漏洞研究-整数安全_第6张图片

除数为0的情况造成程序退出。

0x01 案例试验

1、计数异常

以下为一个借款的模拟程序:

Go语言漏洞研究-整数安全_第7张图片

Go语言漏洞研究-整数安全_第8张图片

由于无符号整数反转,使得金额异常,银行所示惨重。

修复建议:通过限制值的范围防止反转。代码如下:

Go语言漏洞研究-整数安全_第9张图片

 

2、整数截断绕过判断

Go语言漏洞研究-整数安全_第10张图片

         上面代码第一处注释代码存在整数截断风险,len函数返回int类型整数,而接收变量为uint8。如果输入的密码参数长度假设为260,则passwd_len值为4,使得下面的长度判断失效,进入非法的条件语句中。下述密码长度为260:

Go语言漏洞研究-整数安全_第11张图片

输入字符串长度为260bytes,程序云心结果如下所示:

Go语言漏洞研究-整数安全_第12张图片

由于绕过判断,使得进入非法的流程代码中,造成访问数组越界,Go运行时程序将异常退出。

如果使用unsafe包对指针进行运算,将导致缓冲区溢出,造成任意代码执行。

上述代码稍作修改,tempPwd数组使用tempPwdPTR指针访问每个元素,将获取密码参数写到本作用域的局部变量中,由于数据校验的截断,将发生缓冲区溢出,造成栈数据被非法改写,甚至进一步会造成恶意代码执行。代码如下所示:

Go语言漏洞研究-整数安全_第13张图片

运行结果:

Go语言漏洞研究-整数安全_第14张图片

isOverWrite代码中从来没有对该数组进行写操作,正是由于缓冲区溢出写的漏洞,使程序退出前的数据被改变了。从上图后续也可以看出程序栈数据确实被修改了,理论上改写RIP可以实施代码执行。

0x02 整数使用注意事项

  1. 确保整数运算时不会有溢出或反转风险。代码中的所有算数运算(尤其是:+、-、*、++、--、+=、-=、*=),查看是否对参与运算的变量有正确的范围限定,确保没有溢出风险;
  2. 确保整型转换时不会出现截断错误。避免对变量使用强制类型转换语法,若必须使用则需要排查是否存在数据截断隐患;
  3. 确保整型转换时不会出现符号错误。对于代码中变量可能为负数的情况需要排查是否存在违背程序意图的符号错误隐患;
  4. 确保参与移位的操作数的位数足够。产生移位的地方,必须小心检视确保移动位数的对象宽度足够。建议对移位的常量使用强制类型转换语法到宽度足够的类型;
  5. 确保整数运算不会出现除零。查找程序中所有出现除法的语句,检查除数是否有为0的分支判断或者异常处理,检查除数来源是否有为0的风险。

0x03 排查方法

  1. 在数学运算前是否对参与运算的整数进行范围判断。
  2. 赋值语句处,并且有强制类型转换需要注意是否从大类型转换到小类型、或者是否存在符号因素会使值产生坏值。
  3. 查找移位运算符,特别是移位的数据为常量。则通过表达式分析,检查常量实际被推导出的类型大小是否满足移位的要求。
  4. 查找全文的除法运算发生的地方,检查除数为0的情况是否有判断,或者除数来源是否有为0的可能

你可能感兴趣的:(安全编码,Go,指针,整数安全)