0x00 写在前面
之前写题的时候遇到过 int 整数溢出的题,正好今天在看 CSAPP 的视频(CMU真diao),又在ctfshow上看见了一道题,就来写一写整数溢出。
0x01 数据类型
在 C 语言中,整数的基本数据类型分为短整型 (short),整型 (int),长整型 (long),这三个数据类型还分为有符号和无符号,每种数据类型都有各自的大小范围,由编译器决定。
64位下各数据类型大小范围:
数据类型 | 字节数 | 最小值 | 最大值 |
---|---|---|---|
char | 1 | -128 | 127 |
unsigned char | 1 | 0 | 255 |
short | 2 | -32768 | 32767 |
unsigned short | 2 | 0 | 65535 |
int | 4 | -2147483648 | 2147483647 |
unsigned int | 4 | 0 | 4294967295 |
long | 8 | -9223372036854775808 | 9223372036854775807 |
unsigned long | 8 | 0 | 18446744073709551615 |
- Int8, 等于Byte, 占1个字节.
- Int16, 等于short, 占2个字节.
- Int32, 等于int, 占4个字节.
- Int64, 等于long, 占8个字节.
当程序中的数据超过其数据类型的范围,则会造成溢出
0x02 上界溢出
上界溢出分为两种,比如说有两个字节的 short 型,一种是 0x7fff + 1
,一种是 0xffff + 1
。
第一种对无符号型没有影响,第二种对有符号型没有影响。
在计算机的底层,数据是以二进制进行保存的,0x7fff的二进制形式为 0111 1111 1111 1111
,加了1之后就等于 1000 0000 0000 0000
,十六进制就为0x8000,转化为unsigned char就是32768没有影响,但是转化为signed char的话就是-32768,发生了溢出。
第二种溢出,0xffff的二进制形式为 1111 1111 1111 1111
,加了1之后等于 0001 0000 0000 0000 0000
,这里short只有两个字节的空间,而现在显然超出了他的大小范围,这个时候就会将最高位的1舍去,只留下了 0000 0000 0000 0000
,也就是0,也就相当于 65535 + 1 == 0
成立。
0x03 下界溢出
下界溢出与上界溢出同理,0x8000 - 1
和 0x000 - 1
。
第一种对于无符号型来说没有影响,32678 - 1 = 32677
,但是对于有符号型来说就是 -32678 - 1 = 32677
。
第二种对于有符号型来说没有影响,0 - 1 = -1
,但是对于无符号型来说就是 0 - 1 = 65535
。
0x04 漏洞触发
当我们的代码在进行类型转化时没有过多注意,进行了错误的类型转化,或者对用户的输入范围没有进行限制或只进行了单边的限制,就有可能触发整数溢出漏洞。
0x05 例题 ctfshow 数学99
下载文件,反编译一下,可以看到,他给了我们几个数学问题,让我们进行输入,如果有一个不满足条件则退出程序。
第一个 a-b=9,0<=a<9,0<=b<9
,输入的a和b都是signed int型,四个字节大小。题中对输入进行检测,存在负号的话返回0,
输入a为8,b为4294967295,4294967295的二进制表示为0xffffffff,转化为signed int型就等于-1,8 - (-1) = 9
第二个 a*b=9,a>9,b>9
,对我们输入的上限没有限制,我们可以找到一个溢出为9的数,然后对他进行分解。比如说 4294967305 = 48145 * 89209
。
第三个 a/b=ERROR,b!=0
。当输入的b不为0时,有 signal(8, (__sighandler_t)handler);
。
signal函数的value为8,也就是说存在 Floating point exception
时触发handler,而handler恰好可以帮我们读取flag。但是常规的触发 Floating point exception
是使得分母为0,可是b已经被限制不能为0。
SIGFPE:
According to POSIX, the behavior of a process is undefined after it ignores a SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(2) or raise(3). Integer division by zero has undefined result. On some architectures it will generate a SIGFPE signal. (Also dividing the most negative integer by -1 may generate SIGFPE.) Ignoring this signal might lead to an end‐ less loop.
除了分母为0,最小的整数除以-1也会触发 Floating point exception
,于是输入2147483648或者-2147483648,以及-1即可。
from pwn import *
io = remote("pwn.chall.ctf.show", 28005)
io.sendlineafter("a:", "8")
io.sendlineafter("b:", "4294967295")
io.sendlineafter("a:", "48145")
io.sendlineafter("b:", "89209")
io.sendlineafter("a:", "-2147483648")
io.sendlineafter("b:", "-1")
io.interactive()
Comments | NOTHING