目录
一、水平越权
二、账户功能探测
1、登录账号lucy
2、登录账号lili
3、登录账号kobe
三、源码分析
四、渗透实战
1、登录lucy账号
2、越权访问lili账户资料
3、越权访问kobe账户资料
本系列为《pikachu靶场通关笔记》渗透实战,本文通过对越权关卡源码的代码审计找到产生缺陷的真实原因,讲解越权关卡的原理并进行渗透实践。如下图所示,pikcahu靶场在越权关卡介绍的过程中讲解越权的产生原因:如果使用A用户的权限去操作B用户的数据,A的权限小于B的权限,如果能够成功操作,则称之为越权操作。 越权形成的原因是后台使用了不合理的权限校验规则导致的,本文专门讲解越权之水平越权部分的渗透实战。
越权分为两个关卡,分别是水平越权和垂直越权,两者区别如下所示。
对比项目 | 水平越权 | 垂直越权 |
---|---|---|
定义 | 相同权限等级用户间,非法访问或操作其他用户资源、数据,如同为普通用户,一方访问另一方信息。 | 不同权限等级用户间,低权限用户非法访问高权限用户资源数据,或高权限用户访问不应访问的更高层级资源,如普通用户获取管理员权限。 |
一、水平越权
水平越权是指同一权限级别的用户之间,通过非法手段访问或操作其他用户的资源或数据,而这些资源或数据本应是相互隔离、仅能由各自的所有者进行访问和操作的。例如,在一个在线银行系统中,两个普通用户 A 和 B,用户 A 通过某种方式获取了用户 B 的账户信息,并能够对其进行操作,这就发生了水平越权访问。
类别 | 详情 |
---|---|
定义 | 同一权限级别的用户,能非法访问、操作其他用户的资源或数据,这些资源本应仅由各自所有者访问操作。 |
产生原因 | 1. 参数可预测:应用用可预测参数(如连续数字 ID)标识用户资源,攻击者可猜测、枚举修改参数访问他人资源; 2. 访问控制不足:仅依赖登录状态或简单权限验证,未严格检查资源所有者,致攻击者可越权操作 |
危害 | 1. 隐私泄露:攻击者获取他人敏感信息,侵犯用户隐私; 2. 数据篡改:修改他人数据,致数据不一致,影响业务运行; 3. 身份冒用:冒用他人身份操作,给被冒用者带来麻烦损失。 |
防范措施 | 1. 强化访问控制:关键操作严格验证,不仅验登录和权限,还查资源所有者与用户是否一致; 2. 参数验证过滤:严格验证、过滤用户输入参数,用白名单、类型检查、长度限制等确保参数安全; 3. 使用不可预测标识:用随机 UUID 等不可预测标识符标识资源,增加攻击者猜测难度; 4. 日志监控:记录用户操作日志,监控分析,及时发现异常访问并处理。 |
二、账户功能探测
1、登录账号lucy
进入pikachu靶场越权之水平越权关卡,登录普通账号lucy(lucy/123456),密码根据页面右上角的提示“lucy/123456,lili/123456,kobe/123456”获取,这个关卡的三个账号都是普通用户,没有管理员,具体如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_login.php#
使用lucy账号登录后,进入了个人中心页面,有一个“点击查看个人信息”的页面。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php
点击查看个人信息,进入到如下页面,可以查看lucy个人信息,此时完整URL地址如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lucy&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#
此时注意到页面右上角的提示信息“这里可以查别人的信息吗?”,然而在lucy登录后的页面中,我们没有其他链接地址可以查到其他人的信息。
2、登录账号lili
接下来我们验证下其他的账号是否有查看他人信息的方法,此时退出lucy账号,回到水平越权的登录页面,登录账号lili(lili/123456),和lucy用户一样,当我们点击查看个人信息后,现实的只有lili账号的信息,完整的URL地址与页面效果如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lili&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
这里注意,本步骤2.2相对于2.1步骤lucy的查看信息仅username不同,由lucy变为lili。
3、登录账号kobe
接下来我们验证下其他的账号是否有查看他人信息的方法,此时退出lili账号,回到水平越权的登录页面,登录账号kobe(kobe/123456),和lucy和lili用户一样,当我们点击查看个人信息后,现实的只有lili账号的信息,完整的URL地址与页面效果如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=kobe&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
这里注意,本步骤2.3相对于2.1、2.2步骤lucy和lili的查看信息仅username不同,用户名变为lili。
三、源码分析
接下来分析普通用户查看资料的页面源码op1_mem.php文件,其URL如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php
打开op1_mem.php文件,这段代码的主要功能是实现一个用户信息查询和退出登录的功能,对其源码进行详细注释,具体如下所示。
<?php
// 调用 connect 函数,此函数可能用于建立与数据库的连接,将返回的连接对象赋值给变量 $link
$link = connect();// 调用 check_op_login 函数,传入数据库连接对象 $link,用于检查用户是否已登录
// 如果用户未登录,即 check_op_login 函数返回 false
if (!check_op_login($link)) {// 使用 header 函数将用户重定向到登录页面 op1_login.phpheader("location:op1_login.php");
}// 初始化一个空字符串变量 $html,用于存储后续要输出的 HTML 内容
$html = '';// 检查是否通过 GET 方式提交了名为 submit 的参数,并且名为 username 的参数不为空
if (isset($_GET['submit']) && $_GET['username'] != null) {// 调用 escape 函数,传入数据库连接对象 $link 和 $_GET['username']// 该函数可能用于对用户输入的用户名进行转义处理,防止 SQL 注入攻击$username = escape($link, $_GET['username']);// 构建一个 SQL 查询语句,用于从 member 表中查询用户名等于 $username 的所有记录$query = "select * from member where username='$username'";// 调用 execute 函数,传入数据库连接对象 $link 和 SQL 查询语句 $query// 该函数可能用于执行 SQL 查询,并返回查询结果$result = execute($link, $query);// 调用 mysqli_num_rows 函数,传入查询结果 $result,用于获取查询结果中的记录数量// 如果记录数量为 1,说明找到了对应的用户记录if (mysqli_num_rows($result) == 1) {// 调用 mysqli_fetch_assoc 函数,传入查询结果 $result,将查询结果的第一行记录以关联数组的形式返回$data = mysqli_fetch_assoc($result);// 从关联数组 $data 中提取用户名、性别、电话号码、地址和邮箱信息,并分别赋值给对应的变量$uname = $data['username'];$sex = $data['sex'];$phonenum = $data['phonenum'];$add = $data['address'];$email = $data['email'];// 使用 heredoc 语法将用户信息拼接成 HTML 代码,并追加到变量 $html 中$html .= <<<A
<div id="per_info"><h1 class="per_title">hello,{$uname},你的具体信息如下:</h1><p class="per_name">姓名:{$uname}</p><p class="per_sex">性别:{$sex}</p><p class="per_phone">手机:{$phonenum}</p> <p class="per_add">住址:{$add}</p> <p class="per_email">邮箱:{$email}</p>
</div>
A;}
}// 检查是否通过 GET 方式提交了名为 logout 的参数,并且其值等于 1
if (isset($_GET['logout']) && $_GET['logout'] == 1) {// 调用 session_unset 函数,用于释放当前会话中所有已注册的变量session_unset();// 调用 session_destroy 函数,用于销毁当前会话session_destroy();// 调用 setcookie 函数,将会话的 cookie 过期时间设置为当前时间减去 3600 秒,即让该 cookie 立即过期setcookie(session_name(), '', time() - 3600, '/');// 使用 header 函数将用户重定向到登录页面 op1_login.phpheader("location:op1_login.php");
}
?>
代码的主要功能流程如下所示。
- 建立与数据库的连接。
- 检查用户是否已登录,如果未登录则重定向到登录页面。
- 当用户通过 GET 请求提交 submit 参数和 username 参数时,从数据库中查询该用户名对应的用户信息。若找到匹配记录,则将用户信息以 HTML 格式展示出来。
- 当用户通过 GET 请求提交 logout 参数且其值为 1 时,销毁当前会话,清除会话 cookie,并将用户重定向到登录页面。
然而本关卡的代码存在水平越权安全风险,主要原因在于权限校验机制存在问题。代码仅对用户的登录状态进行了检查,而在查询用户信息时,没有将查询操作与当前登录用户进行绑定,而是直接使用了用户通过 GET 请求传入的 username 参数来查询数据库。这就意味着,只要用户处于登录状态,就可以通过修改 URL 中的 username 参数,查询到任意其他用户的信息,从而导致同一权限级别的用户之间出现水平越权访问的情况。例如,用户 A 登录后,将 URL 中的 username 参数修改为用户 B 的用户名,就可以获取到用户 B 的个人信息。
四、渗透实战
1、登录lucy账号
登录lucy账户并查看个人信息,进入如下页面,完整URL如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lucy&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#
2、越权访问lili账户资料
将第1步骤中URL地址中的username中的lucy替换为lili,如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lili&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
访问URL后进入到了lili账号的个人信息查看页面,具体如下所示。
3、越权访问kobe账户资料
将第1步骤中URL地址中的username中的lucy替换为kobe,水平越权渗透成功,如下所示。
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=kobe&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
访问URL后进入到了kobe账号的个人信息查看页面,水平越权渗透成功,具体如下所示。