一 引 言
一款扫描产品上线后,我们经常会反思到底做得怎么样呢?那么如何去衡量一款扫描器的优劣呢?我们确定了几个指标:准确率、扫描及时度及自主发现率,通俗来讲就是如何更快、更全、更准、更智能的去扫描,或者说如何能达到一种完美的平衡,下面从这四个维度去阐述一下我们的实践及思考。
二 衡量指标及解决思路
(一)更快(扫描及时度)
更快:更及时发现漏洞,甚至追求在攻击者之前发现安全漏洞,但有一些残酷现实摆在面前:
我们期望一轮线上所有业务例行扫描能够在3小时以内解决战斗,甚至期望能够做到1小时以内;集群形态时考虑的就是增加机器资源,通过足够的机器资源来提升扫描性能,即使不考虑资源成本,但因为有限速的限制,累加资源最终也会遇到瓶颈,况且真没这么多机器(偷笑);这条路看来是走不通的,还是得从源头进行解决:
1.降低扫描量
数据中心中存在6千万url(已经经过了前面介绍的基于hash的url处理过程),但是有多少url是应该扫描或值得扫描?如果无区别的进行同等扫描估计一个月都没法完成一轮扫描,这里需要方法将数据中心中应该扫描的url抽取出来,减少扫描的url量。我们的思路是:
(1)抽取待扫描URL:通过url页面内容变化来判断当前url是否值得或需要扫描,这个过程中需要排除动态内容对内容相似性对比的干扰(默认认为内容存在变化才需要二次扫描);同时存在后端逻辑变化未反映到页面内容的情形,这种该如何进行识别?完全识别应该是做不到的,只能借助其他手段辅助进行判断;比如可以通过数据分析统计出同一域名下不同url的访问顺序,通过顺序的变化来辅助判断,甚至可以统计不同url的访问量,通过访问量的变化来进行判断等,可以综合这些因素进行考虑筛选;
(2)分阶段进行:第一阶段优先扫描当天库中新增URL(url第一次扫描),第二阶段每半周扫描一次库中有变动的URL,第三阶段每月扫描一次库中全量URL,主要用于弥补后端逻辑有变化但是未引起页面内容变化,导致第二阶段扫描无法覆盖的case。
(3)分类进行:如果url实在太多,这里只能进行取舍,可以按照业务重要性及业务风险情况等维度进行url分类,优先去扫描既重要又风险大的业务。
2.降低poc发包量
针对常见通用安全漏洞,全部重新调研目前的主流扫描方案,然后结合自身经验进行充分思路碰撞形成新扫描方式,同时将poc发包量作为重要考量因素始终贯穿其中,达到换一种扫描思路来降低poc发包量;这里以反射型xss为例,传统扫描方案:收集尽可能全的payloads(至少70+),通过遍历每一个payload发包去验证是否存在漏洞,这里首先不考虑因payloads收集不全而造成漏报的问题,实际上这种情况发生的概率其实不低,就单单从发包量来考虑,单个参数就需要发包70+,一个url按照10处来估算(一个url需要扫描的位置有:headers、path、参数等),单个url至少发包700+,这从速度来考虑简直是灾难。
而新的扫描方案单个参数只需要发包7次不到(包括6次探测包+1次验证包),通过发送探测包(包括“<>’”/*=”、“’”、“””等)来确定xss输出的漏洞点及漏洞发生的具体场景,然后针对其发生场景针对性的构造一个payload进行验证是否存在漏洞即可,相对于传统扫描方案,性能提升了将近10倍。
3.限速
业务的承压能力越强,限速QPS可以放得更宽,相应扫描速度就会越快,尴尬是我们拥有数万域名量,主要里面还有一些小产品线,过高的QPS甚至可以将小产品线扫挂,在没有方法知晓产品线承压能力情况下,只能按照最低限速50QPS进行统一限速扫描;
但实际情况是不同业务线抗压能力相差甚远,统一按照50QPS进行限速将严重影响扫描速度,这里急需根据不同业务线进行逐个的限速设置,不过这里缺乏参照标准,只能借助于业务线的访问量进行大概估计。其实最理想的状态是,能否根据当前业务的响应状态来动态调整发包量?通过发包来确定业务线当前响应的快慢,只要业务线没有响应延迟的情况发生,便可动态调整发包量,从而做到根据业务线实际情况进行限速适配;不幸的是,由于网络延时的变化造成动态调整效果不甚理想,还有一段路需要走,或者是否存在其他更好的方式?
(二)更全(自主发现率)
更全:主要关注漏报率,反过来说就是主动发现率;造成漏报概括起来就两点:第一扫描能力不覆盖,第二能力覆盖,但是由于缺乏输入源或者平台产生异常;为了做到更全,我们就需要考虑:
理想很美好,可问题是:
(1)漏洞类型,特别是弱点规则成百上千需要收集和整理,这是一个浩大的工程,并且为了扫描的准确性,需要一一搭建本地环境进行验证确认;
(2)存在一些漏洞类型依靠之前传统的扫描思路很难解决,或者说不适合以扫描来解决,并且还是一些非常高危的漏洞类型,比如越权、无回显漏洞等;
(3)扫描之前一直以IDC全流量作为扫描的输入源,可问题是全流量并没有全机房覆盖,同时由于带宽及跨机房传输问题,全流量一直存在30%-40%丢失的问题,更重要的是全流量缺乏post请求类型及https流量,可post流量是检测某些漏洞类型的基础,比如存储型xss,而https是未来发展的趋势。
为了解决这些难题,我们的思路是:
1.规则收集
Web漏洞类型其实范围挺广泛,从扫描的角度来看,主要可以分为通用未知漏洞、第三方已知漏洞、主机弱口令、弱点规则等;比较麻烦的是第三方已知漏洞和弱点规则的收集整理,说实话没有捷径可走,只能通过人工去进行筛选收集;针对弱点规则,唯一比较欣慰的是可以通过SRC报告的历史漏洞进行查漏补缺,但对于第三方已知漏洞就只能通过公开漏洞库、官方安全消息进行补充,这需要专门的人力进行,特别针对历史的漏洞,更需要大量人力去搜集整理,这时候安全社区的优势就体现出来了,要么去和安全社区合作,要么就只能通过SRC以安全社区的思路进行收集,依靠小部分人力总会有所遗漏。
比较庆幸的是,我们有IDC全流量(其实全流量是个宝贝),将全流量导入到waf规则集,关注拦截的记录中是否存在扫描所遗漏的规则;同时针对waf未识别的流量进行流量分析,通过机器学习的方法来识别出潜在感兴趣记录,然后人工分析来识别规则是否覆盖;当然前提是需要拥有众多业务,攻击者也希望以此作为目标进行扫描探测或攻击尝试。
2.非传统扫描方式
Web存在一部分漏洞,按照传统思路其实不适合扫描,或者说很难扫描,这时候需要换种思路进行考虑,比如通过xss平台解决一部分存储型xss的难题,通过回显平台去解决无回显漏洞的难题,同时我们还会去尝试以扫描的方式去解决部分csrf,甚至去尝试解决越权访问的难题。
其实就是换种思路让不可能变成可能,接着说存储型xss,其扫描的难点就在于输出点定位,大部分扫描方式只能遍历同一域名的所有url来寻找输出点,这种检测思路对于大型互联网企业来说简直就是灾难(根据上面更快的分析逻辑),我们需要另寻思路,除了依赖上面的xss平台解决一部分case,还有其他方式吗?其实是有的,我们可以思考存储型xss最可能出现在哪?其实最有可能出现在一个请求的直接响应中,或者出现在一个请求后的紧接着的一个请求响应中(存储型xss必须要交互增加一些内容的,基于人的习惯,自己增加的内容自己往往会去查看是否成功等,这意味着下一个请求就很有可能是新增内容的输出点),基于这样的考虑,完全可以依靠全流量统计分析出访问链接直接的顺序关系,通过这种方式来解决部分存储型xss,而不至于遍历域名的所有url这种耗费资源和性能的事情。
3.数据中心
收集最全的url作为扫描输入源,这一直就是数据中心的终极目标,当然url唯一且干净也是数据中心的重要目标;为了收集更全的url绞尽了脑汁,除了IDC全流量,还考虑依托OA全流量,通过主机agent去收集线上业务访问日志、甚至收集线下流量导入到线上,当然还有爬虫,甚至还有用户主动上传;但不幸的是,库中收集的流量还是十分有限,绝大部分漏报都是流量缺失导致,这也是下一步需要考虑的重点。
4.异常监控
扫描平台后端是一个基于云的分布式扫描集群,逻辑相对比较复杂;为了监控异常,需要关注扫描任务的完整生命周期:一个url的漏扫,需要关注url是否存在于数据中心、是否推送到扫描集群扫描、是在集群的那个节点上扫描的、扫描过程中是否出现异常、当时是否扫出漏洞、漏洞是否推送成功、人工是否已经check发单等所有关键节点;同时为了监控扫描平台的异常,还需要关注每周漏洞产出的趋势、数据中心中url趋势、扫描的耗费时间趋势等等。
(三)更准(准确率)
更准:主要指准确率,要求更低的误报,误报产生的原因很多,这里仅列几点说明:
1.扫描方式引起
还是以反射型xss为例,以发送探测包的方式来替代遍历payloads的方式,虽然规避了漏报和提高了性能,但是误报也伴随而来;通过定位输出位置及场景来确定是否存在xss,但是xss的最终形成是需要依赖浏览器进行解析,所以必然存在误报:比如以json串输出,输出点在单引号或者双引号中,并且引号被转义不能被闭合,但是输出格式是html(本来json输出content-type应该正确设置为text/json等,但有些非要设置为text/html,通过场景来分析是不会存在xss的,但因为输出格式问题,浏览器会当做html进行解析从而形成漏洞)。
当然上面列举的这种情形,通过增加一步验证即可解决(根据场景构造特定payload进行验证确认)
2.扫描规则引起
扫描漏洞的确认一般都是通过匹配规则来进行的,规则考虑的欠妥都会引起误报。
最常见的就是扫描特征出现在正常响应中,比如命令执行,通过cat /etc/passwd文件来检测是否存在漏洞,但是特征出现在搜索结果中明显就是误报;当然这里可以通过计算一些动态值予以规避,但悲剧的是动态值一样可能出现在响应中,这时候就需要考虑发两次包计算两次动态值(其实发包量对扫描而言弥足珍贵,多一次包就意味着扫描及时度没法达成,其实及时度、漏报率、准确率是相互制约的)
3.漏洞类型引起
一些不适合扫描的漏洞类型,比如越权访问,通过扫描方式进行必然伴随大量误报,这个除非革新的扫描思路出现,不然目前很难消除误报,而且这样的漏洞类型数目还不在少数。
4.扫描性能引起
为了扫描性能考虑,减少发包量,针对弱点规则,数量众多规则一起检测,除了规则本身不严谨造成的误报以外,更多需要关注规则的交叉影响:比如遇到这种场景,不仅仅请求正常路径响应泄露信息,而是请求任何路径都是响应泄露信息,这种就会造成规则的交叉影响。
(四)更智能
更智能:扫描平台寄希望尽量完全自动化的方式去解决一切问题,尽量减少人工的参与量及运维量,我们设计之初就是将更智能作为重要指标进行考量,这里列举几例说明:
1.热备上线
写第一篇文章时其实简单介绍过热备上线相关情况,安全扫描poc在方案调研及实现过程中,由于调研的不全面或者考虑的不严谨,肯定会出现误报或者漏报问题,这时候就需要通过更新poc予以解决,现在的难点是:扫描集群时刻都有任务在运行,为了更新任务只能热备上线,不然只能将未跑完的任务全部kill掉,这样既浪费扫描资源,又严重影响扫描及时度。
通过内置升级更新模块,当需要更新某个POC时,可以把POC及对应版本推送到POC更新模块,POC更新模块比对新旧两个版本并进行处理(只有当推送的POC版本大于当前版本时才更新,然后删除旧版本,给当前POC打上待升级标识);当积累到一定数量的待升级POC,或者固定的周期时(这些都可以在调度程序中进行设置),调度程序启动升级准备工作,标识所有存活容器为待升级状态,当某个docker容器执行完当前基本单元("单poc+单url")返回结果后,调度程序发现该容器状态为待升级状态,则不再分配扫描任务,并且启动POC更新程序,POC更新程序去升级更新模块查询那些POC带有待升级标识,将所有待升级标识的POC自动拉取到docker容器中,并与容器中的当前版本进行比对,同样POC版本大于当前容器中的版本时才进行更新处理,更新完成以后,通知调度程序升级完成;调度程序获取到相关状态后,重置容器的状态为正常状态,并且可以继续进行扫描任务的派发及执行,基本可以做到不干扰当前运行任务情况下,更新升级,做到热备上线。
2.扫描能力自我完善
前面通过一系列动作使扫描平台日臻完善,但一时的优秀不是目的,持久的优秀才是最终目的,那么后期通过什么方式来持续保障或者完善扫描平台呢?除了前面介绍的通过全流量进行payloads自动收集、查漏补缺以外,我们还可以搭建众多主流扫描引擎来进行外界报告漏洞的自动能力对比,持续提升扫描能力;当然SRC poc收集、第三方平台合作、通用漏洞实时分析&新增扫描poc也在同步进行中。
3.异常自动诊断恢复
监控到扫描体系中的任一阶段存在异常时,启动自动诊断程序进行判断,并根据具体场景来进行后续处理,续扫、重扫或者启动报警人工干预等,第一篇文章中讨论了部分case,这里就不再详细阐述了。
三 总 结
整套体系化建设实践后,大家应该比较好奇我们最终的效果对吧?其实“分布式Web漏洞扫描服务建设实践”系列第一篇文章的时候,我们有简单说过衡量指标的一些统计数据:扫描出来的漏洞准确率达到了98%以上,基本可以做到无需人工check;输入源URL存在的情况下漏报率更是控制在0.5%以下,即使算上输入源URL缺失导致的漏扫,线上的自主发现率也已经达到了90%以上(扫描发现的漏洞占所有线上漏洞的比例,线上漏洞的发现途径有很多,比如扫描发现、外界SRC报告、友商报告、人工渗透测试等);每天新增URL例行任务2小时内结束,全量URL例行任务2天内结束;随着漏洞检测能力的持续提升,安全事件中因常规型漏洞(扫描器很难自动扫描发现的漏洞,比如越权、逻辑、CSRF漏洞类型)引起的占比也由15年之前的52%降低到目前的18%左右;SRC外界白帽子报告的线上漏洞中常规型漏洞占比也由15年之前的72%降低到目前的34%左右,随着今年自研的白盒及灰盒检测能力的持续上线,这个占比肯定会下降得更加明显,当然随之我们的漏洞检测重心也会往越权、逻辑等漏洞探索,我们的目标很明确:期望尽最大可能通过自动化的方式低成本的去解决线上绝大部分漏洞问题(我们人力真的很少,哭样)。
感兴趣同学可以继续关注EnsecTeam后续"分布式Web漏洞扫描服务建设实践"系列技术文章。最后这句感悟依然分享给大家“扫描技术虽已成熟,但只有精心耕耘方能知晓其精髓,而刚触及其精髓才知挑战依旧”。