An extensible backdoor.
- Plugins can be added, removed and executed.
- Immediate code eval.
- Some recon info: PHP version, its own version.
- Password authenticated access.
I got 71 downloads of essentially the same code from 2018-03-15 to 2018-05-30. All code purports to be version "1.0-2".
Higher-numbered versions exist. You can get version 2.0-1 from pastebin. I have reversed a version v2.0-1 instance, to see what changes people have made.
Version 2.0-1 is the earliest occurance on pastebin, at January 9, 2018.
I got another spin on version 2.0-1 via email. It's in the ico directory. A January, 2019 analysis of another, nearly identical, 2.0-1 installation.
On 2018-06-18, my WSO honey pot caught a batch of 5 similar, more primitive backdoors. Those primitive backdoors probably shared an ancestor with these.
A less specific analysis of apparently the version 2.0-1 backdoor. Apparently the attackers in this case actually used the plugin feature.
See also Backdoor installation campaign for what seems like an extension or continuation of the "more primitive" backdoor attacks.
On 2019-05-09, my honey pot caught a v3-01 instance, very reminiscent of v2-01 instances.
Attacker wanted to download to the "RC" action of a WSO (Web Shell by oRb) instance. Code downloaded to the WSO part of my WordPress honey pot.
Every request made of the fake WSO arrives with a WSO cookie. The md5 hash in the cookie corresponds to "nhzgrf", a common WSO password.
The attacker did try a variety of possible WSO URIs:
/wordpress/wp-content/plugins/revslider/temp/update_extract/revslider/db.php
/wordpress/wp-content/plugins/wp-mobile-detector/cache/db.php
/wordpress/wp-content/uploads/wpallimport/uploads/f2af55ff3d3404c81a296c997348e8d1/db.php
/wp-content/plugins/revslider/temp/update_extract/revslider/info.php
/wp-content/plugins/wp-db-ajax-made/wp-ajax.php
/wp-content/plugins/wp_bing/system.php
/wp-content/themes/sketch/404.php
These are all URIs that attackers believe are WSO or other web shells.
IP address | Download time | DNS name |
---|---|---|
212.63.109.204 | 2018-03-15T03:13:33.000-0600 | dns109204.phdns10.es |
85.128.142.46 | 2018-03-16T07:59:31.000-0600 | akl46.rev.netart.pl |
119.59.120.3 | 2018-03-17T08:16:53.000-0600 | N/A |
194.54.82.78 | 2018-03-18T02:01:56.000-0600 | dolina-82-78.dolina.rx-name.net |
162.241.218.136 | 2018-03-19T06:34:18.000-0600 | box5566.bluehost.com |
184.168.200.228 | 2018-03-20T02:06:41.000-0600 | p3plcpnl0168.prod.phx3.secureserver.net |
121.127.254.2 | 2018-03-21T06:43:55.000-0600 | N/A |
103.95.197.62 | 2018-03-22T02:34:12.000-0600 | host62.cloudzone.vn |
162.241.226.121 | 2018-03-23T05:26:53.000-0600 | box5342.bluehost.com |
173.201.196.148 | 2018-03-24T03:03:45.000-0600 | p3nlhg290.shr.prod.phx3.secureserver.net |
173.201.196.110 | 2018-03-25T04:27:29.000-0600 | p3nlhg302.shr.prod.phx3.secureserver.net |
118.123.16.54 | 2018-03-26T03:49:21.000-0600 | N/A |
148.72.232.52 | 2018-03-27T02:49:23.000-0600 | sg2plcpnl0173.prod.sin2.secureserver.net |
85.128.142.58 | 2018-03-28T03:11:08.000-0600 | akl58.rev.netart.pl |
198.71.239.47 | 2018-03-29T07:30:25.000-0600 | a2nlwpweb045.prod.iad2.secureserver.net |
113.212.162.149 | 2018-03-30T08:21:34.000-0600 | mail.bias-samudra.com |
66.147.240.198 | 2018-03-31T02:10:45.000-0600 | host398.hostmonster.com |
207.246.249.205 | 2018-04-01T05:19:51.000-0600 | N/A |
50.63.194.158 | 2018-04-02T03:56:41.000-0600 | p3nlhg1432.shr.prod.phx3.secureserver.net |
184.168.27.12 | 2018-04-03T04:14:59.000-0600 | p3nlhg836.shr.prod.phx3.secureserver.net |
91.200.60.68 | 2018-04-04T03:37:00.000-0600 | wezom3.linevps.net |
158.69.8.115 | 2018-04-04T01:10:37.000-0600 | N/A |
160.153.147.139 | 2018-04-05T07:28:33.000-0600 | n3nlwpweb011.prod.ams3.secureserver.net |
182.50.135.55 | 2018-04-06T03:45:10.000-0600 | sg2plcpnl0107.prod.sin2.secureserver.net |
212.174.107.137 | 2018-04-07T04:05:25.000-0600 | lpm3-ppp137.verisoft.net.tr |
178.210.90.90 | 2018-04-08T04:06:56.000-0600 | tproxy1102.nic.ru |
198.71.238.6 | 2018-04-09T05:56:10.000-0600 | a2nlwpweb054.prod.iad2.secureserver.net |
198.71.238.11 | 2018-04-10T01:23:34.000-0600 | a2nlwpweb059.prod.iad2.secureserver.net |
50.62.176.242 | 2018-04-11T05:51:13.000-0600 | p3plcpnl0726.prod.phx3.secureserver.net |
184.168.27.140 | 2018-04-12T04:20:50.000-0600 | p3nlhg913.shr.prod.phx3.secureserver.net |
50.62.208.38 | 2018-04-13T03:37:27.000-0600 | p3nlwpweb053.shr.prod.phx3.secureserver.net |
178.210.90.90 | 2018-04-14T04:25:48.000-0600 | tproxy1102.nic.ru |
112.78.2.202 | 2018-04-15T01:06:48.000-0600 | N/A |
42.98.72.36 | 2018-04-16T07:19:46.000-0600 | 42-98-72-036.static.netvigator.com |
209.175.164.195 | 2018-04-17T07:50:11.000-0600 | N/A |
122.114.43.148 | 2018-04-18T04:28:21.000-0600 | N/A |
78.24.221.82 | 2018-04-19T08:46:54.000-0600 | legendgroup.fvds.ru |
162.241.216.242 | 2018-04-20T10:03:16.000-0600 | box5442.bluehost.com |
149.202.83.87 | 2018-04-21T07:12:55.000-0600 | ns3019045.ip-149-202-83.eu |
168.167.251.215 | 2018-04-22T08:32:53.000-0600 | N/A |
132.148.104.129 | 2018-04-23T01:29:39.000-0600 | p3nlhg2080.shr.prod.phx3.secureserver.net |
50.87.144.119 | 2018-04-25T07:23:11.000-0600 | gator3084.hostgator.com |
185.26.122.29 | 2018-04-25T05:55:34.000-0600 | serv29-26.hostland.ru |
107.180.120.64 | 2018-04-26T06:32:10.000-0600 | a2nlwpweb145.prod.iad2.secureserver.net |
151.1.184.134 | 2018-04-27T07:40:37.000-0600 | N/A |
95.58.31.174 | 2018-04-28T04:01:05.000-0600 | N/A |
210.61.49.127 | 2018-05-06T03:18:37.000-0600 | 210-61-49-127.hinet-ip.hinet.net |
182.50.151.55 | 2018-05-07T03:13:32.000-0600 | sg2plcpnl0074.prod.sin2.secureserver.net |
45.40.166.153 | 2018-05-08T05:35:46.000-0600 | p3nlhg2063.shr.prod.phx3.secureserver.net |
213.142.130.42 | 2018-05-09T01:49:54.000-0600 | lhost452.websahibi.com |
62.210.185.4 | 2018-05-10T05:48:22.000-0600 | nat-dc2-2.online.net |
207.148.119.51 | 2018-05-11T05:03:21.000-0600 | 207.148.119.51.vultr.com |
198.71.238.17 | 2018-05-12T06:42:15.000-0600 | a2nlwpweb064.prod.iad2.secureserver.net |
198.71.239.45 | 2018-05-13T06:55:18.000-0600 | a2nlwpweb044.prod.iad2.secureserver.net |
188.187.190.59 | 2018-05-14T01:08:57.000-0600 | 188x187x190x59.static-customer.yola.ertelecom.ru |
34.197.246.236 | 2018-05-15T03:26:38.000-0600 | ec2-34-197-246-236.compute-1.amazonaws.com |
50.62.177.187 | 2018-05-16T03:29:19.000-0600 | p3plcpnl0887.prod.phx3.secureserver.net |
50.87.144.56 | 2018-05-17T01:58:28.000-0600 | gator3037.hostgator.com |
107.180.120.67 | 2018-05-18T05:23:55.000-0600 | a2nlwpweb147.prod.iad2.secureserver.net |
107.180.120.45 | 2018-05-19T08:28:42.000-0600 | a2nlwpweb128.prod.iad2.secureserver.net |
198.71.239.9 | 2018-05-20T04:18:36.000-0600 | a2nlwpweb007.prod.iad2.secureserver.net |
182.50.130.35 | 2018-05-21T05:26:24.000-0600 | sg2nw8shg139.shr.prod.sin2.secureserver.net |
81.91.86.129 | 2018-05-22T05:43:57.000-0600 | rs29.web4u.cz |
82.209.199.218 | 2018-05-23T03:06:19.000-0600 | metropr.by |
79.170.40.55 | 2018-05-24T04:11:03.000-0600 | web55.extendcp.co.uk |
176.31.236.164 | 2018-05-25T07:14:22.000-0600 | tarkett-2013.hosting.clever-age.net |
192.185.83.194 | 2018-05-26T03:15:28.000-0600 | marzal.websitewelcome.com |
198.71.238.9 | 2018-05-27T02:51:42.000-0600 | a2nlwpweb056.prod.iad2.secureserver.net |
184.168.46.128 | 2018-05-28T02:35:37.000-0600 | p3nlhg738.shr.prod.phx3.secureserver.net |
195.74.38.66 | 2018-05-29T10:27:34.000-0600 | cl-05.atm.binero.net |
184.168.152.99 | 2018-05-30T03:54:00.000-0600 | p3nlhg663.shr.prod.phx3.secureserver.net |
Looks like they have a fondness for GoDaddy (secureserver.net) machines.
p0f3
says that most of these addresses were Linux machines
at the time of download, but there's a sprinkling of Windows and FreeBSD, too.
The script extract
contains the entire process,
which goes something like this:
-
For each file named
files/*.php.file
:- Create a file with
<?php
at the beginning, and the contents of the original file, with "print" substituted for "eval" - Append a line with "rawurldecode()" or "base64_decode()",
depending on what the encoding in
files/*.php.file
- Create a file with
-
Execute the files created in step 1, substituting "print" for "eval" in the output.
-
Execute the files from step 2, saving the contents in
final/
The dropper is the obfuscated code in files named files/*.php.file
The example final code is in extendable.php
I'd give this code a thumbs-up. It tries to hide mostly in plain sight, but with enought obfuscation that visual inspection by a human would fail. Network traffic can be inobvious, as it is is XOR-ed and base64-encoded. Command traffic can arrive via HTTP cookie, or HTTP POST. The use of a cookie isn't unprecedented, but it would obscure the traffic.
The plugin architecture allows for maximum flexibility. It does have downsides, in that an attacker must install a plugin first, then execute it, causing more than one burst of suspicious traffic.
Any of the files named files/*.php.file
contain similar PHP.
This PHP would get eval'ed by function actionRC()
in a real WSO web shell.
Stylistically, the dropper code seems very consistent.
The dropper has function names prefixed with "fr_".
All function names are something like "fr_CamelCase"
Every variable name is snake_case.
Braces placement is mostly regular, albeit a little unusual.
Left- and right-braces mostly exist on their own lines,
rather than appearing at the end of an "if" or "while" statement line.
A few instances of if (some condition) {
do exist,
possibly indicating a second author.
The code eval'ed by function actionRC()
should detect if either Joomla, Drupal or WordPress
exists on the host running WSO.
The code contains names of a few files that exist in any install of each of the 3 CMS,
for instance WordPress always has wp-settings.php
and wp-config.php
.
Finding one of the file names indicates the corresponding CMS is installed.
For each of the files of the CMS that the dropper decides it finds,
it first tries to put a PHP include();
directive in the CMS file,
creating a randomly-named file to include.
That randomly named file contains the obfuscated backdoor code.
If it did not succeed in adding "include" directives to any of the files of the CMS files, it tries to put the obfuscated backdoor code in the files with an "eval" surrounding it. Very thorough!
It looks like the dropper code output would constitute something like:
URL#http://example.com#FULL
for successful install(s), either with an include or with an eval, and it would report
URL#http://example.com#RESTRICTED
for failed installs. It outputs nothing if it finds no CMS it recognizes, or if it can't write to the CMS files it finds.
The extensible backdoor has function names prefixed with "cs_".
There are some apparent duplications with the dropper,
such as fr_GetHost
in dropper, cs_GetHost
in the back door code.
The back door has a mix of function name styles,
some in camel case (cs_GetCommonStorage()
) some in snake case (cs_plugin_load()
).
Variable names are all "snake_case".
Left- and right-bracket placement is completely consistent throughout.
It looks maybe like the dropper and back door had independent, but cooperating, authors.
Or maybe the back door author just borrowed available code for the dropper,
but did some rewriting to conform to their coding standard.
The backdoor has 5 clearly delineated operations:
- Return host info: PHP version, backdoor version, XOR-key
- Eval: immediate eval of PHP code sent to the backdoor. The most traditional backdoor function.
- Add a plugin.
- Remove a plugin.
- Run all the plugins. There is no way to run just one plugin, it's all or nothing.
You need to supply a password/XOR key for every operation except running all the plugins.
The code looks like the author maybe had an idea of running just one
plugin, but it never got written, even in version 2.0-1:
function cs_plugin_load()
has a formal argument, $name
,
used to decide what plugin to run.
Unfortunately, cs_plugin_load()
does not get invoked with a plugin name,
nor does the code support getting a plugin name to the back door.
This wasn't fixed in the 2.0-1 code.
Plugin code is stored encrypted,
one file per plugin,
in a directory created by the back door.
The directory name is one of a list
("options", "views", "pages", "sessions", "stats", "users", "articles", "dump", "headers", "libs")
in the back door code, chosen by string length of the host name the back door exists on.
Plugin code gets XOR-ed with the host name, so it's not immediately obvious that file
options/0fea6_b90f2694d4fbf770be56dbe9ba1e5fa0
contains PHP code.
Either way the back door code gets installed (include(random file name)
or inside an eval()
),
the plugin code would seem fairly innocuous.
I wrote a client to understand the cipher used, to verify that the backdoors work, and understand the code better. This client can invoke any of the backdoor functions. Any immediate-eval code or plugin code gets taken from a command line parameter, so it's inconvenient to add plugins. It should have an option to read plugins from files.
Out of 71 install attempts, the attacker sent me 41 different password/XOR keys. Just in case this is interesting to anyone:
08daac77-cdf8-4a48-bdaf-3c0e852c89b8
1a306d05-103d-4aac-952e-008871d6017a
1e87ab0f-acf1-4257-91fd-327300af749e
208842cd-e247-4882-aaa3-c881ee68e6b8
265c2857-4559-48e7-bf55-8991a1ecefdb
2802a388-982b-485a-a95a-32935c85945b
2c327d6b-188a-4cc1-9f51-099c5ceaf0f6
34d11daa-f6b5-4605-93f3-b8646febbd91
39da6ea2-8eab-4da6-bcae-0c3bee011999
557a307c-67b4-4c5b-9b73-bf8cbccc674c
615be3f0-a7d4-4bfc-946b-88fbc69f41c9
68468faf-3870-4b43-80e8-2988631799c2
69557b65-8b17-4f7f-b125-7ee49fe550f1
6ae4dfcd-ed62-42c7-80c2-b243de73efaf
6fd124d9-030f-418a-bfe6-9387dfa6e1c9
7018d16b-3efc-4a51-9426-4ac14509454b
70aa4fe9-b7d4-4f3a-a7bc-d38fecfffc99
728abe65-22d5-4a65-90ea-708ecb84af0c
72bff547-acd6-4a7e-9e3a-08f17d7259dd
7375f6b0-11a5-4e32-88d1-a35f30c5cf0b
863257b6-f98d-4e7a-bbba-773fda43c2e3
8dbdf9e9-d81d-48d5-8e2f-8e3658a65059
8f98f4d8-072e-4aa3-93a9-50bbb64a34a4
9a2fc75d-6ae2-42c0-9b70-1cce3d291bd9
9b3d3f5d-680a-4184-90f2-57b8985c188f
9d77d409-8a82-4adf-9407-9debffedab9d
a5d4ba8f-1065-423f-b9cb-f26351d33019
a801fcd0-c240-42a1-8ee9-396d1e89e06d
b405656c-7687-4672-9f30-6b4642901f97
b6f6b857-a137-4fe4-9f6e-0aa7bba96738
b95ef13a-be29-47bb-ac93-bd99740fbc6f
bfda478b-0412-41ef-8dfe-5510aa57ecd6
cca7f80a-a7a8-45e1-9c30-578704560790
d2a77a85-6712-4c43-bc0d-492e8f127b0f
dd8a5d6f-320e-4bbf-b625-decdd0a941c6
e340cca1-52db-41d1-a20f-a12ec7ec189e
ee8a7011-79ad-40f4-90af-91a7be1b7999
ee909dc0-3cbc-4e60-8d26-897fbe30a870
f722eee1-755e-4f1f-a999-410d0dbad878
f8f4d6f3-279a-438c-a89f-2283ac374cb7
f9cd46d4-75a9-4ef5-85ae-96f278b3724f