关于安全领域内漏洞的发现,技术手段非常多,工具也非常多,大致阶段可分为事前、事中、事后来处理。事前大多采用SDL、白盒扫描等;事中、事后有NIDS及漏洞感知,甚至还有WAF来拦截恶意流量等。本文作者主要想尝试通过流量扫描的方式,去发现更多的潜在漏洞。
本文将围绕“权限问题”这类漏洞展开讨论,因为权限一旦出问题,很可能导致大规模的敏感信息泄漏,这样的后果可能比一两个跨站要严重的多。权限问题是每个公司非常重要且很难处理好的一种漏洞,这类漏洞是和业务相关性很强的一种漏洞。对安全团队来说,如果对业务不是足够了解,就无法对权限问题有一个很好的治理直到问题收敛。所以本文针对这些困难和问题,尝试去循序渐进地解决互联网公司权限问题收敛,也不可能做到100%的检测率,权当抛砖引玉。
权限问题可分为以下几类:
1、未授权访问问题。
2、水平权限问题。
3、垂直权限问题。
主要是对这三类进行扫描和检测。
我们将把整个过程分为三个阶段:
二、漏洞检测1、数据清洗,清洗出需要的URL,并通过模型过滤那些不需要检测的URL。
2、扫描,对这些重点URL进行扫描尝试是否存在越权访问的行为。
3、运营阶段,通过运营进一步去除误报的URL、确认漏洞的危害、是否有进一步利用的可能、以及其他相关接口是否还存在相同的漏洞,用来反哺修正扫描器。
权限问题,顾名思义就是因为对用户权限控制不当导致的问题。为了便于检测可以把它分为二个问题:1、未授权的问题。2、有授权的问题(水平、垂直)。其中对于用户的 *** 作又可分为增、删、改、查,4个 *** 作的识别。
先看下技术架构:
技术架构
整个系统分为四层:
0x01、收集流量流量清洗层:互联网公司每日的流量高达几百亿条,我们不能对全部流量进行检测,也没必要。所以需要清洗出可能存在该类问题的URL并且去重,做到精准定位,这样可以节约大量时间用于检测。
模型层:模型层主要过滤那些无法通过规则简单过滤的干扰流量。
扫描层:扫描层通过模型输出的流量逐个进行扫描,并且检测是否存在漏洞。
运营层:最后一层主要是安全运营,逐个查看被扫描器有可能存在的漏洞URL,并且可以去反推整个系统是否还有其他接口存在此类漏洞用于反哺扫描器。
互联网公司的每日流量几乎都是海量数据,对每个流量都进行检测速度太慢,也没必要,并且全量的数据回放会混着非常多的干扰数据,这种数据本身就不需要做权限控制,或本身就不存在权限问题。这归结于敏感信息的识别,如果这部分内容属于某一个人或某一个群体,被群体之外的人访问了或编辑了,那就是有问题的,所以为了降低后续误报带来的影响和运营困难,我们前期先要对流量进行筛选,把那些重要的流量清洗出来再进行扫描。这样做的优点很明显,就是有的放矢;而缺点也很明显,如果数据选择面太窄就会有遗漏。所以在做数据收集时一定要根据业务不断的迭代,增加敏感数据的维度。
1、流量清洗
流量清洗的主要目标是清洗出具备返回敏感信息的API用于后续的检测,当前清洗出了我们比较关心的敏感信息,包含但不限于手机号、***、***、邮箱、组织架构、订单、密码等含有敏感数据的URL作为检测目标。
清洗逻辑这里尽量多用UDF来判断,具体逻辑就不再这里赘述了,UDF函数如下:
代码块
SQL
get_phone_number(response) as phone_num, get_id_card(response) as id_card, get_bank_card(response) as bank_card, get_email(response) as email, get_mark_number(response) as mark_number,
但是清洗出来的敏感信息还需要做第一次误报处理,例如提取出的手机号是包含在一串字符串中的,
样例1:19f3f34d44c135645909580e99ac
我们需要通过前后字符及上下文来判断,这个是属于真实的手机号、***等敏感信息,还是某一个字符串里面的某一部分,如果是截断的字符串那就要作为非手机号过滤掉。
2、归一化并采样
由于流量数据非常大,每日几百亿的URL并且绝大部分都是重复的,没必要做重复的扫描和检测,所以这里需要做2件事:1、归一化。2、采样。
首先需要做的是归一化。
归一化:归一化的目的是为了合并同类URL做更好的采样收录。URL一般的构成形式如下:
代码块
HTTP
https://www.x.com/abc/cdef?name=ali&id=1#top
其中:https - PROTOCOL ,www.x.com - DOMAIN,/abc/cdef - PATH,name=ali&id=1 - PARAM,#top - FRAGMENT
但绝大部分公司内,很多URL的PATH部分不会这么规律,而是采取随机字符串的方式。
代码块
HTTP
a.vip.x.com/cloud/x/y/19f3f34d44c0e99ac/e5f85c0875b5643dc37752554eec a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/e9b61adc14e12d071047d71b143b a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/4b0ed927c1454e0a2ced373a0863 a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/fed8f52005cc8b4fe2a3d82728f8 a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/59666a1b3d174c21ced72340c94d a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/aab104ff5ae8ca999ba9b01c7067 a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/365ebe92ff1bc62e3158144a8fe5 a.vip.x.com/cloud/x/y/1c12c3cf727db5e24/c0894925b18cf1c3d71dc9f56945
其实上面这些都是访问的一个资源,扫描器只需要对一个进行检测就可以了,没必要全量检测,所以这类URL需要进行归一化,进行采样处理既减少了重复工作,又让处理变得更简单。归一化后的URL如下:
代码块
Plain Text
a.vip.x.com/cloud/x/y/{s}/{s}
这里归一化的算法主要采用正则,合并URL路径中含有序列码、纯数字、标签、中文等URL,让他们归为一类:
代码块
SQL
concat(domain, REGEXP_REPLACe(url_path, '/([0-9A-Za-z_\-\*.@|,]{30,}|[a-zA-Z]+[0-9A-Za-z_\-\*.@|,]*[0-9]+|[0-9]+[0-9A-Za-z_\-\*.@|,]*[a-zA-Z]+|[0-9\*.,\-]+|[\u4E00-\u9FA5]+)','/{s}')) as req_url_path
如果一家公司没有一个非常统一的编码标准,那么他们的URL链接复杂程度,就远远不止上面这种类型。笔者遇到过各种千奇百怪的URL形式,有的URL里面甚至包含中文,这都可能导致噪音。面对这一状况,目前没有一个很好的处理手段,只能遇到了就修改正则。
采样:这里采样比较简单的是同一类型URL每小时取一条数据,因为当前的检测划窗定的是1小时。通过SQL的row_number函数对归一化后的URL链接每小时采样一条,采样过程中需要注意:过滤掉返回不成功的流量、扫描器的流量、异常的流量,因为这些流量可能会干扰你的扫描器,因为它本身就不是一个正常流量,在经过你的扫描器修改后,很可能得不到正确的结果。
代码块
SQL
select * from ( select *, row_number() over (partition by req_url_path) as row_num from ( select *, concat(domain, REGEXP_REPLACE(url_path, '/([0-9A-Za-z_\-\*.@|,]{30,}|[a-zA-Z]+[0-9]+[0-9A-Za-z_\-\*.@|,]*|[0-9]+[a-zA-Z]+[0-9A-Za-z_\-\*.@|,]*|[0-9\*.,\-]+)','/{s}')) as req_url_path from data.sec_ds_x_x_x_x_hh where dt= 'yyyymmdd' and hour = 'hour' ) t ) t1 where row_num = 1
3、基于提升树的分类(GBDT)模型
上面通过归一化、采样、去重等手段锁定了扫描器需要检测的目标,并且也缩小了一定范围,但我们这里忽略了一个问题——并非全部手机号码都是重要的,互联网公司都是提供信息的网站,很多卖家信息等都是公开的信息,其中就包括手机号,这在淘宝、京东等的网页就能轻松获取,这部分信息如果作为敏感信息来进行识别权限问题,显然是不合适的,所以需要采用一定方法过滤掉这些卖家***息。先来看下***息的一种形式如下:
公开卖家数据:
代码块
JSON
{"a":200,"b":{"c":"*27816","d":"*1954900","e":"实木上下铺木床成人高低床双层床二层床子母床多功能儿童床上下床下单立减7000","f":"到店更多惊喜礼品等你拿", "g":"https:/ public static void execute(Listurls) { for (Vulbase vulbase : urls) { futureList.add(executorService.submit(new ProcessThread(vulbase))); } for (Future resultFuture : futureList) { try { Result result = resultFuture.get(); if(result.getSuccess() == true) { System.out.println(result.getSuccess() + "," + result.getMsg()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } futureList.clear(); }
这样同时运行100个任务。效率提升会非常明显,原来1万条URL需要3小时左右的回放时间,采用多线程后只需要5分钟。这里也可以根据机器性能,适当调整自己的线程数。
(2)扫描修改类接口是否存在越权
绝大部分增删改的动作都是POST请求,这里同样需要过滤掉那些无效的POST请求,以免产生大量误报,真正的难点是要找出增删改过数据库的POST请求,这个过程比较困难,从表面是无法识别的,我们一般把流量中记录的traceid和db的traceid进行一个关联,如果关联上就说明在这次访问中增删改过数据库,然后就需要构造访问包的方式去访问系统,这个过程也比较危险,因为你很有可能删除掉了非常重要的信息,所以在这里需要控制好登录态就显得非常重要了,否则很可能删除或修改了别人的数据导致线上故障。代码如下:
代码块
Java
String response = HttpUtils.post(url, body, headers,3000, 3000, "UTF-8");
如果能通过自己的登录态去POST这个请求并且改变了别人数据库里的内容,那可能就存在问题了。
0x03、运营检测结果出来后还有一个比较重要的工作就是运营,我们通过安全运营可以去除一些扫描器的误报,并且还可以发现该接口的一些其他问题,或者进一步被利用的可能,比如是否可以被遍历,还可以横向思考是否同系统还有其他接口也存在这类问题,用来发现更多的流量里面没有的URL,因为有些URL非常重要,但是他一天也没几个人访问,甚至没有访问,这种就只能通过运营的能力来发现,有点类似根据扫描结果来做一个有指导的SDL。还是从上面的URL来看,
代码块
Java
https://x.y.com/a/b/getOrderDetail?orderNo=11000603698171
如果他存在权限的问题,接下来运营还需要确认是否可以通过orderNo来遍历全部的订单信息,如果可以那这个漏洞的危害就变得非常大了,还可以排查出y.com这个域名是否存在其他的重要接口,大概率也会存在问题,从而达到横向、纵向的权限梳理,尽量全的覆盖全域的系统和URL。
0x04、小结权限扫描中最难的问题,就是我们对业务的无法理解导致大量误报,最终导致的结果就是不可运营性,这其中的误报包括:
1、返回信息的不确定(是否是敏感信息)
2、对数据库的修改是否是合法 *** 作
笔者主要是通过限定返回信息来缩小敏感信息的范围并配合模型和规则去除误报和无用的返回信息。这里采样起到了一个非常重要的作用,对于全量的数据我们没必要全部进行校验,只需要对同一类接口进行校验就够了,这样可以大大降低引擎的压力同时也能提升效率减少误报。
三、结语权限问题治理、发现、检测对于每一家公司都是非常困难的,困难点主要源于我们对业务的不理解。我们最好在事前、事中、事后体系化去解决这类问题,没有银d。事前可以通过架构层,统一开发框架,统一编码规范,结合白盒扫描等等方式,事后的解决办法主要是从具备敏感资产的这个点进入,并做好权限的动态配置和校验,从而达到检测权限漏洞的能力。其中最主要的是我们要具备每一个系统的权限动态配置能力,这样才能进入到系统对URL进行扫描。时间仓促本文作为漏洞扫描系统的一个功能和大家做一个技术上的探讨和分析,盲人摸象而已,实际权限问题的实践远比想象复杂,后续有机会再做进一步交流。
通常文章看到这里重点内容基本也都结束了,但是以上还不是本文的重点,本文重点想讲的是得物app是一家发展迅猛的电商公司,目前安全部、数据安全还有很多职位以待大佬加盟,或者技术交流可以加微信:hezheng_2329
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)