Measuring and Disrupting Anti-Adblockers Using Differential Execution Analysis
作者:张凯(清华大学)
本文发表于 NDSS 2018,原文作者:Shitong Zhu , Xunchao Hu , Zhiyun Qian, Zubair Shafiq and Heng Yin
摘要
全球数百万人使用广告拦截器删除恶意广告,并防止因为广告追踪造成个人隐私泄露。而对于大多数提供免费在线内容和服务的网站来说广告收入是主要收入来源,故把广告拦截器看做主要威胁,他们部署anti-adblockers来检测和阻止adblockers。针对这种情况,广告拦截器反过来检测和过滤反广告拦截的脚本。广告拦截和反广告拦截之间的军备竞赛在反复进行。
作者对反广告拦截器进行了全面分析,并帮助广告拦截器对抗反广告拦截器。在论文中,提出利用有差异的执行分析方法自动检测和分析反广告拦截器。通过收集使用和未使用广告拦截器的浏览器来访问同一网站的执行踪迹,作者能指出存在分歧的分支及条件语句。而这些分歧因为反广告拦截代码引发的。利用此系统,检测到Alexa top-10K有30.5%的网站部署了反广告拦截器软件,与以往报道相比提高了5-52倍。早期工作仅检测有明显反应的anti-adblockers,这种系统对没有明显反应也能有效测量。通过对1/3被检测的网站进行分析,发现90%网站对广告拦截器存在做出不明显反应。最后,基于这些知识,作者给出JavaScript rewriting和API hook两种方法帮助拦截器绕过当前anti-adblockers。
背景
广告驱动的网站大部分在线内容和服务都是免费的,其大量在线广告引发了严重的安全和隐私考虑。比如广告商跨网追踪用户却不给出任何提示,造成隐私暴露,威胁隐私。攻击者甚至利用广告传播恶意软件,因此很多人选择部署adblockers。广告发布平台因为广告拦截损失10多亿美元的收入。因此部署反广告拦截来检测adblockers,并提出不同应对措施:
1、一些广告发布商比如微软和google,提出可接受的广告程序,让他们的广告都列入白名单。小的发布商可以免费注册,但是中大规模的发布者必须支付他们的广告费。
2、一些广告发布者,最明显的是脸书,控制广告让广告拦截更难移除它们。然而adblockers迅速赶上来并调整它们的规则来更好的拦截广告。
3、许多广告发布者部署反广告拦截器,来检测拦截器并对此客户端作出相应。通常反广告拦截器强制用户把网页加入白名单或者禁用
当前广告拦截器依赖手工整理过滤列表来拦截广告。广告发布商操纵广告交付来规避过滤列表。比如,更改域名和HTML元素标识符来绕过过滤。这迫使过滤列表作者频繁更新列表,工作量很大,研究者提出基于网络流量分析的办法(例如,识别广告服务域名)自动升级过滤规则,这种办法不能解决广告发布商的HTML操纵。当广告拦截器更新规则来阻挡Facebook广告,Facebook能够继续操纵它们的HTML绕过新的过滤规则。研究者提出一种有知觉的广告拦截方法用于可见识别,拦截广告使用可见字符识别,fuzzy图像匹配技术。关键思想是,因为政府法规和行业自律要求,广告和网页正常内容是有区别的。
早期研究提出一些措施来检测和规避anti-blocker软件。一种方法是提取anti-adblockers脚本指纹,然而指纹不会自动生成和扩展。手工分析代码要比识别广告有关的URL或者HTML元素困难的多。有研究者提出自动的静态JavaScript代码分析技术检测恶意JavaScript脚本(基于语法和结构分析)。但是这些技术准确捕捉JavaScript行为很困难。因为JavaScript代码是动态的,并很容易进行代码混淆。
主要工作
作者提出一种动态代码分析方法对anti-adblockers进行大规模系统分析和描绘其行为特征,这里称为执行差异分析。其关键思想就是对网站进行有adblockers和无adblockers情况进行有差别的执行分析。对于一个部署anti-adblockers的网站,如果被安装adblockers的浏览器访问,以及被未安装adblockers浏览器访问,它会有不同的JAVASCRIPT执行轨迹。也就是说,通过对比分析这同一个网站的不同执行轨迹可以有助于发现网站是否部署anti-adblockers软件。
作者开发了一个系统自动对网站访问,对执行轨迹进行比较分析,找出存在分歧的分支及条件语句,检测出该网站是否部署反广告拦截软件。系统概图如下所示。
anti-adblockers脚本主要由两个部分组成:(1)触发器,它检测adblockers的存在;(2)反应器,它能显示adblockers检测消息,甚至给后台服务器报告检测结果。当检测到广告拦截后,anti-adblockers就会执行一些额外语句,例如显示警告消息或者发送统计数据给它们后端的服务器。由于关注的是JavaScript的控制流,所以系统仅收集了所有分支语句的跟踪,以及跟踪对齐所需的调用堆栈信息。这些信息送给差异执行分析来识别两种轨迹的条件分支,最后得到一个分支列表以及分支语句中的检查条件。
已有的JavaScript执行轨迹追踪技术有三类:JavaScript rewriting;JavaScript debugger interface;JavaScript engine-based 。论文选择基于引擎的方法。这种方法不要求对JavaScript代码做任何变动;对于anti-adblockers这种方法透明,无法检测。系统部署Chromium作为JavaScript引擎。Chromium V8对每个函数生成抽象语法树,然后编译成本地代码。在本地代码生成过程中,作者编写的指令嵌入其中,指令收集一些资源映射信息(比如语句在脚本中的偏移位置)和JavaScript语句信息。通过修改JIT,在执行本地代码之前,首先执行stub 代码,它能够在运行时访问内联的变量获得已经被执行的JAVASCRIPT语句的信息。资源映射信息用作被执行语句的ID,在执行痕迹对齐时用到。
由于通过监控控制流的差异来检测anti-adblockers,所以需要对所有分支语句要进行记录。常见分支有if/else ;switch/case;condition?expr1:expr2;for/while loop;try/catch;以及其它潜在的条件表达式。根据anti-adblockers脚本最常见用法,选择if/else 和条件表达式。为了对齐执行痕迹需要记录call/return 语句,堆栈信息中包含了它们调用的上下文语句信息。通过对adblockers的filter list进行配置以便最大程度发现anti-blocker存在:禁用广告白名单,不让网站的任何广告显示;禁用警告删除列表,一旦广告被拦截允许显示警告消息;删除EasyList中针对anti-blocker的规则。这样做的目的是让反广告拦截器软件充分生效,以便观察和研究。
为了保证精确对齐,使用堆栈信息,这两个条件都必须满足:
1、两个轨迹中所有语句的调用堆栈完全匹配
2、所有语句的标识符完全匹配
因为外部因素可以导致一个脚本出现不同执行轨迹,比如因为时间,随机资源等原因。为了避免无关原因造成的误判。作者采用冗余执行的方法,来克服测量中白噪声问题。在实验中,作者生成3组positive轨迹,3组negative轨迹来检测。测量一个网站所需要时间:因为浏览器加载扩展后变慢,所以每次等待20秒后停止追踪以确保网站完成加载。这样运行一次差异执行分析平均大约3分钟(positive和negative各测量三次)。
实验中使用一个32核心的服务器,在14个小时多一点就能处理10k个网站。因此利用此机器,系统分析Alexa top-10K一天时间就能完成。
小规模真实数据集测试
该数据集(2017.2)包含686个安装anti-adblockers的网站,对此列表人工重新分析从中挑出428个网站,这些网站都部署anti-adblockers,并对adblockers能做出明显的反应。又从中人工选择挑100个不包含广告,也没有部署反广告拦截器软件的网站。
测试表明系统达到86.9% (372/428) 准确率,0% 误报率;100 labeled negative websites, 没有一个网站被错认为部署anti-adblockers。
大规模Alexa Top 10k测试
测试结果表明30.5%网站部署anti-adblocker。1238个使用if/else,473个使用选择表达式,1344个包含两者。反广告拦截器数量比早先报道的要多,并不断在增加。作者分析原因指出前期研究只关注有明显反应的情况,漏掉slient report。结果显示排名越靠前网站,部署的越多。作者分析原因指出可能广告是提供免费在线信息和服务网站的重要收入来源。作者对anti-adblockers脚本进行分析,指出仅有422个网站使用自己的反广告拦截软件,其它2219个均采用第三方脚本(详见表格)。13个第三方anti-adblockers脚本,Google脚本是最流行的,YouTube次之,Taboola,PageFair脚本使用者也很多。13个脚本中9个占了部署anti软件的top-10k中1/3 ,悄悄检查adblockers和报告给后台服务器。这些anti脚本都检测DOM元素,大部分被检查的元素都是诱饵和real ads。
表一 基于不同源码的Anti-adblocker脚本的主要来源
作者提出两种帮助adblockers来抵制anti-blocker的方法。
1、JavaScript Rewriting
它强制条件表达式取值,转向执行不存在adblockers情况对应的分支语句。在之前的有差异的执行分析了解到,根据adblockers软件是否存在,anti脚本执行不同的分支语句。因此作者的idea就是,强制改变条件判断语句的值,转到到没有广告拦截的语句分支执行。这样做可以有效避免任何反广告拦截器的逻辑。
Rewriting只针对特定分支,而不是全部语句。一般都是条件判断语句。有两种方法来重写一个分支中的条件,1.用希望的输出替换原来条件语句 2.在条件语句末尾添加一个bool值,改变语句输出。方法1禁止条件原来条件语句执行,方法2不允许原来函数被调用。方法1不执行原来方法,可能会影响其余代码执行。比如,一个变量是在该函数定义的,缺少后可能引发未定义变量导致的崩溃。方法2可以避免这个问题,论文倾向采用它。当对齐a/b两个执行痕迹发现存在多重嵌套分支,决定重写全部分支输出或者部分,就变得非常重要了。 论文倾向重写外层条件。因为重写较低层可能会潜在引起功能崩溃。利用true让条件语句为真,让反广告拦截软件以为不存在拦截器。在外围改变可避免内部修改潜在问题。
测试及结果讨论
JavaScript重写方法对已知部署anti-adblockers软件并作出明显回应的网站测试,该系统成功规避了352(82.2%)。重写后明显没有了警告和弹出消息。这种方法有一些缺点和局限性。首先它需要一个中间代理,不能作为浏览器扩展立即应用。其次它的介入容易引发网页功能崩溃。
2、API Hooking
API Hooking主要是拦截应用程序对API的调用,从而改变(扩展)API的原有行为,常用代码覆盖来实现。作者注意到发布者调用API来检测页面状态,可以被浏览器扩展截获和修改,这使得可以进行注入代码来操作页面对象。与检测adblockers有关的有两种资源:DOM元素;与元素不相干关的变量。通过注入脚本可以截获取得的元素。如果对象被认定是一个诱饵或者真实广告,通过简单返回一个伪造的对象。之后当检查对象尺寸,按照前期分析得到数据回复一个值就行。但是如果对象是动态生成,就很难确定是否是一个对象,需要运行时更多监视。如果检测的变量与DOM元素无关,唯一可能性就是与JavaScript拦截脚本有关。这种情况,有个全局变量定义在和广告有关的脚本中。如果脚本被拦截,则变量成了未定义,因此引发广告拦截器检测(就是说,该脚本无效,说明ad-blocker存在并起作用)。幸运的是如果变量是全局并直接访问浏览器内置window对象,可以用一条语句来拦截它,返回任何预期值(表示没有adblocking)来通过检测。如果是嵌套在其它对象中的变量,则不能拦截它的访问。作为变通方法,作者提出让与广告相关的脚本加载,而不是拦截它,依赖其它广告拦截规则删除任何注入的广告(毕竟广告已经插入DOM树中)。如果与广告相关脚本没有注入任何广告,只是提供诱饵定义了一些变量。不拦截他们,实际上已经避免了anti-blocker广告拦截。
测试结果讨论
通过实施了一个概念性的Chrome扩展,让它工作在随机选择的的网站子集(都是真实数据集;5个流行第三方JavaScript(悄悄报告),5个用户自行开发或不流行脚本(悄悄报告),5个有明显响应),该方案工作非常好,抵御了所有网站。成功避免了anti-blocker,特别是总是成功避免了警告消息或者改变了报告消息。作者发现8个网站检查DOM元素的属性,7个网站检查变量值。7个中,6个检查全局变量。
基本上讲,API Hooking方法操纵DOM元素或者与被拦截的与ad相关的脚本。与JavaScript重写相比,它将更精确,更少的副作用。
工作的局限性
在论文结尾部分,作者从三个方面讨论了当前工作的局限性。
1、系统实施的不完整。不能覆盖所有的分支,特别是潜在的分支操作。
2、有差别的执行踪迹分析系统鲁棒性。网速和负载的波动会干扰系统,甚至导致分析失败;随机性(行为随机性和内容随机性)会影响系统。
3、反广告软件规避的鲁棒性。JavaScript rewriting 很难估计改变代码带来的影响;部署需要中间人代理;API hooking,与JavaScript rewrite相比,更准确,带来副作用更小。但是它依赖DOM元素的准确发现。