互联网在深度改造我们生活方式的同时,也不断给各行业的服务交付模式带来巨大变化,过去需要到专门机构或营业场所办理的业务,如今只要通过电脑浏览器或手机APP就能完成了,我们称之为用户体验前置。随之客户端的能力越来越强,客户端安全漏洞的威胁自然越来越大,在软件测试过程中对客户端安全的检测也更加重要,今天就让我们来聊聊常见的绕过客户端控件的方法,以及如何有效封堵这些客户端安全漏洞。
说客户端漏洞之前,先来了解下客户端和服务端的区别,一般来说客户端就是我们使用的电脑、手机,也就是我们通过IE,火狐等浏览器或APP向服务器端发出请求并展示服务端响应的所在;服务器端就是存放应用、文件、数据库数据,并提供响应计算服务的服务器。 应用程序依靠客户端控件限制用户,其表现无非是通过客户端组件,使用某种它认为可以防止用户修改的机制传送数据,或在客户端执行保护措施控制用户与客户端的交互,从而对功能实现限制。 绕过客户端控件的几种方法
客户端控件一般可被攻击的包括HTML验证、隐藏表单字段、Http cookie、URL参数、模糊数据几个方面,而绕过客户端控件攻击通常有两类测试方法:
A、使用浏览器自带调试工具完成,对于某些简单的、可见的客户端控件验证, 大多数可以使用该方法;
B、使用Burp拦截服务器,对于较复杂、不可见的如cookie、模糊数据等,使用拦截服务器拦截并做下一步处理。如抓取request、修改request的目的是企图欺骗服务器,从而使后端服务器、数据库、文件服务器受到伤害;或修改response,目的是为了欺骗前端服务器。所有的攻击者都是为了让后端受到伤害,或第三方受到伤害,所以修改response的比较少。
接下来我们详细说说关于绕过客户端控件的方法。
1、绕过客户端控件-HTML验证
HTML验证不应该被认为是一种安全的方法验证参数,这种验证方式只能帮那些不知道所需输入的用户缩短服务器处理时间,攻击者可以用各种方法轻易的绕过这种机制。任何客户端验证都应该复制到服务器端,这将大大减少不安全的参数在应用程序中使用的可能性。 HTML验证包括两部分:HTML表单内置验证和基于脚本(js)的验证,例如图1中的应用中的用户名长度,maxlength=“”,浏览器将阻止用户输入超过多少字符值;
若该应用只在前端通过HTML表单内置验证方式对用户名长度进行验证,没有在后端做验证,通过拦截提交表单的请求并输入任意值即可跳过,因为HTML表单内置的输入确认机制极其简单也不够详细,不足以对各种输入执行验证。所以,开发者通常在脚本中加入js验证,例如这样一段代码:
很多基于HTML验证的客户端控件攻击可以直接采用浏览器调试工具来进行。仍以图1为例,直接通过浏览器调试工具,删除maxlength=“”属性,此时在用户名栏便可以输入任意长度的字符,并能成功注册。
JS里边可能调用ajax去后端执行了一些验证或者JS里写死了一些参数验证 ,这里再演示一个JS验证的例子,如图4,有一个表单中的每一个输入框的验证方式都不同,要求输入的内容格式也不同,我们可以通过开发者工具看到该表单是通过js验证方式,代码中首先通过正则表达式的方式规定好每个输入框中输入字符的格式,用户点击提交按钮后,先获取每个输入框的value并与事先定义好的正则比较,并且用一个err字符来作为标记,初始err=0,如果有不符合要求的则把err加1,最后代码中会判断,如果err大于0,说明有表单中的value不符合要求,则弹出一个错误信息提示框,若果err小于等于0,则表单才会提交成功,当我们知道这个验证过程之后,那么我们可以在这个方法中加个断点,每个输入框中都输入不符合要求的字符,点击submit后并拦截该请求:
当代码运行到if(err>0) alert(msg);这行时,由于我们收入的都是不符合要求的字符,自然err此时大于0,但是我们在Console中修改下err值为0,点击下一步后观察:
此时光标移到else这行,准备执行else中的内容,我们成功跳过了前端js中的验证!
2、绕过客户端控件-隐藏表单字段
隐藏的HTML表单是一种看上去无法修改,通过客户端传送数据的常用机制。如果一个表单字段标记为hidden或者readonly那么它就无法编辑,如果是完全隐藏( display:none )则不会在屏幕上显示,但是提交时保存在表单中的字段名称和值仍然被送交给应用程序。
如图7里边价格的表单字段名为price,标记为hidden。虽然 price字段并不可以编辑,但应用程序使浏览器隐藏了该字段,我们就可以通过拦截代理服务器(burp suite)拦截立刻购买请求,直接改变这个price值,然后使用更便宜的价格成功购买,如图8、图9:
(图8)
(图9)
3、绕过客户端控件-HTTP COOKIE
与隐藏表单类似,http cookie并不显示在用户屏幕上,也不可直接修改。而有些网站对于不同的会员等级会有不同的折扣,判断是否享用折扣就用cookie来传达。如有些电商最早对金牌会员的折扣就是用 cookie传达,类似在用户登录后返回一个响应:
HTTP/1.1 200 OK Set-Cookie:DiscountAgreed=20 …
这样我们通过拦截发现了该cookie值并可在购买时对其进行修改从而拿到了更低廉的折扣,如:
POST /shop/1.html HTTP/1.1 Host: 127.0.0.1 Cookie: DiscountAgreed=20 quantity=1&price=449
而修改这种cookie的方式有两种,一种是拦截Post请求并修改cookie,另一种是拦截response,修改set-cookie的值,总之不管你用哪种方式都可以,按个人喜好啦。
4、绕过客户端控件-URL 参数
应用程序有可能会使用预先设定好的URL参数通过客户端传递数据,如:
http://127.0.0.1/shop/1.html?quantity=1&price=449
当然这个url不一定直接显示在浏览器地址栏中,也可能通过包含参数的url加载框架内容或用弹窗等其他方法隐藏地址栏,这时仍可以用拦截代理服务器去捕获任何一个不规范的url参数,方法与以上类似,直接修改价格然后提交请求。
5、绕过客户端控件-模糊数据
有时候通过客户端传送的数据是经过加密或某种形式的模糊处理,并不以明文显示。如通过拦截代理服务器得到“D61E4BBD6393C9111E6526EA173A7 C8B”这样一组模糊数据,有几种方法可以实施攻击:
1)破解:看是否是utf-8、base32、MD5等加密,通过decode或彩虹表判断,成功破解后修改值进行攻击。
2)如果完全无法理解仍可以重新传送他的值,如抓取另一款较便宜的产品的price进行替换,无视其模糊处理。
两个真实案例
到此,大家对绕过客户端控件的常用方法都有了了解,再跟大家聊两个真实案例,先说Discuz论坛附件下载权限绕过的案例吧,如图10:
(图10)
Discuz的附件下载地址是类似forum.php?mod=attachment&aid=Nzg4fDQwNG
QzYjMxfDEzODk2OTM4Mzh8MzEyNTR8MjY3M w%3D%3D这样的,其中的aid参数是一串base64,解码后是:788|404d3b31|1389693838|31254|2673,这串aid在
source/module/forum/forum_attachment.php 中被这样解析:
@list($_GET[’aid’], $_GET[’k’], $_GET[’t’], $_GET[’uid’], $_GET[’tableid’]) =
daddslashes(explode(’|’, base64_decode($_GET[’aid’])));
之前提交的 788|404d3b31|1389693838|31254|2673 中的 31254 就是登录用户的UID,它被赋值进了 $_GET[’uid’] 变量。紧接着:
……if($_GET['uid'] != $_G['uid'] && $_GET['uid']) { $_G['uid'] = $_GET['uid'] = intval($_GET['uid']); ......
如果用户提交的uid和当前登录的uid不同,DZ就会将用户所提交的uid赋值给discuz的全局使用变量$_G,注意这儿还有代码逻辑问题,就是说下载的时候如果传过来的uid跟登录着的uid相同,那么就以当前登录者的权限去下载,如果不同就按$_G中用户权限去下载,也就是说用户可以在下载附件时任意“伪造”自己的身份。修改其中的uid部分(这里将uid改成了管理员账户:1),
788|404d3b31|1389693838|1|2673编码修改后的aid:
forum.php?mod=attachment&aid=Nzg4fDQwNGQzYjMxfDEzODk2OTM4Mzh8MxwyNjcz
此时就能成功下载原本需要高权限的附件了。
再说一个绕过支付的案例,这个是XX演艺海棠秀(XX大剧院在线购票)系统,首先随便注册了个号, 用户名:1xxxxxxxxxx,然后登录进去购票,选择时间、点击【选坐购票】、点击【订票】,最后来到信息 确认页面,点击网上支付并且抓包、确定。
将图12中最后一个参数值改成00(状态码)然后直接跳过了支付宝支付环节,给出了取票密码。 如何封堵绕过客户端控件的攻击
在我们日常生活中还有许许多多类似的例子,大家可以自己百度。那么对于这种绕过客户端控件的攻击应该怎么处理呢?
首先,尽量不要使用客户端传送关键数据,如价格等信息完全可以保存在数据库中,必要时通过服务器逻辑引用;
第二,如果不得不通过客户端传递,尽量对数据进行加密,并且是包含足够的上下文加密,以防止攻击者使用其他的加密数据替换;
第三,要避免单一js验证方式,添加后台验证;
最后,加强日志和警报机制。
说了这么多别人家的例子,最后来说说云智慧的透视宝,以透视宝注册功能为例,所有验证方式都是在后端代码里处理的,所以不用担心会有客户端控件绕过漏洞。
那么什么情况下要进行绕过漏洞检测呢?
1)有验证码的地方;
2)有校验的地方,只要是得到了与前端校验不符的结果都可以算作安全漏洞;
3)跟钱相关的地方(金额、数量、优惠、支付……);
4)隐藏域 (readonly、hidden、display、none) 只要是在页面无法修改或没显示,在请求里显示了的(注: 特殊情况,页面上是readonly、hidden,请求里也不存在)尝试传输此值,看是否能被后端接受;
5)response (如支付失败);
6)可能参与业务逻辑的cookie;
7)有模糊数据的地方;
好了,关于绕过客户端控件暂时先说到这里,大家可以自己找些例子试试,其实关于Web应用程序的安全问题,只要我们坚持一句话“任何输入均不可信!”即可。