SoloRTB generates auto-clicks with JS code

Today we are investigating a suspicious code which is involved into a process of impressions delivering by SoloRTB/NewBornTown. In this article we are checking the code and demonstrate how it works.

We have found that they use a code which begins loading a video media file from a VAST code, interrupts downloading obviously to avoid high traffic consumption, randomly generates fake tracking events and clicks emulating normal user activity. Similar things are performed for banner advertisements.

Initially we have noticed impressions with referrers like:

http://v.rollupuh.com/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

Considering that this code may be removed after this article is published, we have saved it in archive.org:

https://web.archive.org/web/20190829045808/http://v.rollupuh.com/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

So, what is referrer? In general referrer is an URL of a webpage from which the request was received. When pixel, image or a script is loaded into web view or browser, the server receives the referrer in a header. So, in our situation referrer is an address of a webpage where the advertising code was rendered.

Now let’s open the source code of this webpage and check how it works and what it does (bellow we’re referring to the version saved in webarchive.org).

We’re starting from the beginning of call chain:

Line 902:

SoloRTB.Request(req_param, statusChange);

The code requests available advertisement. When a response is received a function statusChange (Line 828) is called. This function processes the response and in case of several ads available it changes the banners (Line 863).
A function do_adx_ad is called for served ad code (Lines 844-856). This function (Line 747) renders the ad depending on its type: 0 - video, 4 - banner, 3 - native (native seems not working).

Now let’s check the code at line 705 in detail:

Line 705:

setTimeout(function(){
if(Math.random() > 0.03){
return;
}

if(is_vast){
return;
}


var elements = document.getElementsByTagName("iframe")[0].contentWindow.document.getElementsByTagName("a");
for(var i = 0, len = elements.length; i < len; i++) {
try{
var iframe = document.createElement('<iframe src="'+elements[i].href+'" width=0 height=0 scrolling="no" style="display:none"></iframe>');
document.body.appendChild(iframe);
}catch(e){
var iframe = document.createElement('iframe');
iframe.setAttribute("src", elements[i].href);
iframe.width = 0;
iframe.height = 0;
iframe.scrolling = "no";
iframe.style = "display:none";
document.body.appendChild(iframe);
}
}

for(var i = 0, len = click_urls.length; i < len; i++) {
try{
var iframe = document.createElement('<iframe src="'+click_urls[i]+'" width=0 height=0 scrolling="no" style="display:none"></iframe>');
document.body.appendChild(iframe);
}catch(e){
var iframe = document.createElement('iframe');
iframe.setAttribute("src", click_urls[i]);
iframe.width = 0;
iframe.height = 0;
iframe.scrolling = "no";
iframe.style = "display:none";
document.body.appendChild(iframe);
}
}
}, 2000);

It launches a function in 2 seconds. The function stops at line 28 in 97% cases in other 3% it moves on. It stops working for video (Line 32), so it works only for banners. At line 37-38 they find all links (a-tags) in the iframe where they have rendered the banner and create iframes with SRCs equal to URL of the links’ URLs. This code generates auto-click in 3% of impressions.

Now let’s sort out how it processes the video. At line 487 the code starts loading video and stops downloading (line 495) right after the first frame is loaded, so the file isn’t even loaded:

Line 495:

var oVideo=document.getElementById("video");

oVideo.setAttribute("src",v1Video.media_file);
var firstQuartileTime = Math.floor(v1Video.duration*0.25);
var midpointTime = Math.floor(v1Video.duration*0.5);
var thirdQuartile = Math.floor(v1Video.duration*0.75);

