I got a call from my friend, let's call him Erik, he was having trouble with his business website. Erik's not technical, but he has a Drupal site and he knows I work with Drupal so he occasionally asks me web-related questions. Today's question was, "Are you able to fix this?"
Disclaimer! Chromatic is in no way involved with this tale of web woe. In fact, Chromatic is in the business of making sure such tales don't exist.
The trouble he was having was fairly simple, his site was broken. The only thing that displayed was an ugly-looking PHP error:
Parse error: syntax error, unexpected end of file in /path/to/his/drupal/website/modules/block/.vb4221fe.ico : eval()'d code on line 1
I was intrigued. What the heck is this vb4221fe.ico
file in the block module folder? It's certainly not part of Drupal.
I got access to his web hosting control panel and had a look around. It only took a few seconds to find that the Drupal installation was infested with strange .ico
and .php
files, including index.php
files in folders that should not have them, e.g. /sites/all/index.php
.
I deleted the offending .ico
file referenced in the above parse error and magically the site was back up and running. Mission accomplished!
Okay, mission not quite accomplished. Clearly the site (it's running Drupal 7.59) has been compromised and is infested by some kind of malware. I know these things happen, but I have never actually seen one in the wild. What is actually going on?
I looked at the contents of a rogue index.php
file and all it contained was a call to include one of the strange .ico
files, using octal-encoded characters to obfuscate the file path.
<?php
@include "\057path/to/his/drupal/website/mo\144ules/block/.vb\0642\0621fe.ic\157";
?>
The above line is the equivalent of
<?php
@include "/path/to/his/drupal/website/modules/block/vb4221fe.ico";
?>
Next I looked inside that .ico
file, which had obfuscated php inside, it was like looking at loads of gobbledy-gook, with random comment strings interspersed between actual commands.
This is only a partial copy/paste of the .ico
file to give you an idea.
<?php
$_pxwhi0 = basename/*a3*/(/*ug75*/trim/*57zed*/(/*z*/preg_replace/*8du*/(/*mt*/rawurldecode/*hk*/(/*jelq*/"%2F%5C%28.%2A%24%2F"/*3gn*/)/*d*/, '', __FILE__/*iwbl*/)/*tq1d*//*ejqa*/)/*87*//*x4rq*/)/*c8vn*/;$_3fomp2 = "GSC%1F%13%5C%5DPZ%40%0C%07G%09F%17EW";
?>
In another randomly named and situated php file (e.g. hgjd5hd.php
) I found a string variable and this crazy array which is built by referencing characters in the string variable.
<?php
$dnlrjpx = '4\'2*yo07be1v8H_xta9iu#6frlcnks-gdp3m';
$hpempw = Array();
$hpempw[] = $dnlrjpx[13].$dnlrjpx[3];$hpempw[] = $dnlrjpx[34].$dnlrjpx[2].$dnlrjpx[0].$dnlrjpx[9].$dnlrjpx[10].$dnlrjpx[9].$dnlrjpx[8].$dnlrjpx[8].$dnlrjpx[30].$dnlrjpx[7].$dnlrjpx[2].$dnlrjpx[17].$dnlrjpx[0].$dnlrjpx[30].$dnlrjpx[0].$dnlrjpx[18].$dnlrjpx[23].$dnlrjpx[8].$dnlrjpx[30].$dnlrjpx[8].$dnlrjpx[17].$dnlrjpx[0].$dnlrjpx[12].$dnlrjpx[30].$dnlrjpx[2].$dnlrjpx[0].$dnlrjpx[23].$dnlrjpx[17].$dnlrjpx[23].$dnlrjpx[2].$dnlrjpx[6].$dnlrjpx[18].$dnlrjpx[34].$dnlrjpx[6].$dnlrjpx[9].$dnlrjpx[22];$hpempw[] = $dnlrjpx[21];$hpempw[] = $dnlrjpx[26].$dnlrjpx[5].$dnlrjpx[20].$dnlrjpx[27].$dnlrjpx[16];$hpempw[] = $dnlrjpx[29].$dnlrjpx[16].$dnlrjpx[24].$dnlrjpx[14].$dnlrjpx[24].$dnlrjpx[9].$dnlrjpx[33].$dnlrjpx[9].$dnlrjpx[17].$dnlrjpx[16];$hpempw[] = $dnlrjpx[9].$dnlrjpx[15].$dnlrjpx[33].$dnlrjpx[25].$dnlrjpx[5].$dnlrjpx[32].$dnlrjpx[9];$hpempw[] = $dnlrjpx[29].$dnlrjpx[20].$dnlrjpx[8].$dnlrjpx[29].$dnlrjpx[16].$dnlrjpx[24];$hpempw[] = $dnlrjpx[17].$dnlrjpx[24].$dnlrjpx[24].$dnlrjpx[17].$dnlrjpx[4].$dnlrjpx[14].$dnlrjpx[35].$dnlrjpx[9].$dnlrjpx[24].$dnlrjpx[31].$dnlrjpx[9];$hpempw[] = $dnlrjpx[29].$dnlrjpx[16].$dnlrjpx[24].$dnlrjpx[25].$dnlrjpx[9].$dnlrjpx[27];$hpempw[] = $dnlrjpx[33].$dnlrjpx[17].$dnlrjpx[26].$dnlrjpx[28];
?>
When I did a var_dump()
of the crazy $hpempw
array, this is what I got:
array (size=10)
0 => string 'H*'
1 => string '324e1ebb-72a4-49fb-ba48-24faf20930e6'
2 => string '#'
3 => string 'count'
4 => string 'str_repeat'
5 => string 'explode'
6 => string 'substr'
7 => string 'array_merge'
8 => string 'strlen'
9 => string 'pack'
Finally something familiar looking. Good ole' php functions. Later in the code the crazy array was used to call these functions, so instead of using array_merge($arr1, $arr2)
, the code used $hpempw[7]($arr1, $arr2)
etc. (Note that the value of $hpempw[7]
is array_merge
.)
I didn't take the time to figure out exactly what was going on, but my guess is that this must be cryptocurrency mining malware that does its thing while trying to make sure it's difficult to figure out what that thing is. (I deleted a bunch of malware files that day and on subsequent checks discovered that in time they get regenerated.)
I called Erik and broke the bad news that his site has a malware infestation.
Erik: "What's it going to take to fix?"
Märt: "Well, just deleting these bad files won't do it, they regenerate themselves. You need a clean copy of your site reinstalled on a clean system to make sure there is no malware lurking about. I know you don't use version control, but do you have site back-ups? I'm wondering if there is a clean set of files we can use."
Erik: "Ummm, I'm not sure. I'd have to ask."
Märt: "Who maintains your site?"
Erik: "I was hoping you could."
[Crickets]
This is where the thrilling challenge of solving the infestation puzzle was subdued by the despair of a site with no maintenance plan. Like an untended garden or Sideshow Bob's hair, chaos eventually ensues.
Here at Chromatic, we fight the chaos by using version control and automated tools such as GitHub’s Dependabot to let us know when updates are available. Following the Drupal security team’s advisories and its Twitter stream is also crucial. And if disaster were to strike, we can rely on the daily database back-ups we create to restore a site quickly.
What about your site? If disaster strikes, are you confident that you can restore it with minimal fuss or will you be in a tight spot like Erik, confused and not knowing where to turn? At Chromatic, we choose conscientiousness and diligence, and we hope you do too.