
文章目录
-
- [1. 什么是整数溢出?](#1. 什么是整数溢出?)
- [2. 整数溢出的原理(二进制视角)](#2. 整数溢出的原理(二进制视角))
-
- [示例:8位无符号整数(uint8,范围 0~255)](#示例:8位无符号整数(uint8,范围 0~255))
- 有符号整数(补码表示)
- [3. 常见整数类型位宽一览](#3. 常见整数类型位宽一览)
- [4. 整数溢出在网络安全中的危害](#4. 整数溢出在网络安全中的危害)
- [5. 真实世界案例](#5. 真实世界案例)
- [6. CTF例题:菜狗杯 - 茶歇区](#6. CTF例题:菜狗杯 - 茶歇区)
-
- [攻击 Payload](#攻击 Payload)
- [7. 如何防范整数溢出?(开发者必备 checklist)](#7. 如何防范整数溢出?(开发者必备 checklist))
- [8. 总结与学习建议](#8. 总结与学习建议)
1. 什么是整数溢出?
整数溢出(Integer Overflow) 是指:计算机用固定长度的二进制位(bit)来存储整数,当运算结果超出这个"容器"的容量时,就会发生"溢出"------数值被截断或回绕,导致程序行为异常。
比喻
想象一个只能显示两位数字的汽车里程表(00~99):
- 当前是 99 公里
- 开了 1 公里后 → 本该是 100,但表只能存两位数字 → 直接回绕成 00
计算机的整数类型(int、long、uint 等)就像这个里程表,每种类型都有固定的"位宽"(bit width),超过上限就会"洒出来"。
2. 整数溢出的原理(二进制视角)
计算机内存中,整数是用二进制补码存储的。
示例:8位无符号整数(uint8,范围 0~255)
- 255 的二进制:
11111111 - 执行
255 + 1:- 计算结果:
1 00000000(9位) - 8位容器只能存低8位 →
00000000(即 0)
- 计算结果:
代码演示(Python 模拟 8 位溢出):
python
def uint8_add(a: int, b: int) -> int:
return (a + b) & 0xFF # 保留低8位,模拟溢出
print(uint8_add(255, 1)) # 输出: 0
print(uint8_add(200, 100)) # 输出: 44 (300 - 256 = 44)
有符号整数(补码表示)
有符号整数最高位是符号位(0=正,1=负)。
32位有符号 int(int32) 范围:
- 最小值:
-2147483648(0x80000000) - 最大值:
2147483647(0x7FFFFFFF)
溢出示例:
c
// C语言示例(编译运行可验证)
#include <stdio.h>
#include <limits.h>
int main() {
int max = INT_MAX; // 2147483647
printf("MAX: %d\n", max);
printf("MAX + 1: %d\n", max + 1); // 输出: -2147483648(溢出回绕到最小负数)
return 0;
}
3. 常见整数类型位宽一览
| 位宽 | 类型 | 最小值 | 最大值 |
|---|---|---|---|
| 8位 | uint8 | 0 | 255 |
| 8位 | int8 | -128 | 127 |
| 16位 | uint16 | 0 | 65,535 |
| 16位 | int16 | -32,768 | 32,767 |
| 32位 | uint32 | 0 | 4,294,967,295 |
| 32位 | int32 | -2,147,483,648 | 2,147,483,647 |
| 64位 | uint64 | 0 | 18,446,744,073,709,551,615 |
| 64位 | int64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
PHP 中 :64位系统下 int 默认就是 int64 ,可通过 PHP_INT_MAX 常量获取最大值。
php
<?php
echo PHP_INT_MAX; // 输出: 9223372036854775807
var_dump(PHP_INT_MAX + 1); // 输出: float(9.223372036854776E+18)
// PHP 7+ 会自动转为 float,但乘法运算中仍可能产生回绕
?>
4. 整数溢出在网络安全中的危害
溢出本身不会让程序崩溃,但它会让逻辑判断失效,成为攻击者的切入点:
-
绕过大小/数量限制
文件上传检查
if ($size > 2*1024*1024),若$size接近PHP_INT_MAX,乘以系数后溢出 → 检查失效。 -
缓冲区溢出(Buffer Overflow)
计算内存分配大小时溢出,导致实际分配内存远小于预期,后续写入数据覆盖其他内存 → 可执行任意代码(RCE)。
-
数组索引越界
用溢出后的负数作为数组下标,读取敏感内存。
-
逻辑绕过(权限、价格、计数器)
游戏货币、区块链转账、电商折扣计算等都可能被刷。
经典代码漏洞示例(文件大小检查绕过)
php
<?php
$max_size = 2 * 1024 * 1024; // 2MB
$file_size = $_FILES['file']['size']; // 用户可控
// 危险!未做溢出检查
if ($file_size > $max_size) {
die("文件太大!");
}
move_uploaded_file(...); // 实际可上传超大恶意文件
?>
改进版(推荐):
php
<?php
$file_size = (float) $_FILES['file']['size']; // 转为 float 避免溢出
if ($file_size > 2 * 1024 * 1024 || $file_size < 0) {
die("文件大小非法!");
}
// 乘法前显式检查
if ($file_size > PHP_INT_MAX / 10) { // 防止后续乘法溢出
trigger_error("潜在整数溢出!", E_USER_WARNING);
die("操作拒绝");
}
?>
5. 真实世界案例
- OpenSSH 整数溢出(2000s) :
size * 1024溢出导致分配 0 字节缓冲区 → 远程代码执行。 - Ariane 5 火箭爆炸:64位浮点数转16位整数时溢出,火箭自毁(损失5亿美元)。
- 区块链智能合约 :许多 Solidity 合约因
uint256溢出被刷币(后引入 SafeMath 库)。 - 游戏外挂:数量/金钱计算溢出,实现无限刷资源。
6. CTF例题:菜狗杯 - 茶歇区

这道题完美体现了"整数溢出绕过限制":
- 题目背景:你有 1024 FP(资金),每种食物得分 = 消耗 FP(比率 1:1)。目标得分 > 114514 才能出 flag。
- 正常情况下:最多得 1024 分 → 不可能。
- 漏洞点 :后台用
int64计算FP = FP - count * cost和score = count * score。
攻击 Payload
第一次提交(触发 FP 溢出):
- 咖啡(e)填:
932337203685477581(18位,刚好让count * cost溢出) - 其他食物填 0
第二次提交:
- 再次填相同数字(或 1000000000000000000)
- 提交后 FP 变成极大正数,得分轻松超过 114514,flag 弹出。
Python 模拟器(本地复现):
python
PHP_INT_MAX = 9223372036854775807
FP = 1024
count = 932337203685477581
cost = 1
# 第一次购买(模拟溢出)
new_fp = FP - (count * cost) # PHP 中可能回绕成负数或极大值
print("第一次后 FP:", new_fp) # 通常为极大正数
# 第二次购买
score = count * 10 # 咖啡得分10
print("最终得分:", score) # 远超114514
Burp Suite 抓包技巧:
- 正常提交一次,抓到 POST 数据。
- 用 Repeater 重复发送两次 payload。
- 修改
e=932337203685477581。
正常做法(不用BP)参考:ctfshow菜狗杯wp2(新手必刷)(Web部分)
7. 如何防范整数溢出?(开发者必备 checklist)
-
输入强制类型转换
php$user_input = (float) $_POST['count']; // 优先用 float -
乘法前显式检查
phpif ($count > 0 && $cost > PHP_INT_MAX / $count) { die("数值过大,拒绝操作!"); } -
使用安全库
- PHP:
gmp扩展处理任意精度整数。 - C/C++:
SafeInt或__int128。 - Python:原生支持无限大整数,无需担心。
- PHP:
-
边界检查 + 日志
所有用户输入必须经过
is_numeric()+ 范围检查 +trigger_error。 -
代码审计 checklist:
- 文件大小(
filesize()) - 数组索引(
isset()+count()) - 内存分配(
str_repeat($str, $size)) - 循环计数器
- 文件大小(
8. 总结与学习建议
整数溢出本质是:"计算机的整数不是数学上的无限大,而是有容量上限的固定容器,装不下就会洒出来,洒出来的那一刻就是漏洞。"
下一步行动:
- 本地搭建 PHP 环境,运行上面的所有代码示例。
- 去 CTF 平台搜"整数溢出"标签,再刷 2-3 道题。
- 阅读 OWASP Top 10 中的"注入"和"安全配置错误"章节。
- 推荐书籍:《白帽子讲 Web 安全》、《C 语言安全编程》。
最后思考:安全从来不是"加固",而是"从设计之初就考虑边界"。下次写任何数量、价格、索引相关的代码,都提醒自己问一句:"这个数会不会溢出?"
参考:
- PHP 官方文档:PHP_INT_MAX
- 文章《PHP的整数溢出漏洞:在处理文件大小或数组索引时的边界检查与位宽问题》
- 菜狗杯 2025 茶歇区题目