I have never learned more about JavaScript and the web than when I've reverse engineered malicious advertisements.
My most recent case study is an advert that knows when and how to hide itself from the browser's JS console. I stumbled across this script during a routine check of a certain high traffic website. I noticed some particularly high CPU usage on a resting landing page, so I opened the console to investigate. I was greeted with the log output Console was cleared
just as the CPU mysteriously calmed down. How conveeeeeenient!
Without boring anyone with the tedium of decoding obfuscated JS, this is the essence of the script's console detection:
var probe = new Image();
probe.__defineGetter__('id', function() {
probe.style.display = 'none';
});
console.log(probe);
var touchImage = setInterval(function() {
console.log(probe);
}, 60 * 1000);
var pollImage = setInterval(function() {
console.info('probing for console');
if (probe.style.display === 'none') {
hideTheBodies();
clearInterval(touchImage);
clearInterval(pollImage);
}
}, 1000);
var hideTheBodies = function() {
console.clear();
// stop the rest of the suspicious activity
};
Here we have an object probe
, a looping event that logs it to the console every minute, and another looping event that tests probe
for a specific style property every second. The function that makes the test return true is attached to its id
property getter.
This is a browser-specific exploit that works in Chrome and Safari, taking advantage of the fact that when stringifying a DOM node to the console, the browser will access the node's id
property. Even though the image is logged immediately and every minute thereafter, it's only actually stringified when the console output becomes visible, satisfying the boolean test and triggering the cover-up.
You can try this yourself in Chrome or Safari:
i = new Image() // undefined
i.__defineGetter__('id', function() { i.name = "Horace" }) // undefined
i // <image name="Horace">
This would work if i
was any DOM node like document.createElement('div')
. It would also work if you attached the getter function to the className
property. If you tried this in Firefox, though, you would not change the name property of the image until you explicitly invoked i.id
. If I find a way to make this exploit work in Firefox, I sure as hell won't tell anyone.
Now that we know how the script hid its activity, what the hell was it doing in the first place? If this were 5 years ago, I might have suggested Bitcoin mining. To know exactly what was going on requires cross referencing this script with a few others that it loads, which I haven't done yet, but I suspect it was concurrently fetching and playing 9 other embeddables in order to rack up video impressions that the end user doesn't even see. Evil and genius.
The company who supplied this script is Virjet, which also goes by Ceph and Piacy, and they can get fucked in both eyeballs.