1. 漏洞概述
越权漏洞(Over Permission)是指服务器端在处理客户端请求时,没有对发起请求的用户进行充分的权限控制,导致用户能够执行其本不应该拥有的操作或者访问本不属于该用户的数据。
越权通常分为两种:
- 水平越权 :攻击者能够访问或操作与自己拥有 同等权限 的其他用户的数据(例如:普通用户 A 查看到普通用户 B 的个人信息)。
- 垂直越权 :攻击者能够访问或操作 更高权限 用户的数据或功能(例如:普通用户执行了管理员才能执行的后台操作)。
本次挑战的是 水平越权 关卡。正如漏洞的核心原理:后台校验时没有严格绑定用户的登录态(Session),而是过度信任了客户端传入的参数。
2. 闯关实操 (Exploitation)
第一步:正常登录并观察
打开关卡页面,首先映入眼帘的是一个登录框。我们使用已知的测试账号密码进行登录:
- 账号:
vince - 密码:
123456
登录成功后,页面显示“欢迎来到个人信息中心”。点击“点击查看个人信息”按钮,页面正常展示了 vince 的个人信息(如手机、住址 chain、邮箱等)。

第二步:抓包分析
在点击“查看个人信息”时,我们使用 Burp Suite 拦截请求数据包,内容如下:
GET /vul/overpermission/op1/op1_mem.php?username=vince&submit=%E7%82%B9%E5%87%BB%E6%9F%A5%E7%9C%8B%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF HTTP/1.1
Host: localhost:8899
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0
Cookie: BEEFHOOK=5Il...; PHPSESSID=9vqh97fqumrrttvp3cjopp8eh1
Connection: close
观察发现,URL 中通过 GET 方式传递了一个核心参数 username=vince。这立刻引起了警觉:服务器是根据当前 Session 里的用户信息来查询,还是直接根据这个 GET 参数来查询的呢?
第三步:篡改参数,实现水平越权
为了验证我们的猜想,我们直接在浏览器 URL 或拦截的数据包中,将 username=vince 修改为另一个已知存在的用户名 username=kobe。
发送请求后,页面结果如预期一样发生了变化!
我们成功在 vince 的登录状态下,查看到到了 kobe 的敏感信息(性别: boy, 手机: 15988767673, 住址: nba lakes)。
这标志着 水平越权攻击成功。

3. 源码分析 (Code Review)
截取核心的查询逻辑代码如下:
if(isset($_GET['submit']) && $_GET['username']!=null){
// 没有使用 session 来校验, 而是使用的传进来的值,权限校验出现问题, 这里应该跟登录态关系进行绑定
$username=escape($link, $_GET['username']);
$query="select * from member where username='$username'";
$result=execute($link, $query);
if(mysqli_num_rows($result)==1){$data=mysqli_fetch_assoc($result);
$uname=$data['username'];
$sex=$data['sex'];
// ... 省略部分信息拼接代码
漏洞成因分析:
- 虽然页面顶部有
check_op_login($link)检查了用户是否登录,但这仅仅是一个“门禁”,并没有做细粒度的“房间钥匙”校验。 - 在查询个人信息时,程序 直接接收了前端传入的
$_GET['username'],将其带入 SQL 语句select * from member where username='$username'中执行查询。 - 缺少身份一致性校验:后端完全没有去验证传入的
$username是否等于当前 Session 中保存的登录用户名($_SESSION['username'])。
总结我的理解:后台校验时没有严格按照用户真实的登录状态(Session)来进行权限控制,而是过于信任并直接通过客户端数据包传入的值进行数据库查询。攻击者只要伪造这个参数,就能轻易突破权限边界,获取他人信息。
4. 修复建议
针对此类越权漏洞,最有效的修复方式是 不可信任客户端提交的与权限 / 身份相关的参数。
应该将上述查询逻辑改为从服务端的 Session 中获取当前用户的身份标识:
// 修复示例:使用 Session 中的 username 替代 GET 传参
if(isset($_GET['submit'])){
// 从服务端的 Session 中获取当前登录的用户名,防止被篡改
$username = $_SESSION['op']['username'];
$username = escape($link, $username);
$query="select * from member where username='$username'";
$result=execute($link, $query);
// ...
}
并且在前端 HTML 中,无需再通过 <input type="hidden" name="username" ...> 将用户名暴露在表单中进行提交。