前言
最近在帮合作单位做渗透测试,这是我入行以来第一次参与正式项目。怀着激动而忐忑的心情测了两周,我一共发现了十几个漏洞,其中大部分还都是高危漏洞。终于没有交白卷,忐忑的心情终于放下,现在心里只剩一点小激动,一点成就感。
渗透测试是一个既需要技术,又需要经验的活儿。思来想去,趁现在兴奋劲儿还没过,赶紧做一下总结,毕竟经验是通过实战积累的,而实战之后的总结则是最好的方式。由于在测试中发现的漏洞以“越权”类居多,而且本人的人生中提交的第一个漏洞也是越权,所以这里就先聊聊越权的那些事儿。
什么是越权
越权(或者说权限提升,Privilege Escalation)是指攻击者能够执行他本身没有资格执行的一些操作,属于“访问控制”的问题。用大白话讲,越权就是“超越了你你拥有的权限,干了你本来不可能干的事儿”。先来几个越权的例子:
Winmail普通用户可直接进入后台取得域名管理、用户管理等所有权限
一般情况下,常见的访问控制方式有三种:垂直访问控制、水平访问控制和上下文相关的访问控制。垂直访问控制允许不同类型的用户(常见的有基于角色划分用户类型)访问应用程序的不同功能,例如在某系统中普通用户只能执行有限的操作,管理员则拥有最高权限。水平访问控制允许用户访问一组相同类型的资源,如在一个网银系统中,每个用户只能看到他自己的账户信息,只能操作自己的账户进行转账。而上下文相关的访问控制可确保基于应用程序当前的状态,限制用户仅能访问所允许的内容,如在常见的找回/修改密码功能中,必须通过身份验证才能重新设置密码。许多情况下,垂直访问控制与水平访问控制会相交交叠,配合使用。
如果在一个应用中,用户能够访问他本身无权访问的功能或者资源,就说明该应用存在访问控制缺陷,也就是说存在越权漏洞。与访问控制相对应的,将越权分为垂直越权、水平越权和上下文相关的越权。
垂直越权:如果攻击者能够执行某项功能,而他所属的角色并不具备该权限,这就存在垂直越权漏洞,如上述示例中前两个例子。水平越权:如果攻击者能够执行与自己同级别的其他用户能够执行的操作,这就存在水平越权漏洞,如上述示例中的后两个例子。
上下文相关的越权:如果攻击者能够利用应用程序状态机中的漏洞获得关键资源的访问权限,这就存在上下文相关的越权。如在找回密码过程中,攻击者使用自己的账户信息通过验证,但最终却通过某种手段(例如使用BurpSuite改数据包)将他人的密码进行了修改。上下文相关的越权漏洞一般属于业务逻辑漏洞。
为什么会出现越权
通常情况下,我们使用一个web应用程序提供的功能时,流程是:登录—>提交请求—>验证权限—>数据库查询—>返回结果。如果在“验证权限”环节存在缺陷,那么便会导致越权。一种常见的存在越权的情形是:Web应用程序的开发者安全意识不足,认为通过登录即可验证用户的身份,而对用户登录之后的操作不做进一步的权限验证,进而导致越权问题。
1. 通过隐藏URL实现访问控制
有些应用程序仅通过URL实现访问控制。例如:使用管理员身份登录后可以看到后台管理页面的链接,但是以普通用户登录则看不到该链接。在这种情况下,开发者认为普通用户不知道或者很难猜到后台管理页面的URL,因此实现对管理功能的保护。这其实是一种错误观点,因为攻击者完全有可能通过其他方式(如Google Hacking、HTML/js源码分析、暴力猜解URL、利用其他漏洞等)得到后台管理URL。
2. 直接对象引用(Direct Object reference)
用户提交HTTP请求访问某资源时,被请求资源的标识符常常以GET或者POST请求参数的形式出现在URL查询字符串或者POST请求主体中提交给服务器。例如,在一个网银系统中,用户可以使用以下URL查询账户信息:
https://www.onlinebank.com/viewInfo.php?accountId=12345678
其中accountId是用户自己的账户ID。用户登录自己的账户后,该URL的链接会出现在用户账户页面中,用户点击即可跳转到账户信息页面。虽然其他用户无法看到这个链接,但是如果该网银系统的访问控制不完善,攻击者完全可以通过枚举accountId进而构造出URL,然后越权查看他人的账户信息。
3. 多阶段功能
应用程序的一些功能通过几个阶段执行,并且在执行过程中向服务器依次提交多个请求。这种情况很常见,比如转账功能、找回密码功能等,需要先验证用户的身份,验证通过后才允许用户执行后续动作。多阶段功能本身并没有问题,但是如果开发者认为到达验证过程后续阶段的用户一定已经拥有了相关的权限,并在后续阶段执行操作时不再对用户提交的请求进行验证,那么就很有可能存在越权漏洞。攻击者完全有可能绕过前几阶段的验证阶段,直接执行后续的动作。讲一个我在测试中遇到的真实的案例。
某网站在找回密码时做了很严格的验证,需要验证姓名、手机号、身份证号等信息,验证通过了才能修改密码。那么问题来了,既然做了这么严格的验证,怎么还会存在越权?该网站的“找回密码”功能被设计成了两步(提交了两个请求报文):第一步验证用户身份,这时提交第一个请求报文,验证成功之后,进入第二步;第二步才是真正的修改密码的动作,而修改密码的POST数据包有3个请求参数,分别是新密码、确认新密码以及账号值。问题就出在第二步,在执行修改密码的动作时,服务器并未验证被修改密码的账户是否是第一步中通过身份验证的账户,因此攻击者可以很容易的以自己的身份通过认证,然后修改第二步提交的报文,实现对任意账户的密码修改!
4. 静态文件
有些Web应用程序在用户访问动态页面时会执行相应的访问控制检查,以确定用户是否拥有执行相关操作所需的权限。但是,用户仍然会提交对静态资源的访问请求,如下载网站中的word、excel、pdf文档等。这些文档都是完全静态的资源,其内容直接由Web服务器返回,它并不在服务器上运行。因此,静态资源自身并不能执行任何检查以确认用户的访问权限。如果这些静态资源没有得到有效的保护,那么任何知晓URL命名规则的人都可以越权访问这些静态资源。这种情况的越权也很常见,而且即使不知道URL命名规则,完全有可能通过Google hacking搜索到敏感文件。
5. 平台配置错误
一些应用程序通过在Web服务器或应用程序平台层使用控件来控制对特定URL路径的访问。例如,有些应用程序会根据用户角色来限制对管理后台路径(如/admin)的访问,如果用户不属于“管理员”组,则拒绝其访问后台管理页面的请求。但是,如果在配置平台及控件时出现错误,就可能导致越权访问。
越权漏洞怎么挖
首先,找出疑似存在越权漏洞的请求。存在越权漏洞的请求有一定的特点,可以从两个方面下手。
从HTTP请求上来说,就是通过BurpSuite抓取在目标站点正常操作所产生的请求数据包,然后找出可能产生越权的请求。一般情况下,类似上文所述URL的GET或者POST请求都要列为重点怀疑对象:https://www.onlinebank.com/viewInfo.php?accountId=12345678
从业务功能上来说,找到容易产生越权的功能点。常见的越权高发功能点有:根据订单号查订单、根据用户ID查看帐户信息、修改/找回密码等。
确定了怀疑对象之后,还需要进一步的分析,以确定是否真的存在越权。这时,需要两个不同的账号(下文分别称为账号A和账号B),以便测试登录账号A后进行操作能否影响到账号B。注意在浏览器中测试时,需要使用两个浏览器分别登录不同的账号,或者使用浏览器的隐私浏览功能登录其中一个账号。
以上文提到的URL为例进行说明。假设账号A的id为1234,账号B的id为5678,BurpSuite中抓取的数据包都是在账号A的。我的习惯做法是:在BurpSuite中将该请求发送到Repeater,然后将参数id的值修改为5678,最后提交修改后的请求包;服务器返回响应后,可以看一下BurpSuite中收到的响应报文。如果响应报文直接提示错误之类的,基本上可以确定此处不存在越权;如果响应报文提示操作成功,此时应该使用浏览器登录账号B进行二次确认,如果数据确实有相应的改动,那么则说明这里存在越权漏洞。这里需要注意:BurpSuite中报文提示操作成功有可能是误报,必须在浏览器中进行再次确认。而对于查询订单这类的请求来说,情况更简单了,修改参数id提交请求,如果返回了账号B的数据,那么就可以确定存在越权。
当然,越权漏洞的攻击点不仅仅存在于请求参数中,还有可能在Cookie中。因此,需要具体情况具体分析,复杂一点的可能还需要Cookie值配合请求参数值以实现越权攻击。
总结
实现应用程序的完善的访问控制不是件容易的事,因此越权漏洞防不胜防。对于开发者而言,一定要有安全意识,时刻保持警惕。以下是几点建议:
永远不要相信来自客户端(用户)的输入!
执行关键操作前必须验证用户身份,多阶段功能的每一步都要验证用户身份。
对于直接对象引用,加密资源ID,以防止攻击者对ID进行枚举。
在前端实现的验证并不可靠,前端可以验证用户的输入是否合规,在服务器端验证用户权限。
参考文献
Direct Object References and Horizontal Privilege Escalation
本文参考转载至文章聊聊越权那些事儿