Featured image of post PWN-4

PWN-4

二进制漏洞与利用——整数安全

前言:在二进制安全领域中,整数安全是需要去关注的,虽说内容较少,但不容忽视,整数溢出及FMT这块应和栈堆溢出漏洞联动的,但为了有更好的区分度以及更好理解,我索性把它俩各独立成一篇文章供入门PWN读者食用,在此基础上还增加了一些应用案例,难免会“啰嗦”,可以稍微跳过,谢谢支持。

整数溢出在PWN中属于比较简单的内容,当然并不是说整数溢出的题目比较简单,只是整数溢出原理本身不是很复杂,情况较少而已。但是整数溢出本身是直接无法利用的,需要结合其它手段才能达到利用的目的。

(参考星盟安全、Nu1L:《Ctfer:From Zero to One》)

整数溢出漏洞的定义

定义:“整数溢出”漏洞,是计算机对数值进行处理的过程中(存储、转换、条件判断、分类),产生了非预期的错误,使其产生可被利用的漏洞。

人话就是,正常人思考正确逻辑应该是1+1=2,那个人开发出计算机时对整数这方面的设计发生非预期错误,导致了计算机1+1=3(这不扯淡吗?😄),而且此后的程序运行还建立在此错误的基础上,那么计算机此时就偏离理想的结果预期了/(ㄒoㄒ)/~~。

在C语言中,整数的基本数据类型分为短整型(short),整型(int),长整型(long),这三个数据类型还分为有符号和无符号,每种数据类型都有各自的大小范围,如下所示:

类型 字节 范围
int 4 byte 0 ~ 2147483647(0 ~ 0x7FFFFFFF)
-2147483648 ~ -1(0x8000 ~ 0xFFFFFFFF)
short int 2 byte 0 ~ 32767(0~0x7FFF)
-32768 ~ -1(0x8000 - 0xFFFF)
unsigned short int 2 byte 0 ~ 65535(0 ~ 0xFFFF)
unsigned int 4 byte 0 ~ 4294967295(0 ~ 0xFFFFFFFF)
long int 8 byte 正:0 ~ 0x7FFFFFFFFFFFFFFF
负:0x8000000000000000 ~ 0xFFFFFFFFFFFFFFFF
unsigned long int 8 byte 0 ~ 0xFFFFFFFFFFFFFFFF

当程序中的数据超过其数据类型的范围,则会造成溢出,整数类型的溢出被称为整数溢出。

计算机存储原理

首先,要知道,计算机存储一个“整数”,是“模2的偶数次幂”的原理。负数的编码上,也只是一个移码,它只是把整个“负数集合”,从左往右挪(移),假设左边是负数,右边是整数。

至于补码,其实就是加了一个“轮回”(2的偶数次幂)。至于,浮点数的存储原理,和整数非常不同,本文主要探究整数溢出,但浮点数的存储原理,我依然会在下面提及一点点。

浮点数,主要是涉及一个“无论多少位都无法精确表达”的问题,就是IEEE754中,浮点数设计的缺陷。在十进制中,唯有尾数是0或5的小数,才可以理论上被精确表示。 浮点数,用一个三元组表示{Sign,Exponent, Significance}。其中,指数位(Exponent),主要是一个移码的设计。当指数位为全0的时候,可以表示十进制数0。当指数位全1的时候,可以表示正/负无穷。此上述两者为特殊数值,故去掉后,码位减少了2个位置。在减少两个位置的情况下,将正数、负数,“均等”地编码在这些码位上,则移码的偏移量为127。如果指数是3,则机器存储,为3+127=130;负数同理,亦是加上127,再进行机器存储。这个移码设计,保证了存储上,正数大于负数。

对于浮点数的漏洞,我在ctfPWN学习中,并未见到一个典型案例,当然可能是我没遇到…此处不做讨论,当前我们PWN中心应当在栈堆利用和内核学习。但理论上这个浮点数漏洞是存在的。

如果连浮点数都能轻易理解,那么理解整数,整数上的模2群运算,应该不成问题。顶多涉及一些编码负数那位置)的问题。显然,一个数(无符号数),加着加着,或者减着减着,或者去做乘法,它有可能溢出,跑到负数的编码位置上。如果程序设计不好,将这个“不符合预期理论设计”得出的数,当作一个有符号数(负数)进行处理,那肯定出问题。

计算机条件判断

这个,涉及X86的汇编,就是0F标志位,和SF标志位。〇F标志位,只对无符号数有意义,因为,正数加正数,不会出现一个负数(符号位为1);负数加负数,不会出现一个正数(符号位为)。

至于,SF,就更简单了,记录了运算结果的符号位。SF,运算结果是正数,就是0;运算结果是负数,就是1。X86的计算机,根据这两个标志位,就可以进行有符号数、无符号数的大小判断了。(简单了解)

整数溢出的利用

好了,我知道你在想什么。感觉我在讲废话对吧!

整数溢出虽然简单,但实际上利用起来并不见得容易,在我们的PWN生涯中,上面三个,导致“整数溢出”漏洞的诱因,往往综合起来出现。

举个例子,譬如,体现在一个函数(功能)上:有一个函数(如atoi、strlen函数等),对输入的字符进行处理,并转化为数值。如果该数值,存储的过程中,被模“截断了”,那么它导致程序出现非预期行为的概率非常大。如果,后续,还用这个数值做判断,那不就是多个诱因综合出现嘛😄。

整数溢出转换成缓冲区溢出

整数溢出可以将很小的数突变成很大的数,比如,无符号下溢可以将一个表示缓冲区大小的较小的数通过减法变成一个超大的整数,从而导致缓冲区溢出(正负转换,栈溢出联动)

整数溢出转数组越界

这个比较好理解,在我大一学习C语言时,学到数组这一块,我很清楚数组索引的操作只是简单地将数组指针加上索引来实现的,并不会检查边界。因此,很大的索引会访问到数组后的数据,如果索引是负数,那么还会访问到数组之前的内存。

通常,整数溢出转数组越界更常见。在数组索引的过程中,数组索引还要乘以数组元素的长度来计算元素的实际地址。以int类型数组为例,这个数组索引需要乘以4来计算偏移。加入通过传入负数来绕过边界检查,那么正常情况下只能访问数组之前的内存。但由于索引会被乘以4,所以可以索引数组后的数据甚至整个内存空间。例如,想要索引数组后0x1000字节处的内容,只需要传入负数-2147482624,该值用十六进制数表示为0x80000400,再乘以元素长度4后,由于无符号整数上溢结果,即为0x00001000。可以看到,与整数溢出转缓冲区溢出相比,数组越界更容易利用

总结

整数溢出虽然简单,但实际上利用起来并不见得容易,在我们的PWN生涯中:整数溢出不像栈溢出扽会给内存破坏可以直接通过覆盖内存进行利用,常常需要进行一定转换才能溢出,整数溢出的单独典型例子并不多,大都融合进栈堆内核等大类漏洞进行考察,当然,初期学习整数安全,我这有少量例子,可以借鉴我的文章——CTFshow - 入门 PWN-writeup

最后更新于 2025-12-28