Pellegrino, G., Tschürtz, C., Bodden, E., & Rossow, C. (2015, November). jäk: Using dynamic analysis to crawl and test modern web applications. In International Symposium on Recent Advances in Intrusion Detection (pp. 295-316). Springer, Cham.
关于文章
这篇文章是RAID 2015的一篇文章,是我从一篇S&P 21的论文的参考文献和代码里找到的一个核心组件。主要思想就是利用JS动态分析Web应用来进行爬虫操作。主要的工作就是各种函数和API的Hook及动态分析指引爬虫探索。还是有不小的改动空间的。文章中也提到他们使用的是WebKit默认的JavaScript引擎,即JavaScript Core来进行动态分析,可以使用V8或者其他的引擎来Hook set函数。这也是他们未来的工作内容。
下面是文章的大致内容。
摘要
传统的爬虫程序会通过解析HTML文档并应用静态正则表达式来提取新的URL。虽然这种方法可以提取传统Web应用程序中的URL,但它无法探索大部分基于JavaScript的现代应用程序。本文提出了一种基于客户端JavaScript程序动态分析的Web应用新技术。我们使用动态分析来Hook JavaScript API,这使我们能够检测事件的注册、网络通信API的使用以及动态生成的URL或用户表单。然后,我们建议使用导航图来执行进一步的爬行。基于这一新的爬行技术,我们提出了一种Web应用扫描器jÄk。
主要贡献
- 提出了一种新的基于JavaScript API函数挂钩和运行时DOM分析的动态程序分析技术。
- 出了一种基于模型的Web应用爬行技术,它可以通过与JavaScript程序的交互来推断导航图
- (实现jÄk并与现有工具比较证明其有效性)
背景
事件处理注册
Event Handler Attribute:
1 | <a href="contact.php" onclick="doSomething"></a> |
Event Handler Property:
1 | <a id="link" href="contact.php"></a> |
addEventListener Function:
1 | <a id="link" href="contact.php"></a> |
Timing Events:
这些事件的处理程序注册分别通过setTimeout和setInterval函数执行。
网络通信API
XMLHttpRequest (XHR) API的使用
1 | var server = "http://foo.com/"; |
经典爬虫静态分析HTML和JavaScript代码以提取URL,传统爬虫无法提取HTTP POST请求的结构。如果URL是动态生成的也无法检测。现代Web应用程序的许多部分只能通过解释这种动态生成的请求来实现,从而限制了现有爬虫程序的覆盖范围。
爬取现代Web应用
最初,用户加载URL http://foo.com/,该URL将Web应用程序的登录页面加载到浏览器中。然后,该页面被加载到其初始状态并显示给用户。然后,用户可以与页面交互,例如提交HTML Forms或单击HTML Links,这将调用更多页面,如http://foo.com/BAR/,如右侧所示。但是,用户事件或自发事件(如计时器)也可以更改页面的内部和可视状态,如虚线箭头所示。这些内部状态可能会对页面的DOM造成重大更改,这就是爬虫程序也应该考虑它们的原因。然而,大多数当前的爬行器只关注HTML,这实际上限制了它们只能发现那些HTML页面的初始状态。
总体思路是将经典的Web应用爬虫与Web应用客户端的程序分析结合起来。爬虫从种子URL(例如,登录页面)开始,并且它检索客户端程序(例如,HTML页面或JavaScript程序)的资源。然后,它在修改后的JavaScript执行环境中运行客户端程序,以分析其行为。从分析中,爬虫程序可以提取事件和URL,这些事件和URL稍后用于探索客户端程序和服务器端程序。最后,爬虫重复分析,直到无法发现更多新行为。
动态JavaScript程序分析
一种方法是修改JavaScript解释器以检查和监视程序的执行。每当执行感兴趣的指令时,解释器都会执行Hook函数,而不是原始指令,或者除了原始指令之外还执行钩子函数。然而,这种方法需要修改JavaScript引擎,比较复杂。此外,该方法将该技术绑定到特定引擎。
另一种方法是在客户端JavaScript程序的源代码中插入对自己的JavaScript函数的调用。这种方法需要对程序的源代码进行处理和转换。不幸的是,JavaScript程序的源代码可能不是作为一个整体提供的,因为它可能在运行时一次一段地流到客户端。
本文的jÄk使用第三种方法,通过将函数挂钩到JavaScript执行环境内的API来监视程序的执行。首先初始化JavaScript引擎。然后,它通过在引擎中运行自己的JavaScript代码来修改执行环境。此代码安装函数挂钩以捕获对JavaScript API函数和对象方法的调用,并安排对DOM树的检查。之后,它运行客户端JavaScript程序
函数挂钩
函数挂钩是一系列允许截取函数调用以检查参数或改变程序行为的技术。不解释了,直接看demo:
1 | alert("Hello world!"); // show a popup window |
1 | Object.defineProperty(obj,"prop",{set:myHook}). |
事件处理程序注册
addEventListener函数、事件处理程序属性(event handler property)和计时事件。对于事件处理程序属性,我们不使用函数挂钩。因为在此模型中,处理程序注册为HTML属性,所以我们通过访问HTML DOM树来捕获它们。
Hooking addEventListener
每当调用addEventListener函数时,jÄk都会注入自己的函数。
1 | installHook(Element.prototype , "addEventListener", myHook) |
Element.Prototype是一个特殊的对象,它定义了所有DOM节点的基本行为。
Hooking Event Handler Properties
在DOM元素中安装一个钩子函数作为set函数,set函数不能保证Hook在整个分析过程中都保持不变。这可以通过首先重新定义fineProperty函数,然后监视其使用来解决。
1 | function installHook(obj, f, hook) { |
Hooking Timing Event Handlers
用上面的installHook:
1 | installHook(window , "setTimeout", myHook) |
网络通信API
首先,实例化一个XHR对象。然后,通过open函数将服务器端的HTTP请求方法和URL传递给XHR对象。最后,使用函数Send发送HTTP请求。同样使用installHook函数:
1 | installHook(XMLHttpRequest , "open", myHook); |
通常,想要在构造函数中hook一个函数时,需要修改installHook的第5行以返回该对象的一个实例,即return new orig(arguments[0], ...)
。
运行时DOM分析
我们考虑运行时DOM分析的三种用途。首先,它用于将处理程序的注册提取为HTML属性。其次,它用于在触发事件时识别树中的更改。第三,当JavaScript引擎不允许将代码作为集合函数挂钩时,它可以用来发现事件处理程序的注册。
动态跟踪收集
对于事件处理程序注册,钩子函数取决于事件类型。例如,对于DOM事件,钩子函数收集事件的名称、源的DOM树中的位置,并将其发送到爬虫程序。相反,对于计时事件,钩子可以收集调用者设置的超时。在这两种情况下,钩子函数都会通过JavaScript对象跟踪将跟踪条目发送到爬虫程序,该跟踪项映射到爬虫程序内存中的队列对象。该对象充当JavaScript执行环境和爬虫执行环境之间的桥梁。
1 | function addEventListenerHook(elem , args) { |
在收集网络通信API的跟踪条目时,API通常需要多个步骤才能建立通信通道并将消息传递到服务器端,这些步骤不一定是原子的。jÄk为每个API函数定义了一个钩子,然后使用API对象来存储API调用的当前状态。
爬虫
导航图
jÄk创建并维护Web应用程序的导航图,该导航图对客户端程序内的转换和网页之间的转换进行建模。该模型是一个类似于图1的有向图,其中节点是页面群集,边可以是事件和URL。
导航
JavaScript程序的动态分析生成包含事件处理程序注册和动态生成的URL的运行时跟踪。它还包括DOM树分析的结果,例如链接的URL和表单。然后,该信息被分类到两个列表中,即事件列表和URL列表。这些列表代表了爬虫可以采取的行动前沿,以进一步探索网络应用。
每种类型的动作都可能有不同的结果。一方面,请求一个新的URL肯定会导致检索一个新的页面,如果该页面包含一个JavaScript程序,那么它就会在一个新的JavaScript环境中执行。事件则不一定如此。触发事件可能让爬虫探索JavaScript程序的更多行为,即生成新的URL。然而,事件也可能导致运行新的JavaScript程序,例如将window.location
设置为新的URL。然而,我们可以通过函数挂钩来阻止这种行为。由于这些原因,我们的爬虫对事件的优先级高于URL。当列表中没有更多的事件了,那么我们就会处理URL的列表。当所有列表都是空的时候,那么爬虫就会退出。
诸如点击、聚焦、双击和鼠标移动等事件可以在JavaScript执行环境中被触发。要启动一个事件e,jÄk首先识别DOM元素,然后通过DOM事件接口函数dispatchEvent来启动事件。之后,jÄk通过动态分析观察处理程序的执行结果。事件处理程序可以引起页面的刷新,加载新的页面,向服务器端发送消息。为了避免对服务器端的任何干扰,当发生事件时,例如,网络通信API的Hook函数将阻止消息的发送。
如果事件处理程序的结果是网络通信API,那么jÄk从跟踪中获取URL,并将其列入URL列表。类似的,如果事件处理程序设置了一个新的URL(例如,window.location=UR L),那么jÄk会把这个URL加入到linked-URLs列表中。如果事件处理程序添加了新的链接URL和表单,那么它们将被插入到相应的列表中。最后,如果事件处理程序注册了新的事件,那么jÄk准备了特殊事件,它包括了导致这一点的事件序列。
爬虫的目标应该是找到包含新内容的页面,而不是已知内容的页面。为了选择下一个页面,jÄk根据两个因素给每个前沿的URL分配一个优先级。(i)jÄk在过去见过多少次类似的URL,(ii)过去的URL在集群中的分散程度。爬虫会从最高优先级到较低优先级处理URL。
jÄk使用两种技术来终止执行。首先,它对搜索深度有一个硬限制。第二,如果不能再找到新的内容,爬虫就会终止。
实现
jÄk是用Python编写的,通过Qt应用框架绑定在WebKit浏览器引擎上。开源:https://github.com/ConstantinT/jAEk/
jÄk包括四个模块:动态分析模块、爬虫模块、攻击者模块和分析模块。jÄk依靠WebKit默认的JavaScript引擎,即JavaScript Core来进行动态分析。然而JavaScript Core将事件属性设置为不可配置。因此,JavaScript Core不允许通过set函数来使用函数挂钩。为了解决这个问题,jÄk通过DOM检查来处理这些情况。然而,我们验证了Google和Mozilla的JavaScript引擎,即V8和SpiderMonkey,允许Hook set函数。在未来,我们计划用V8取代JavaScript Core引擎。
爬虫模块实现了前面的爬虫逻辑,从种子URL开始,jÄk检索客户端程序并传递给动态分析模块。从一个种子URL开始,jÄk检索客户端程序,并将其传递给动态分析模块。动态分析模块返回用于填充URLs和事件边界的痕迹。然后,jÄk选择下一个动作并提供给动态分析模块。在整个过程中,jÄk创建并维护网络应用的导航图,用于选择下一个动作。爬虫模块的输出是表单和URL的列表。最后,攻击者和分析模块针对一些漏洞测试服务器端。对于每个URL,攻击者模块都会准备携带payload的URL。然后,它将URL传递给动态分析模块以请求URL。然后,响应在动态分析模块内执行,该模块返回一个执行跟踪。然后,分析模块对跟踪进行分析,以决定测试是否成功。
评估和相关工作
略
结论
本文提出了一种基于客户端JavaScript程序动态分析的新型网络应用抓取技术。动态分析Hook JavaScript API的函数,检测事件的注册、网络通信API的使用,发现动态生成的URL和用户表单。然后,这将被爬虫用来执行下一个动作。爬虫创建并建立一个导航图,用于选择下一个动作。我们搞了一个工具jÄk,它实现了所介绍的方法。我们使用13个网络应用对jÄk和其他四个网络应用扫描器进行了评估。我们的实验结果表明,jÄk可以探索网络应用的表面,比其他工具大86%。