1. 序我在之前的一篇文章中简单讲解了Web应用代码自动化审计的几种实现方式。 这篇文章以自动化辅助挖掘XSS漏洞漏洞为例,简单的讲解一个实际的灰盒分析实现的例子。 在上文中有提到到,漏洞可以认为是输入到危险函数的过程,所以这篇文章涉及到的主要是输入、危险函数、具体实现这三个部分。 2. 输入作为污点来源的输入主要考虑当前状态、网络请求和存储函数三个来源。 当前状态主要指像窗口名、当前Url、Hash、referr等,具体对应如下这些变量: window.name window.location.href window.location.search window.location.hash window.location.pathname window.location.url document.URL document.documentURI document.URLUnencoded document.baseURI document.referrer
网络请求主要指使用异步方式获取的请求及其响应,这部分可以通过hook XMLHttpRequest fetch 等API来获取。 存储主要指Cookie、Indexdb、localStorage、sessionStorage等。 部分输入在网页初始化时已经确定,这部分由程序记录下来。部分输入会不断变化,如cookie等,这部分输入会通过插桩、事件处理等方式进行监控,并实时对变化进行记录。 3. 危险函数这里把危险函数分为直接执行JavaScript、加载URL、执行HTML、创建元素、部分可控执行五类,具体涉及到的函数与相关模式如下。 3.1 直接执行JavaScript这类危险函数直接把输入以JavaScript代码的形式执行,例如。 3.2 加载URL这类危险函数以URL加载的形式执行JavaScript代码,但是大体和JavaScript类似。 location=javascript:alert(/xss/) location.href=javascript:alert(/xss/) location.assign(javascript:alert(/xss/)) location.replace(javascript:alert(/xss/))
3.3 执行HTML这类危险函数直接把输入以HTML代码的形式执行,在一定情况下可以执行代码。 3.4 创建元素这类调用大多是创建一个DOM元素,并将其加入页面中。当script的源可控或者元素的构造可控的时候,可能会出现问题。 scriptElement.src domElement.appendChild domElement.insertBefore domElement.replaceChild
3.5 部分可控执行这类调用存在一定的动态成分,可控的程度不高,但是在部分情况下存在价值,因此在工具中加入了对其的监控。 (new Array()).reduce(func) (new Array()).reduceRight(func) (new Array()).map(func) (new Array()).filter(func)
4. 整体架构4.1 污点追踪污点追踪的实现有两种思路,一种思路是hook浏览器native的实现,但是这种方法要求对浏览器本身的实现机制有比较好的了解,而且编译过程复杂,很难迁移。浏览器一旦更新,就需要修改大量的代码来适应。 另外一种思路是基于浏览器插件做JavaScript层的Hook,这种方式虽然没有浏览器源代码层的hook底层,但是开发更快,更容易迁移,在有新的机制出现时也比较容易适应。 Chrome插件中,代码分在content-script、background、popup等运行时中。其中只有content-script可以操纵宿主页面的DOM,但是宿主页面JavaScript和content-script也在不同的沙箱中,无法hook,只能使用注入的方式。 在hook后,当出现危险函数的调用或网络请求时,则将其记录至后台。 4.2 疑似利用确认和很多漏洞不同,大部分存储型的漏洞是没有回显的,因此需要做一定的确认工作。 在获取信息后,以域名为单位。遍历sink和source,查找重合的地方。如果source在sink的参数中出现,就可能是漏洞点。需要注意的是,除了hash之外,网络请求等各种参数不一定可控。另外,需要去除同一域名下,同一参数同一调用的Sink,同一源和同一返回的结果,减少重复数据。 在具体确认的时候,考虑到,sink中的参数可能是source的一部分,source中也可能只是sink的一部分,因此使用公共子字符串算法,只要字串的长度小于sink和source最小的长度。 不过即使完全可控,也可能出现waf、sanitizer、难以绕过的csp策略等。因此这种方法会有比较高的误报率,但是相对的,在hook较全的情况下,漏报率会小很多。 除了上面提到的这种方式,工具还采取了动态污染的方式。通过修改请求参数和修改函数调用时的参数两种方式,传入一些测试性的payload,如果在返回界面获取到了相信的结果,那么漏洞就是存在的。 4.3 结果查看这里考虑过直接用插件自带的界面popup / background来显示可能的结果,用浏览器层的localstorge存储数据,但是考虑这种方式会影响浏览器间迁移的兼容性。 于是最后单独用vuejs + django编写了一个小的站点来接收请求,查看结果
|