在我看来,越权漏洞的成因主要是因为开发过程中在对数据进行增、删、改、查时对客户端请求的数据没有进行严格的权限判定,导致单个用户可以操作其他用户的一些操作,叫做越权。
越权分为两种:一种为水平越权,一种为垂直越权。
水平越权:指攻击者尝试访问与他拥有相同权限的用户资源。例如有一个写作网站,作者(A)登录后可以对自己的文章进行发布、查看、删除等操作。当删除一篇文章时,发送的请求 URL 如下:
http://www.xxxx.com/article.php?action=delete&id=1
action 参数是要执行的动作 delete 删除,id 为文章 id 号。当 A 用户想恶意攻击时,将 id 号改为了 2,发送了如下 URL:
http://www.xxxx.com/article.php?action=delete&id=2
因为 id 为 2 的文章不属于 A 用户,A 将 id 改为 2 删除成功,此时程序没有对请求进行相关的权限判断,导致任何人可操作,则为水平越权。
垂直越权:垂直越权可以分为两种,分别是向上越权和向下越权。向上指一个低级别攻击者尝试访问高级别用户的资源,向下指一个高级别用户尝试访问低级别用户的资源。例如一个用户的个人信息管理页是 user.php,而管理员管理所有用户信息的页面是 manageuser.php, 但管理页面没有相关的权限验证,导致任何人输入管理页面地址都可以访问,则导致了垂直越权中的向上越权。向下越权相反。
通常情况下,一个 Web 程序功能流程是登录 - 提交请求 - 验证权限 - 数据库查询 - 返回结果。如果验证权限不足,便会导致越权。常见的程序都会认为通过登录后即可验证用户的身份,从而不会做下一步验证,最后导致越权。
1,通过隐藏 URL 实现控制访问
有些程序的管理员的管理页面只有管理员才显示,普通用户看不到,利用 URL 实现访问控制,但 URL 泄露或被恶意攻击者猜到后,这会导致越权攻击。
2,直接对象引用
这种通过修改一下参数就可以产生水平越权,例如查看用户信息页面 URL 后加上自己的 id 便可查看,当修改为他人的 ID 号时会返回他人的信息,便产生了水平越权。
3,多阶段功能
多阶段功能是一个功能有多个阶段的实现。例如修改密码,可能第一步是验证用户身份信息,号码验证码类的。当验证成功后,跳到第二步,输入新密码,很多程序会在这一步不再验证用户身份,导致恶意攻击者抓包直接修改参数值,导致可修改任意用户密码。
4, 静态文件
很多网站的下载功能,一些被下载的静态文件,例如 pdf、word、xls 等,可能只有付费用户或会员可下载,但当这些文件的 URL 地址泄露后,导致任何人可下载,如果知道 URL 命名规则,则会便利服务器的收费文档进行批量下载。
5,平台配置错误
一些程序会通过控件来限制用户的访问,例如后台地址,普通用户不属于管理员组,则不能访问。但当配置平台或配置控件错误时,就会出现越权访问。
对于渗透测试,可以对一些请求进行抓包操作,或者查看请求的 URL 地址,对于关键的参数修改下值查看下返回结果来初步判定。随后可以注册两个小号,相互辅助来确定是否存在越权。
常见的越权高发功能点有:根据订单号查订单、根据用户 ID 查看帐户信息、修改 / 找回密码等。
对于代码审计,可以先查看前端的网页源码,查看一些操作的表单提交的值。查看配置文件和一些过滤器,看是否对 URL 有相关的筛选操作。最后查看后台处理逻辑,是否存在身份验证机制,逻辑是否异常,有时的逻辑漏洞也可导致越权操作。
一般测试时,可以注册两个小号测试,更方便,大概流程图如下:
水平越权,例如一个网站的修改个人信息页面,用户提交表单时程序从表单中获取 userid 的值,然后发送给后台执行修改操作,前端代码如下:
<form method="post" action="/edituserinfo">
<input type="hidden" name="userid" value="<%=res.getString("userid")>" />
用户名:<%=res.getString("username")>
密 码:<input type="password" name="password"/>
<input type="submit" value="修改"/>
</form>
表单的用户 id 做为隐藏字段,在用户修改信息时一起发送到后台处理,后台代码如下:
int userid=Integer.valueOf( request.getParameter("userid"));
String pass=request.getParameter("pass");
JdbcConnection conn = null;
try {
conn = new JdbcConnection();
Object[] params = new Object[2];
params[0] = pass;
params[1] = userid;
final String sql = "update user set password=? where userid=?";
conn.execUpdate(sql,params);
conn.closeConn();
这样的代码安全性就很低,恶意用户在前台通过抓包便可以随意修改用户 id,导致修改任意用户的密码,可定义为水平越权。
垂直越权,也可以叫做权限提升攻击,原理是由于 Web 应用没有做权限控制,或只是在菜单做了权限控制,导致恶意攻击者只要猜到管理员的 URL,就可以访问和控制其他角色,达到权限提升目的,例如以下的菜单控制代码:
<tr>
<td><a href="/userinfo.jsp">管理个人信息</a></td>
</tr>
<%if (power.indexOf("administrators")>-1){%>
<tr>
<td><a href="/manageuser.jsp">管理所有用户</a></td>
</tr>
<%}%>
1,类似于这种请求,例如查看用户信息等,不能只根据用户 id 去搜索,应该再次进行身份验证。
2,可以从用户的加密认证 cookie 中获取当前用户 id,防止攻击者对其修改。或在 session、cookie 中加入不可预测、不可猜解的 user 信息。
3,在每个页面加载前进行权限认证。
4,特别敏感操作可以让用户再次输入密码或其他的验证信息。