利用数组越界访问调用结构体中win函数指针的可行性咨询
问题背景
手头有一个存在漏洞的C文件,需要分析其利用方式。发现漏洞出在结构体访问环节,相关代码如下:
#define BOARD_SIZE 10 typedef int (*turn_function_t)(struct board *); typedef void (*win_function_t)(struct board *); struct board { uint8_t f1[BOARD_SIZE][BOARD_SIZE]; uint8_t f2[BOARD_SIZE][BOARD_SIZE]; win_function_t win; turn_function_t turn; int avail; }; int do_shot(struct board *board, int strength, int x, int y) { if(!(x >= 0 && x <= BOARD_SIZE && y >= 0 && y <= BOARD_SIZE)) { return SHOT_ERR_EINVAL; } /* If there was already a sunken ship, return error */ if(board->f1[x][y] && !board->f2[x][y]) return SHOT_ERR_SUNKEN; /* Now perform shot */ if(!board->f2[x][y]) return SHOT_WATER; board->f2[x][y] -= strength; if(!board->f2[x][y]) return SHOT_SUNKEN; return SHOT_HIT; }
漏洞点在于do_shot函数对数组f2的索引检查错误:允许输入0到10(包含)的x、y值,但f2是[10][10]的数组,合法索引仅为0-9,这意味着x=10或y=10时会触发越界访问。而win函数指针恰好存储在f2数组的内存后方,想知道能否利用这个越界访问调用win函数(参数无关)。
分析与结论
无法直接通过do_shot的越界访问调用win函数,因为do_shot本身没有任何调用函数指针的逻辑。但可以利用这个越界写漏洞修改win函数指针的内容,待程序后续在正常业务流程中调用win时,触发执行目标代码(包括原win函数或自定义恶意逻辑)。具体利用步骤如下:
确定内存偏移与对齐
f1和f2各占10*10=100字节,总大小为200字节。需注意编译器的内存对齐规则:部分编译器会在f2数组与win指针之间添加填充字节,以满足函数指针的对齐要求(32位系统通常4字节对齐,64位为8字节)。
可通过offsetof(struct board, win)宏计算win指针相对于结构体起始地址的偏移量,再减去f2的偏移量(100字节),得到f2数组到win指针的字节偏移。例如:- 若无填充,偏移量为100字节,对应
x=10,y从0开始即可访问win指针的各个字节; - 若存在4字节填充,需
x=10,y从4开始才能定位到win指针。
- 若无填充,偏移量为100字节,对应
逐字节修改
win函数指针
由于board->f2[x][y] -= strength是单字节写入操作,可通过多次调用do_shot,每次修改win指针的一个字节:- 针对32位系统,调用4次
do_shot,分别修改win指针的4个字节,将其指向目标函数地址; - 针对64位系统,需调用8次完成完整地址覆盖。
- 针对32位系统,调用4次
触发
win函数调用
等待程序进入调用win指针的正常流程(例如游戏胜利后的回调逻辑),此时程序会执行我们修改后的指针指向的代码,从而达成调用目标函数的目的。
内容的提问来源于stack exchange,提问作者Dan23

