一、漏洞概述
1.1 核心概念
越权(Privilege Escalation)是指攻击者通过某种方式获取了超出其应有权限的操作能力。在Web应用中,越权漏洞通常表现为:一个用户能够访问或操作另一个用户的数据或功能,而系统未能正确验证操作者的身份与资源归属关系。
一句话原理:系统只验证了用户是否登录,但没有验证用户是否有权操作目标资源。
1.2 越权分类
| 类型 | 英文 | 说明 | 实例 |
|---|---|---|---|
| 水平越权 | Horizontal | 同级别用户之间越权 | 普通用户A访问普通用户B的订单 |
| 垂直越权 | Vertical | 低权限用户获取高权限功能 | 普通用户执行管理员操作 |
| 交叉越权 | Cross | 既有水平又有垂直 | 普通用户越权成为管理员 |
1.3 危险代码示例
// 水平越权:只验证了登录,未验证归属 <?php session_start(); $order_id = $_GET['order_id']; $user_id = $_SESSION['user_id']; // 当前登录用户ID // 危险!只根据order_id查询,没有检查该订单是否属于当前用户 $sql = "SELECT * FROM orders WHERE id = $order_id"; $order = $db->query($sql); echo $order['detail']; ?> // 攻击者只要修改 order_id 参数即可查看他人订单// 垂直越权:普通用户访问管理员接口 <?php // admin/del_user.php - 删除用户接口 session_start(); $user_id = $_GET['user_id']; // 危险!只判断了是否登录,没有判断是否是管理员 if(isset($_SESSION['user_id'])) { $db->query("DELETE FROM users WHERE id = $user_id"); } ?>二、水平越权
2.1 原理
水平越权发生在同一角色/权限级别的用户之间,攻击者通过修改资源ID等参数,访问或操作其他用户的私有资源。
2.2 常见场景
| 场景 | 越权方式 | 示例 |
|---|---|---|
| 查看订单 | 修改订单号 | order_id=1001→order_id=1002 |
| 个人资料 | 修改用户ID | user_id=1→user_id=2 |
| 文件下载 | 修改文件名 | file=resume_1.pdf→file=resume_2.pdf |
| 消息读取 | 修改消息ID | msg_id=100→msg_id=101 |
| 购物车 | 修改CartID | cart=abc123→cart=abc124 |
2.3 实战示例
场景1:订单查看
# 正常请求 GET /my_order.php?order_id=12345 # 越权尝试 GET /my_order.php?order_id=12346 # 只要ID存在且可遍历,即可查看他人订单场景2:用户资料修改
# 正常请求 POST /profile.php user_id=1001&email=hacker%40evil.com # 越权(如果未校验归属) POST /profile.php user_id=1002&email=victim%40evil.com # 修改他人邮箱场景3:接口无权限校验
# 管理员删除用户的接口(但未做权限验证) GET /admin/del_user.php?user_id=5 # 普通用户登录后,直接访问该URL即可删除任意用户2.4 检测方法
# 1. 准备两个账号 A 和 B # 2. 登录 A,获取 A 的资源ID # 3. 修改资源ID为 B 的资源ID # 4. 观察能否访问/操作 B 的资源 # 示例:订单查看 A登录 → 查看订单 1001 修改参数 → 查看订单 1002(B的订单) 如果能看到B的订单信息 → 存在水平越权三、垂直越权
3.1 原理
垂直越权是指低权限用户(如普通会员、游客)通过某种方式执行了高权限用户(如管理员、超级管理员)才能执行的操作。
3.2 常见场景
| 场景 | 越权方式 | 示例 |
|---|---|---|
| 管理员功能 | 未做角色校验 | 普通用户访问/admin/user_list.php |
| 隐藏按钮 | 功能暴露但无权限验证 | 前端隐藏管理按钮,但后端接口未校验 |
| 参数伪造 | 传入高权限参数 | role=admin |
| 直接访问API | API路径可猜测 | /api/delete_user |
3.3 实战示例
场景1:未校验管理员身份
// admin.php - 后台管理页面 <?php session_start(); // 危险:只判断是否登录,未判断角色 if(isset($_SESSION['user_id'])) { // 管理员操作 $users = $db->query("SELECT * FROM users"); } ?> // 普通用户登录后直接访问 admin.php 即可看到所有用户场景2:角色参数在请求中
# 正常请求(可能通过隐藏字段) POST /update_role.php user_id=1001&role=user # 越权尝试 POST /update_role.php user_id=1001&role=admin # 如果后端信任前端传参,即可提权场景3:未授权API访问
# 管理员专用API(但没有任何鉴权) POST /api/create_admin username=hacker&password=123456 # 攻击者直接调用即可创建管理员账号3.4 检测方法
# 1. 使用普通用户登录 # 2. 抓取所有功能请求 # 3. 尝试访问管理员专用URL(如 /admin, /manage, /system) # 4. 尝试访问管理员API(通过JS或文档发现) # 5. 尝试修改请求中的权限相关参数(如 role=admin) # 示例:访问后台 普通用户登录后,直接访问 /admin/index.html 如果返回200且能看到后台内容 → 存在垂直越权四、常见越权场景汇总
4.1 基于ID的越权(最常见)
# GET请求 GET /user/info?id=1001 GET /order/detail?order_no=2024001 GET /download?file=resume_1.pdf # POST请求(参数在Body) POST /delete_address address_id=1001 # 渗透测试:修改ID值,观察是否返回他人数据4.2 基于角色参数的越权
# 前端传角色 POST /register username=hacker&role=admin # 尝试注册时提权 # 修改角色 POST /update_profile role=admin # 测试方式:抓包看是否有role、group、level等参数,尝试修改4.3 基于未授权功能的越权
# 常见后台路径 /admin /manager /system /administrator /api/admin /console # 常见后台文件 /admin.php /manage.php /sys.php # 测试方式:直接访问这些路径,看是否被拦截4.4 基于UUID/GUID的越权
# 看似不可猜测,但仍可能泄露 https://target.com/share/abc123-def-456 # 如果UUID生成算法可预测或通过其他渠道泄露,仍可越权 # 测试方式:收集所有可见的UUID,尝试替换访问4.5 基于URL参数的越权
# 参数中有用户的标识 GET /profile?user_id=1001 # 参数中有操作的资源标识 POST /delete_post?post_id=2001 # 测试方式:修改user_id、post_id等参数五、越权漏洞检测方法
5.1 手工测试
# 水平越权测试流程 1. 注册账号A、B 2. 登录A,访问所有功能点,记录资源ID 3. 登录B,尝试用A的资源ID替换B的资源ID 4. 观察是否能正常访问/操作 # 垂直越权测试流程 1. 注册普通账号 2. 尝试访问管理员功能路径 3. 尝试修改请求中的权限参数 4. 尝试访问JS中隐藏的管理员API5.2 自动化工具
# Burp Suite Autorize / AuthMatrix - 配置不同角色的Cookie - 批量测试接口的越权情况 # 自定义脚本 import requests # 账号A的Cookie cookies_a = {'session': 'xxx'} # 账号B的Cookie cookies_b = {'session': 'yyy'} # 资源ID(从A获取) resource_id = 1001 # 用B的Cookie请求A的资源 r = requests.get(f'http://target.com/api/order/{resource_id}', cookies=cookies_b) if r.status_code == 200 and 'order data' in r.text: print(f"越权漏洞!账号B可访问账号A的资源: {resource_id}")5.3 代码审计要点
// 危险模式:未校验资源归属 $id = $_GET['id']; $data = $db->query("SELECT * FROM orders WHERE id = $id"); // 正确模式:增加归属校验 $id = $_GET['id']; $user_id = $_SESSION['user_id']; $data = $db->query("SELECT * FROM orders WHERE id = $id AND user_id = $user_id"); // 危险模式:仅校验登录,未校验角色 if(isset($_SESSION['user'])) { ... 管理员操作 ... } // 正确模式:校验角色 if($_SESSION['role'] == 'admin') { ... }六、越权防御措施
6.1 服务端强制校验(最核心)
// 水平越权防御:校验资源归属 <?php $order_id = $_GET['order_id']; $user_id = $_SESSION['user_id']; // SQL中增加 user_id 条件 $sql = "SELECT * FROM orders WHERE id = ? AND user_id = ?"; $stmt = $db->prepare($sql); $stmt->execute([$order_id, $user_id]); if($stmt->rowCount() == 0) { die('无权限访问'); } ?>6.2 使用不可猜测的标识符
// 使用UUID替代自增ID $order_uuid = bin2hex(random_bytes(16)); // 32位随机字符串 // 避免使用可遍历的ID - 不安全:order_id=1001, 1002, 1003 - 较安全:order_uuid=7c9e6679-7425-40de-944b-e07fc1f90ae76.3 统一权限控制(RBAC)
// 基于角色的访问控制 class Auth { public static function check($resource, $action) { $role = $_SESSION['role']; // 权限矩阵 $permissions = [ 'admin' => ['*' => true], 'user' => ['order' => ['view', 'edit'], 'profile' => ['view', 'edit']], 'guest' => ['login' => true] ]; return $permissions[$role][$resource][$action] ?? false; } } // 使用 if(!Auth::check('order', 'view')) { die('无权限'); }6.4 接口统一鉴权
// 中间件模式(以Laravel为例) Route::middleware(['auth', 'can:view,order'])->get('/order/{order}', 'OrderController@show');6.5 最小权限原则
-- 数据库层面:使用视图限制数据行 CREATE VIEW user_orders AS SELECT * FROM orders WHERE user_id = CURRENT_USER_ID();6.6 其他措施
| 措施 | 说明 |
|---|---|
| Token绑定 | 重要操作验证绑定Token |
| 操作日志 | 记录敏感操作,便于溯源 |
| 二次验证 | 修改密码、转账等需二次确认 |
| 防止参数遍历 | ID使用UUID,不暴露自增规律 |
七、实战案例分析
案例1:电商平台水平越权
漏洞描述:用户可查看他人订单详情
攻击步骤:
1. 登录账号A,进入"我的订单" 2. 订单URL:https://shop.com/order.php?id=1001 3. 修改id参数为1002(账号B的订单) 4. 成功看到账号B的订单信息(姓名、电话、地址)修复方案:
// 增加用户ID校验 $order_id = $_GET['id']; $user_id = $_SESSION['user_id']; $sql = "SELECT * FROM orders WHERE id = ? AND user_id = ?";案例2:后台管理垂直越权
漏洞描述:普通用户可访问管理员功能
攻击步骤:
1. 注册普通用户登录 2. 访问 https://admin.shop.com/user_list.php(后台地址) 3. 成功加载页面,显示所有用户信息修复方案:
// 增加角色校验 if($_SESSION['role'] !== 'admin') { header('HTTP/1.1 403 Forbidden'); die('无权限访问'); }案例3:API接口越权
漏洞描述:通过修改请求body中的user_id可修改他人资料
攻击步骤:
POST /api/update_profile Content-Type: application/json {"user_id": 1001, "email": "hacker@evil.com"} # 将 user_id 改为 1002,成功修改他人邮箱修复方案:
// 后端不信任前端传的user_id,使用会话中的user_id $user_id = $_SESSION['user_id']; // 只允许修改当前登录用户的信息八、越权与常见漏洞的区别
| 漏洞 | 区别 |
|---|---|
| 越权 vs 未授权访问 | 越权是已登录但权限超出;未授权是未登录直接访问 |
| 越权 vs IDOR | IDOR是越权的一种,特指通过修改ID参数越权 |
| 越权 vs 权限提升 | 权限提升常指获取更高操作系统权限,越权更多指Web应用逻辑缺陷 |
| 越权 vs CSRF | CSRF是诱骗用户执行操作;越权是直接操作他人的数据 |
九、检测工具与技巧
9.1 Burp Suite 插件
| 插件 | 功能 |
|---|---|
| Autorize | 自动检测越权,配置不同角色Cookie对比响应 |
| AuthMatrix | 矩阵式测试不同角色的API权限 |
9.2 手工测试技巧
# 1. 收集所有ID参数 使用Burp爬虫,提取所有 id、user_id、order_id 等参数 # 2. 测试边界值 尝试 id=0, id=-1, id=1, id=999999, id=1001+1 # 3. 测试未公开的API 查看HTML/JS中隐藏的API路径 # 4. 测试不同角色 注册多个账号:user1、user2、admin # 5. 测试并行会话 同时登录两个账号,互操作对方的资源9.3 Python检测脚本模板
import requests import json class PrivilegeEscalationTester: def __init__(self, target, cookie_user_a, cookie_user_b): self.target = target self.cookie_a = cookie_user_a self.cookie_b = cookie_user_b self.endpoints = [] def add_endpoint(self, method, url, params=None, data=None): self.endpoints.append((method, url, params, data)) def test_horizontal(self): """水平越权测试:用B的Cookie访问A的资源""" for method, url, params, data in self.endpoints: # 先用A获取一个有效资源ID(假设params中有id) resp_a = requests.request(method, self.target + url, params=params, data=data, cookies=self.cookie_a) # 用B访问同一个资源 resp_b = requests.request(method, self.target + url, params=params, data=data, cookies=self.cookie_b) if resp_b.status_code == 200 and resp_b.text != resp_a.text: print(f"[!] 水平越权可能: {url}") print(f" B收到了不同于A的响应内容") def test_vertical(self, admin_cookie): """垂直越权测试:用普通用户Cookie访问管理员接口""" for method, url, params, data in self.endpoints: # 访问管理员接口 resp = requests.request(method, self.target + url, params=params, data=data, cookies=self.cookie_b) if resp.status_code == 200 and "admin" in resp.text.lower(): print(f"[!] 垂直越权可能: {url}") # 使用示例 tester = PrivilegeEscalationTester( target="http://target.com", cookie_user_a={'session': 'aaa'}, cookie_user_b={'session': 'bbb'} ) tester.add_endpoint('GET', '/order/detail', {'id': 1001}) tester.test_horizontal()十、速查表
常见越权参数
| 参数名 | 类型 |
|---|---|
| id, user_id, uid, userId | 用户ID |
| order_id, order_no, oid | 订单ID |
| post_id, article_id, pid | 文章ID |
| file, filename, path | 文件路径 |
| role, group, level, permission | 角色参数 |
防御速查
| 防御方式 | 实现 |
|---|---|
| 归属校验 | SQL中加入WHERE user_id = ? |
| 不可猜测ID | 使用UUID,禁用自增ID |
| 角色校验 | if($_SESSION['role'] == 'admin') |
| 权限矩阵 | RBAC统一鉴权 |
| Token绑定 | 重要操作绑定用户Token |
| 接口鉴权 | 中间件/注解统一处理 |
检测速查
# 水平越权 1. 准备账号A、B 2. 获取A的资源ID 3. 登录B,用A的ID访问 4. 观察能否成功 # 垂直越权 1. 登录普通账号 2. 访问/admin、/manage等路径 3. 查看响应是否返回后台内容 4. 抓包修改role=admin等参数十一、一句话总结
越权漏洞 = 系统只验证了身份,未验证权限边界
核心问题:后端没有校验当前用户是否有权操作目标资源。
防御核心:
水平越权:查询时增加用户ID条件,确保资源归属
垂直越权:建立统一的角色/权限校验机制(RBAC)
通用原则:永远不信任前端传递的权限标识,所有校验必须在服务端完成
记住:用户只能访问自己的数据,只能执行自己角色的操作——这个规则必须在后端代码中明确实现,不能依赖前端判断。