oVideo.addEventListener("loadeddata",function () {
v1Video.stopDownload(oVideo);

At line 498 they begin a loop which waits some time to generate standard video events with certain probability:

Line 497:

var lose = Math.floor((Math.random()*20)+10);
var interval = setInterval(function () {
if(updatetime===0){

creatTags("impression");
creatTags("event_start");
creatTags("event_createView");
}
if(updatetime===firstQuartileTime){
var one = Math.floor((Math.random()*100)+1);
if(one>=lose){

creatTags("event_firstQuartile");
}else {

clearInterval(interval);
return false;
}
}
if(updatetime===midpointTime){
var two = Math.floor((Math.random()*100)+1);
if(two>=lose){

creatTags("event_midpoint");
}else {

clearInterval(interval);
return false;
}
}
if(updatetime===thirdQuartile){
var three = Math.floor((Math.random()*100)+1);
if(three>=lose){

creatTags("event_thirdQuartile");
}else {

clearInterval(interval);
return false;
}
}
if(updatetime===v1Video.duration){
var end = Math.floor((Math.random()*100)+1);
if(end>=lose){

creatTags("event_complete");
var click = Math.floor((Math.random()*1000)+1);
if(click<=videoCtr){

creatTags("click_tracking");
location.href = v1Video.click_through_url;
clearInterval(interval);
}else {

clearInterval(interval);
return false;
}
}else {

clearInterval(interval);
return false;
}
}

The variable lose emulates random interruption of viewing the video by user. It’s a random value in range from 10 to 30. firstQuartile event fires if random value saved in variable one (from 1 to 101) is greater than lose. The same things are repeated for midpoint, thirdQuartile and complete events.

At line 122 they generate a click in 1.2% cases (random value in click from 1 to 1001 should be less or equal to videoCtr which is set to 12 at line 824).

And that’s all. We have prepared an environment so anyone can check how in works. We have saved the page containg the code to our server, and did the following modifications:

  1. replaced URLs of their servers with our fake backend which always responds with ads
  2. added console.logs to illustrate call chains
  3. changed ad type to request both video and banner ads

Now you may check by yourself by opening http://172.105.95.148:3000/solo_banner.html or http://172.105.95.148:3000/solo_video.html and pressing F12 in Chrome or Firefox to view the console output. After every refresh of the page you see the different results because they depend on random numbers.

We intentionaly made tracking links to respond with 404 errors, to make these requests clearly visible in the console output. Here are different outputs for video:

  1. A user has viewed the video fully but hasn’t clicked:

  1. A user has viewed the 1/4 of video:

  1. A user has viewed the 3/4 of video:

  1. If you’re lucky enough to catch auto-click (1.2%) and see the following:

As for banners, in 97% cases you see the following:

In other 3% you find an iframe with our test click URL (http://172.105.95.148:3000/click):

To ensure that v.rollupuh.com and r1.snnd.co belong to the same company, let’s replace the domain v.rollupuh.com with r1.snnd.co in the URL that we have found in the referrer and compare the codes:

http://r1.snnd.co/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

Saved version: https://web.archive.org/web/20190830025339/http://r1.snnd.co/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

The source codes of both pages are the same, so the domain of SoloRTB (r1.snnd.co) can serve the same fraudulent code. This domain has multiple A records:

1
2
3
4
5
6
7
8
9
r1.snnd.co.		599	IN	CNAME	pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com.
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 52.26.104.182
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 54.69.81.100
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 54.218.74.191
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 52.41.130.96
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 54.218.87.232
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 54.200.136.190
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 52.35.122.28
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 44 IN A 35.167.128.163

And they match the A records of the domain api.pingstart.com:

1
2
3
4
5
6
7
8
9
api.pingstart.com.	7199	IN	CNAME	pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com.
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 52.41.130.96
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 52.88.126.15
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 54.200.136.190
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 52.35.122.28
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 52.42.135.104
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 54.218.74.191
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 54.69.81.100
pingstart-v1-apps-alb-1860488418.us-west-2.elb.amazonaws.com. 59 IN A 54.218.87.232

Well, let’s now try to replace the domain v.rollupuh.com with api.pingstart.com and check the response:

http://api.pingstart.com/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

Saved version: https://web.archive.org/web/20190830033608/http://api.pingstart.com/v1/wad/r?_po=lninZVha0e0U0aoq1uox_tXei37TOqafifACCcOZZ6lMwIFX3YN3tEFqjYGZScQYhnl2gA6BrO2G_RBUAt_yoC3y9P9rxe41SwsKuzJmfi8IEflgznsG9zkvCva6hFTojdp7_VPZ5sw04IMZ1BxfE6o7X6xnMCojWVyC4

The domain api.pingstart.com also serves the fraudulent code and all the domains: api.pingstart.com, r1.snnd.co and v.rollupuh.com belong to the same company NewBorn Town.

Obviously, this code generates fake clicks and mostly likely fake impressions (at least video, because the media file is not loaded). This code can not be a bug or technical issue, it was specifically created to perform in such a manner.

Good luck!