mirror of
https://github.com/scr34m/php-malware-scanner.git
synced 2026-06-16 12:30:35 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55e75079df | ||
|
|
c9e4050b7d | ||
|
|
31fa36c82a | ||
|
|
ba466dc1ff | ||
|
|
201ab77516 | ||
|
|
46024eca5e | ||
|
|
a31cc18dc5 | ||
|
|
96806c69e9 | ||
|
|
42c2aad685 | ||
|
|
cad03dc3b4 | ||
|
|
c542a745e4 | ||
|
|
7ac65c0c8d | ||
|
|
5061e319e3 | ||
|
|
b2b2c4b081 | ||
|
|
26458d20af | ||
|
|
70edc4210d | ||
|
|
aec0f56af5 | ||
|
|
2e8b9c604f | ||
|
|
802ead97cc | ||
|
|
4666a101f9 | ||
|
|
e4755feeef | ||
|
|
920cf8a4c6 | ||
|
|
aa774f4330 | ||
|
|
cd1164dbb5 | ||
|
|
77ebd8abd7 | ||
|
|
29e6c73558 | ||
|
|
bf13288367 | ||
|
|
088c0761b3 | ||
|
|
18b06fc48b | ||
|
|
f1b8b89ca5 | ||
|
|
c6a52dc67e | ||
|
|
3b76a7270e | ||
|
|
f0bdb1f1e1 | ||
|
|
43876b337b | ||
|
|
1fad164790 | ||
|
|
f4d53e89d8 | ||
|
|
34ea02323b | ||
|
|
b74494a4f1 | ||
|
|
9624ec4403 | ||
|
|
335b13b7c4 | ||
|
|
78bee49176 | ||
|
|
cc0fdc7a9f | ||
|
|
ec8f9920ba | ||
|
|
5883c68f54 | ||
|
|
22b51a1ee3 | ||
|
|
2b1a0c1266 | ||
|
|
c495cc822c | ||
|
|
e9a45d4bdc | ||
|
|
21185202f3 | ||
|
|
195717d625 | ||
|
|
2973e55871 | ||
|
|
8b1994956e | ||
|
|
46faa31c74 | ||
|
|
d67a865bf0 | ||
|
|
b290826f82 | ||
|
|
8030cec89f | ||
|
|
9ec295f80d | ||
|
|
c1c71bd9ef |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idea/
|
||||||
|
whitelist.dat
|
||||||
|
vendor/
|
||||||
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM php:8.2-cli
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
git \
|
||||||
|
unzip \
|
||||||
|
libzip-dev \
|
||||||
|
&& docker-php-ext-install zip
|
||||||
|
|
||||||
|
WORKDIR /scanner
|
||||||
|
COPY . .
|
||||||
|
RUN chmod +x scan
|
||||||
|
|
||||||
|
ENTRYPOINT ["./scan"]
|
||||||
|
CMD ["/code"]
|
||||||
55
README.md
55
README.md
@@ -26,6 +26,7 @@ Usage: php scan.php -d <directory>
|
|||||||
-x --extra-check Adds GoogleBot and htaccess to Scan List
|
-x --extra-check Adds GoogleBot and htaccess to Scan List
|
||||||
-l --follow-symlink Follow symlinked directories
|
-l --follow-symlink Follow symlinked directories
|
||||||
-k --hide-ok Hide results with 'OK' status
|
-k --hide-ok Hide results with 'OK' status
|
||||||
|
-r --hide-err Hide results with 'ER' status
|
||||||
-w --hide-whitelist Hide results with 'WL' status
|
-w --hide-whitelist Hide results with 'WL' status
|
||||||
-n --no-color Disable color mode
|
-n --no-color Disable color mode
|
||||||
-s --no-stop Continue scanning file after first hit
|
-s --no-stop Continue scanning file after first hit
|
||||||
@@ -33,8 +34,10 @@ Usage: php scan.php -d <directory>
|
|||||||
-t --time Show time of last file change
|
-t --time Show time of last file change
|
||||||
-L --line-number Display matching pattern line number in file
|
-L --line-number Display matching pattern line number in file
|
||||||
-o --output-format Custom defined output format
|
-o --output-format Custom defined output format
|
||||||
-j --wordpress-version Version of wordpress to get md5 signatures
|
-j <version> --wordpress-version Version of wordpress to get md5 signatures
|
||||||
--combined-whitelist Combined whitelist
|
--combined-whitelist Combined whitelist
|
||||||
|
--custom-whitelist Loads whitelist from specified file and merge with existing
|
||||||
|
--disable-stats Disable statistics output
|
||||||
```
|
```
|
||||||
|
|
||||||
Ignore argument could be used multiple times and accept glob style matching ex.: "`cache*`", "`??-cache.php`" or "`/cache`" etc.
|
Ignore argument could be used multiple times and accept glob style matching ex.: "`cache*`", "`??-cache.php`" or "`/cache`" etc.
|
||||||
@@ -112,6 +115,22 @@ It is guaranteed that IF 'base64_decode' was present in the plain text code, the
|
|||||||
The presence of 'YmFzZTY0X2RlY29kZ' in a block of code may be because 'ase64_decod' was in the original code.
|
The presence of 'YmFzZTY0X2RlY29kZ' in a block of code may be because 'ase64_decod' was in the original code.
|
||||||
ote the missing edge characters which is due to bit misalignment and character bleed.
|
ote the missing edge characters which is due to bit misalignment and character bleed.
|
||||||
|
|
||||||
|
Using as library
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The scan.php perform a check, that it's called by commandline or not, so to use as library use different directory than scan.php it self.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once '../scan.php';
|
||||||
|
|
||||||
|
$scan = new MalwareScanner();
|
||||||
|
$scan->setFlagHideWhitelist(true);
|
||||||
|
$scan->setFlagHideOk(true);
|
||||||
|
$scan->run('../samples/test');
|
||||||
|
```
|
||||||
|
|
||||||
Resources
|
Resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -127,3 +146,37 @@ Licensing
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
PHP malware scanner is [licensed](https://github.com/scr34m/php-malware-scanner/blob/master/LICENSE.txt) under the GNU General Public License v3.
|
PHP malware scanner is [licensed](https://github.com/scr34m/php-malware-scanner/blob/master/LICENSE.txt) under the GNU General Public License v3.
|
||||||
|
|
||||||
|
Docker Usage
|
||||||
|
-----------
|
||||||
|
|
||||||
|
You can also run the scanner using Docker:
|
||||||
|
|
||||||
|
1. Build the image:
|
||||||
|
```bash
|
||||||
|
docker build -t php-malware-scanner .
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Scan a directory:
|
||||||
|
```bash
|
||||||
|
docker run -v /path/to/scan:/code php-malware-scanner -d /code
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, to scan a WordPress installation:
|
||||||
|
```bash
|
||||||
|
docker run -v /var/www/html:/code php-malware-scanner -d /code -j 6.4.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Common usage with flags:
|
||||||
|
```bash
|
||||||
|
# Show only infected files (hide OK status)
|
||||||
|
docker run -v /path/to/scan:/code php-malware-scanner -d /code -k
|
||||||
|
|
||||||
|
# Show comments for matched patterns
|
||||||
|
docker run -v /path/to/scan:/code php-malware-scanner -d /code -c
|
||||||
|
|
||||||
|
# Show MD5 hashes and continue after first match
|
||||||
|
docker run -v /path/to/scan:/code php-malware-scanner -d /code -m -s
|
||||||
|
```
|
||||||
|
|
||||||
|
The `/code` directory inside the container is where your files will be mounted for scanning.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"homepage": "https://github.com/scr34m/php-malware-scanner",
|
"homepage": "https://github.com/scr34m/php-malware-scanner",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.2.0"
|
"php": ">=5.3.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,4 +16,48 @@ opendns
|
|||||||
phishtank
|
phishtank
|
||||||
sophos
|
sophos
|
||||||
surfright
|
surfright
|
||||||
symantec
|
# symantec - removed because already a TLD too so generate many false positives
|
||||||
|
|
||||||
|
# SEO poison, pharmacy redirect
|
||||||
|
dealonline.su
|
||||||
|
|
||||||
|
# functions escaped as hexadecimal string
|
||||||
|
7068705f756e616d65
|
||||||
|
70687076657273696f6e
|
||||||
|
6368646972
|
||||||
|
676574637764
|
||||||
|
707265675f73706c6974
|
||||||
|
636f7079
|
||||||
|
66696c655f6765745f636f6e74656e7473
|
||||||
|
6261736536345f6465636f6465
|
||||||
|
69735f646972
|
||||||
|
6f625f656e645f636c65616e28293b
|
||||||
|
756e6c696e6b
|
||||||
|
6d6b646972
|
||||||
|
63686d6f64
|
||||||
|
7363616e646972
|
||||||
|
7374725f7265706c616365
|
||||||
|
68746d6c7370656369616c6368617273
|
||||||
|
7661725f64756d70
|
||||||
|
666f70656e
|
||||||
|
667772697465
|
||||||
|
66636c6f7365
|
||||||
|
64617465
|
||||||
|
66696c656d74696d65
|
||||||
|
737562737472
|
||||||
|
737072696e7466
|
||||||
|
66696c657065726d73
|
||||||
|
746f756368
|
||||||
|
66696c655f657869737473
|
||||||
|
72656e616d65
|
||||||
|
69735f6172726179
|
||||||
|
69735f6f626a656374
|
||||||
|
737472706f73
|
||||||
|
69735f7772697461626c65
|
||||||
|
69735f7265616461626c65
|
||||||
|
737472746f74696d65
|
||||||
|
66696c6573697a65
|
||||||
|
726d646972
|
||||||
|
6f625f6765745f636c65616e
|
||||||
|
7265616466696c65
|
||||||
|
617373657274
|
||||||
@@ -22,14 +22,23 @@ SHELL_PASSWORD
|
|||||||
ConnectBackShell
|
ConnectBackShell
|
||||||
ShellBOT
|
ShellBOT
|
||||||
== "bindshell"
|
== "bindshell"
|
||||||
|
".\x00..\x20"
|
||||||
|
FM_SESSION_ID
|
||||||
|
HACKED BY
|
||||||
|
_Mybb
|
||||||
|
|
||||||
#Remote Code
|
#Remote Code
|
||||||
curl_get_from_webpage
|
curl_get_from_webpage
|
||||||
file_get_contents('http://codepad.org
|
file_get_contents('http://codepad.org
|
||||||
|
|
||||||
|
#mailers
|
||||||
|
leafmailer.pw
|
||||||
|
|
||||||
#Base64 String Samples. Each plain text string should have 3 base64 equivalents
|
#Base64 String Samples. Each plain text string should have 3 base64 equivalents
|
||||||
|
|
||||||
|
# https://
|
||||||
|
aHR0cHM6Ly
|
||||||
|
|
||||||
# "shell" in base64
|
# "shell" in base64
|
||||||
c2hlbG
|
c2hlbG
|
||||||
NoZWxs
|
NoZWxs
|
||||||
@@ -108,11 +117,6 @@ SFRUUF9VU0VSX0FHRU5U
|
|||||||
hUVFBfVVNFUl9BR0VOV
|
hUVFBfVVNFUl9BR0VOV
|
||||||
IVFRQX1VTRVJfQUdFTl
|
IVFRQX1VTRVJfQUdFTl
|
||||||
|
|
||||||
# "file" in base64
|
|
||||||
ZmlsZ
|
|
||||||
ZpbG
|
|
||||||
maWxl
|
|
||||||
|
|
||||||
# "gzinflate" in base64
|
# "gzinflate" in base64
|
||||||
Z3ppbmZsYXRl
|
Z3ppbmZsYXRl
|
||||||
d6aW5mbGF0Z
|
d6aW5mbGF0Z
|
||||||
@@ -184,14 +188,23 @@ RlZmluZ
|
|||||||
kZWZpbm
|
kZWZpbm
|
||||||
|
|
||||||
# Obfuscation related code
|
# Obfuscation related code
|
||||||
|
'.'6'.'4'.'_'.'
|
||||||
|
bas'.'e64_dec
|
||||||
|
file'.'_put_co
|
||||||
|
fil'.'e_ex
|
||||||
|
Pz4=
|
||||||
|
L3gvaQ==
|
||||||
eval("?>
|
eval("?>
|
||||||
|
eval('?>
|
||||||
|
@eval(
|
||||||
"base64_decode"
|
"base64_decode"
|
||||||
='base'.(32*2).'_de'.'code'
|
='base'.(32*2).'_de'.'code'
|
||||||
"p"."r"."e"."g"."_"
|
"p"."r"."e"."g"."_"
|
||||||
WSOstripslashes
|
WSOstripslashes
|
||||||
\x73\x79\x73\x74\x65\x6d' /* case, dec/hex issue? */, // system
|
\x5f\x43\x4f\x4f\x4b\x49\x45
|
||||||
\x70\x72\x65\x67\x5f\x72\x65\x70\x6c\x61\x63\x65' /* case, dec/hex issue? */, // preg_replace
|
\x73\x79\x73\x74\x65\x6d
|
||||||
\x65\x78\x65\x63' /* dec/hex issue? */, // exec
|
\x70\x72\x65\x67\x5f\x72\x65\x70\x6c\x61\x63\x65
|
||||||
|
\x65\x78\x65\x63
|
||||||
ev\x61l
|
ev\x61l
|
||||||
\x65\166\x61\154\x28' /* dec/hex issue? */,
|
\x65\166\x61\154\x28' /* dec/hex issue? */,
|
||||||
\x65\x76\x61\x6C' /* case, dec/hex issue? */,
|
\x65\x76\x61\x6C' /* case, dec/hex issue? */,
|
||||||
@@ -201,11 +214,24 @@ eval(base64_decode(
|
|||||||
$data = base64_decode("
|
$data = base64_decode("
|
||||||
edoced_46esab
|
edoced_46esab
|
||||||
base=base64_encode
|
base=base64_encode
|
||||||
|
'b'.'ase6'.'4_e'.'ncode'
|
||||||
cr"."eat"."e_fun"."cti"."on
|
cr"."eat"."e_fun"."cti"."on
|
||||||
gz'.'inf'.'late
|
gz'.'inf'.'late
|
||||||
|
@eval("\
|
||||||
|
";eval(
|
||||||
|
eval(eval(
|
||||||
|
@eval(`
|
||||||
|
eVaL('?>
|
||||||
|
eval($_REQUEST
|
||||||
|
convert_uudecode(convert_uuencode
|
||||||
|
"64_decode"
|
||||||
|
'f' . 'il' . 'e' . '_'
|
||||||
|
'co' . 'nt' . 'e' . 'nt'
|
||||||
|
'h' . 'tm' . 'l' . 'sp'
|
||||||
|
'ha' . 'r' . 's'
|
||||||
|
|
||||||
# fopo.com.ar - free online php obfuscator. It conveniently leaves comments in the code.
|
# fopo.com.ar - free online php obfuscator. It conveniently leaves comments in the code.
|
||||||
http://www.fopo.com.ar/
|
http://www.fopo.com.ar/
|
||||||
@eval("\
|
|
||||||
|
|
||||||
#Malware/Attack specific strings/fingerprints/signatures
|
#Malware/Attack specific strings/fingerprints/signatures
|
||||||
MagelangCyber
|
MagelangCyber
|
||||||
@@ -260,7 +286,25 @@ itsoknoproblembro
|
|||||||
tmhapbzcerff
|
tmhapbzcerff
|
||||||
IndoXploit
|
IndoXploit
|
||||||
FaisaL Ahmed aka rEd X
|
FaisaL Ahmed aka rEd X
|
||||||
|
smisbot
|
||||||
|
smotherbot
|
||||||
|
Indonesian Hacker Rulez
|
||||||
|
pwetan.com
|
||||||
|
iNHUMaN
|
||||||
|
Heartzz
|
||||||
|
Bye Bye Litespeed
|
||||||
|
BunnyInvisible
|
||||||
|
SEMOGABERKAH
|
||||||
|
BUTERFLYCOUNTRY
|
||||||
|
|
||||||
|
# WP-VCD Malware https://www.getastra.com/blog/911/how-to-fix-wp-vcd-backdoor-hack-in-wordpress-functions-php/
|
||||||
|
wp-vcd
|
||||||
|
class.theme-modules.php
|
||||||
|
wp-tmp.php
|
||||||
|
tmpcontentx
|
||||||
|
function wp_temp_setupx
|
||||||
|
derna.top/code.php
|
||||||
|
stripos($tmpcontent, $wp_auth_key)
|
||||||
|
|
||||||
#Miscellaneous
|
#Miscellaneous
|
||||||
uname -a
|
uname -a
|
||||||
@@ -349,6 +393,7 @@ php_uname()
|
|||||||
str_split(rawurldecode(str_rot13(
|
str_split(rawurldecode(str_rot13(
|
||||||
# generating PHP file name to put content
|
# generating PHP file name to put content
|
||||||
substr(md5(time()), 0, 8) . ".php"
|
substr(md5(time()), 0, 8) . ".php"
|
||||||
|
'a:1:{s:13:\"administrator\";b:1;}'
|
||||||
|
|
||||||
# webshell
|
# webshell
|
||||||
0byt3m1n1
|
0byt3m1n1
|
||||||
@@ -363,3 +408,63 @@ ZeroByte
|
|||||||
|
|
||||||
# JS escaped: String.fromCharCode(
|
# JS escaped: String.fromCharCode(
|
||||||
83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40
|
83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40
|
||||||
|
|
||||||
|
# SEO poisoning control site call
|
||||||
|
"http://$xxx
|
||||||
|
?useragent=$botbotbot
|
||||||
|
[#*#*#]
|
||||||
|
|
||||||
|
# php://input encoded in base64
|
||||||
|
cGhwOi8vaW5wdXQ=
|
||||||
|
|
||||||
|
# backdoor script
|
||||||
|
<font color="red">Upload Gagal..</font><br />
|
||||||
|
explode('?>',$shell
|
||||||
|
0.33333333333333+0.33333333333333+0.33333333333333
|
||||||
|
0.66666666666667+0.66666666666667+0.66666666666667
|
||||||
|
1.3333333333333+1.3333333333333+1.3333333333333
|
||||||
|
class _t{private static$_
|
||||||
|
'LQ'.'=='
|
||||||
|
|
||||||
|
# common mobile agent check in SEO poison scripts
|
||||||
|
Array("1207", "3gso", "4thp", "501i", "502i", "503i", "504i", "505i", "506i",
|
||||||
|
|
||||||
|
# eval url decoded string
|
||||||
|
eval(rawurldecode('
|
||||||
|
eval(htmlspecialchars_decode(
|
||||||
|
|
||||||
|
# simple obfuscated function
|
||||||
|
'gz'.'unc'.'ompress'
|
||||||
|
'create'.'_'.'function'
|
||||||
|
'gzinf', 'la', 'te'
|
||||||
|
'e_f', 'cti', 'un', 'on', 'cr', 'eat'
|
||||||
|
'base', '64_dec', 'ode'
|
||||||
|
'cook', 'set', 'ie'
|
||||||
|
'repl', 'str_', 'ace'
|
||||||
|
"base"."64_"
|
||||||
|
'base'.'64_'
|
||||||
|
"t"."m"."p"."_"."n"."a"."m"."e"
|
||||||
|
"f"."i"."l"."e"."_"."p"."u"."t"
|
||||||
|
"f"."i"."l"."e"."_"."g"."e"."t"
|
||||||
|
'ode', 'e64_', 'bas', 'dec'
|
||||||
|
'unct', 'ion', 'te_f', 'crea'
|
||||||
|
'te', 'g', 'nf', 'l', 'a', 'zi'
|
||||||
|
'tion', 'e_func', 'creat'
|
||||||
|
'64_d', 'se', 'eco', 'de', 'ba'
|
||||||
|
'co', 'ki', 'e', 'o', 'set'
|
||||||
|
'str', '_rep', 'lace'
|
||||||
|
|
||||||
|
# process data from request object directly
|
||||||
|
extract($_REQUEST) && @$
|
||||||
|
extract($_REQUEST)&&@$
|
||||||
|
xtract($_REQUEST)&&@$
|
||||||
|
|
||||||
|
# uncompress cafted content
|
||||||
|
gzuncompress(strrev(substr(
|
||||||
|
|
||||||
|
# disable error reporting
|
||||||
|
<?php error_reporting(0);?>
|
||||||
|
|
||||||
|
# infected file include attached on the top of a legit file
|
||||||
|
<?php if (file_exists(dirname(__FILE__) . '/class.theme-modules.php')) include_once(dirname(__FILE__) . '/class.theme-modules.php'); ?>
|
||||||
|
<?php if (file_exists(dirname(__FILE__) . '/class.plugin-modules.php')) include_once(dirname(__FILE__) . '/class.plugin-modules.php'); ?>
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ eval\/\*[a-z0-9]+\*\/
|
|||||||
#
|
#
|
||||||
eval\([a-z0-9]{4,}\(\$[a-z0-9]{4,}, \$[0-9a-z]{4,}\)\);
|
eval\([a-z0-9]{4,}\(\$[a-z0-9]{4,}, \$[0-9a-z]{4,}\)\);
|
||||||
|
|
||||||
#
|
# chr(101).chr(118).chr(97)
|
||||||
(chr\(\d+\^\d+\)\.){4,}
|
(chr\(\d+\^\d+\)\.){4,}
|
||||||
|
|
||||||
#
|
# $_uU(101).$_uU(118).$_uU(97)
|
||||||
|
(\$\_[a-z0-9]{2,}\(\d+\)\.){4,}
|
||||||
|
|
||||||
|
# $uUx[101].$uUx[118].$uUx[97]
|
||||||
(\$[a-z0-9]{3,}\[\d+\]\.){4,}
|
(\$[a-z0-9]{3,}\[\d+\]\.){4,}
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -37,6 +40,9 @@ Googlebot['"]{0,1}\s*\)\){echo\s+file_get_contents
|
|||||||
#execute base64 code
|
#execute base64 code
|
||||||
eVaL\(\s*trim\(\s*baSe64_deCoDe\(
|
eVaL\(\s*trim\(\s*baSe64_deCoDe\(
|
||||||
|
|
||||||
|
# execute escaped code
|
||||||
|
exec\("(\\[0-9a-fx]{2,3}){3,}
|
||||||
|
|
||||||
#
|
#
|
||||||
if\s*\(\s*mail\s*\(\s*\$mails\[\$i\]\s*,\s*\$tema\s*,\s*base64_encode\s*\(\s*\$text
|
if\s*\(\s*mail\s*\(\s*\$mails\[\$i\]\s*,\s*\$tema\s*,\s*base64_encode\s*\(\s*\$text
|
||||||
|
|
||||||
@@ -54,7 +60,7 @@ chr\s*\(\s*101\s*\)\s*\.\s*chr\s*\(\s*118\s*\)\s*\.\s*chr\s*\(\s*97\s*\)\s*\.\s*
|
|||||||
|
|
||||||
#Detects the '_' character encoded in a string like "\x5F". '_' is present in many functions that malware would want to hide.
|
#Detects the '_' character encoded in a string like "\x5F". '_' is present in many functions that malware would want to hide.
|
||||||
# '_' as "\x5f"
|
# '_' as "\x5f"
|
||||||
\\[Xx](5[Ff])
|
# \\[Xx](5[Ff]) - removed because generate many false positives
|
||||||
|
|
||||||
#Detects the '_' character placed inside a call to the 'chr()' function
|
#Detects the '_' character placed inside a call to the 'chr()' function
|
||||||
# '_' as 'chr(95)' or 'chr(0x5f)'
|
# '_' as 'chr(95)' or 'chr(0x5f)'
|
||||||
@@ -73,7 +79,7 @@ chr\s*\(\s*['"]?\s*((95)|(0[Xx]5[Ff]))\s*['"]?\s*\)
|
|||||||
#Escaped path characters: \x2fho\x6de/\x69mp\x75ls\x69oq\x65/w\x77w. or \x2fhome\x2fimpu\x6csioq\x65/www\x2emusc
|
#Escaped path characters: \x2fho\x6de/\x69mp\x75ls\x69oq\x65/w\x77w. or \x2fhome\x2fimpu\x6csioq\x65/www\x2emusc
|
||||||
(\\x[0-9abcdef]{2}[a-z0-9.-\/]{1,4}){4,}
|
(\\x[0-9abcdef]{2}[a-z0-9.-\/]{1,4}){4,}
|
||||||
|
|
||||||
#Malware inffected files sometimes marked with comments like /*87cda*/ to avoid infect again
|
#Malware infected files sometimes marked with comments like /*87cda*/ to avoid infect again
|
||||||
\/\*[a-z0-9]{5}\*\/
|
\/\*[a-z0-9]{5}\*\/
|
||||||
|
|
||||||
# XOR-ed strings with custom math
|
# XOR-ed strings with custom math
|
||||||
@@ -89,7 +95,7 @@ eval\(\$[a-z0-9_]+\(\$_POST
|
|||||||
("[a-z0-9]+"\.chr\(\d+\)\.){3,}
|
("[a-z0-9]+"\.chr\(\d+\)\.){3,}
|
||||||
|
|
||||||
# nested function call used variables
|
# nested function call used variables
|
||||||
\$[a-z]+\(\$[a-z0-9]+\(
|
\$[a-z0-9_]+\(\$[a-z0-9_]+\(
|
||||||
|
|
||||||
# GLOBALS inject with escaped content
|
# GLOBALS inject with escaped content
|
||||||
\$GLOBALS;\$\{"\\x
|
\$GLOBALS;\$\{"\\x
|
||||||
@@ -108,3 +114,54 @@ function\s+_[0-9]{8,}\(
|
|||||||
|
|
||||||
# create_function is dangerous as like eval() see http://php.net/manual/en/function.create-function.php
|
# create_function is dangerous as like eval() see http://php.net/manual/en/function.create-function.php
|
||||||
create_function\s*\(\s*['"]{2}
|
create_function\s*\(\s*['"]{2}
|
||||||
|
|
||||||
|
# control concated from cookie at the call
|
||||||
|
(\$[a-z]{2,}=urldecode\(\$_COOKIE\['[a-z]{2,}'\]\);){3,}
|
||||||
|
|
||||||
|
# ${$O{18}.$O{7}.$O{24}.$O{2}.$O{50}.$O{8}
|
||||||
|
(\$[A-Z]+\{\d+\}\.){3,}
|
||||||
|
|
||||||
|
# comment in variable name $_REQUEST /*YUsrqpbzvXTSa...QpDNTPYQvLSFPCqsSnWNVqPdSIAYaQj*/[
|
||||||
|
\$_REQUEST\s*\/\*[A-Za-z]+\*\/\[
|
||||||
|
|
||||||
|
# cookie payload if(isset($_COOKIE)){$p=$_COOKIE;(count($p)==55&&in_array(gettype($p).count($p),$p))?(($p[68]=$p[68].$p[22])&&($p[35]=$p[68]($p[35]))&&($p=$p[35]($p[13],$p[68]($p[45])))&&$p()):$p;}
|
||||||
|
\(count\(\$p\)==\d+&&in_array\(gettype\(\$p\)\.count\(\$p\),\$p\)\)
|
||||||
|
|
||||||
|
# gzipped payload post process
|
||||||
|
explode\('\|\x01\|\x03\|\x03', gzinflate\(
|
||||||
|
|
||||||
|
# backdoor reported #71
|
||||||
|
@header\(\w{3,5}::\w{1,2}\('_\w{1,3}' \. '\w{1,3}', '_\w{1,3}'\)\);
|
||||||
|
@header\(\w{3,5}::\w{1,2}\('_\w{1,3}', '_' \. '\w{1,3}' . '\w{1,3}'\)\);
|
||||||
|
|
||||||
|
# backdoor reported #72
|
||||||
|
@\$[a-z]{1}\[\d+\]\(\$[a-z]{1}\[\d+\]\);
|
||||||
|
|
||||||
|
# reported #77
|
||||||
|
\$[a-z]11 \^ [a-z]8\(\$[a-z]6, \$[a-z]14, \$[a-z]6\[13\]\(\$[a-z]11\)\)\)\);
|
||||||
|
|
||||||
|
# eval function return and concat
|
||||||
|
eval\([A-Za-z0-9]{5,}\(\) \. '
|
||||||
|
|
||||||
|
# eval function return, parameter is a hex string
|
||||||
|
eval\([A-Za-z0-9]{5,}\(\"[A-Z0-9]{16,}
|
||||||
|
|
||||||
|
eval\(\s+'\?>'
|
||||||
|
|
||||||
|
# gzip payload called by variable named function
|
||||||
|
\$[a-zA-Z0-9]{6,}\('\x78\x9C\xAD\x90\x41\x0E
|
||||||
|
|
||||||
|
# obfuscated code return with error suppression
|
||||||
|
return @\$[a-z]{2}\d+\[\d+\]\(\$[a-z]{2}\d+\[\d+\],
|
||||||
|
|
||||||
|
# htaccess alternating
|
||||||
|
[a-z]{1}\([a-z]{1}\(\$[a-z]{2}\.'\/\.htaccess'\)
|
||||||
|
|
||||||
|
# Javascript specific rules
|
||||||
|
|
||||||
|
# JS - escaped command
|
||||||
|
\.fromCharCode\([0-9,]{4,}\)
|
||||||
|
\+-parseInt\(\w\('0x[0-9a-z]+'\)\)\/
|
||||||
|
|
||||||
|
# concated hash value
|
||||||
|
('[a-z0-9]{2,}'\.){4,}
|
||||||
|
|||||||
186
scan.php
186
scan.php
@@ -31,6 +31,7 @@ class MalwareScanner
|
|||||||
private $flagChecksum = false;
|
private $flagChecksum = false;
|
||||||
private $flagComments = false;
|
private $flagComments = false;
|
||||||
private $flagHideOk = false;
|
private $flagHideOk = false;
|
||||||
|
private $flagHideErr = false;
|
||||||
private $flagHideWhitelist = false;
|
private $flagHideWhitelist = false;
|
||||||
private $flagNoStop = false;
|
private $flagNoStop = false;
|
||||||
private $flagPattern = false;
|
private $flagPattern = false;
|
||||||
@@ -40,6 +41,8 @@ class MalwareScanner
|
|||||||
private $flagLineNumber = false;
|
private $flagLineNumber = false;
|
||||||
private $flagScanEverything = false;
|
private $flagScanEverything = false;
|
||||||
private $flagCombinedWhitelist = false;
|
private $flagCombinedWhitelist = false;
|
||||||
|
private $flagDisableStats = false;
|
||||||
|
private $customWhitelist = array();
|
||||||
private $outputFormat = '';
|
private $outputFormat = '';
|
||||||
private $whitelist = array();
|
private $whitelist = array();
|
||||||
private $ignore = array();
|
private $ignore = array();
|
||||||
@@ -68,16 +71,31 @@ class MalwareScanner
|
|||||||
if ($cli === true) {
|
if ($cli === true) {
|
||||||
//Read Run Options
|
//Read Run Options
|
||||||
$this->parseArgs();
|
$this->parseArgs();
|
||||||
$this->dir = realpath($this->dir);
|
|
||||||
|
$dirs = array();
|
||||||
|
if (is_array($this->dir)) {
|
||||||
|
// allow multiple directory aka. array
|
||||||
|
foreach ($this->dir as $path) {
|
||||||
|
$dirs[] = realpath($path);
|
||||||
|
}
|
||||||
|
} elseif ($bpos = strpos($this->dir, '{')) {
|
||||||
|
// Check path has a "brace", expand it to subdirectories
|
||||||
|
foreach (glob($this->dir, GLOB_BRACE) as $path) {
|
||||||
|
$dirs[] = realpath($path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only one directory specified
|
||||||
|
$dirs = array (realpath($this->dir));
|
||||||
|
}
|
||||||
|
|
||||||
//Make sure a directory was specified.
|
//Make sure a directory was specified.
|
||||||
if ($this->dir === '') {
|
if (empty($dirs)) {
|
||||||
$this->error('No directory specified or directory doesn\'t exist');
|
$this->error('No directory specified or directory doesn\'t exist');
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initiate Scan
|
//Initiate Scan
|
||||||
if (!$this->run($this->dir)) {
|
if (!$this->run($dirs)) {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +121,7 @@ class MalwareScanner
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Handles pattern loading and saving to the class object
|
//Handles pattern loading and saving to the class object
|
||||||
private function initializePatterns()
|
public function initializePatterns()
|
||||||
{
|
{
|
||||||
$dir = dirname(__FILE__);
|
$dir = dirname(__FILE__);
|
||||||
//Loads either the primary scanning patterns or the base64 patterns depending on -b/--base64 flag
|
//Loads either the primary scanning patterns or the base64 patterns depending on -b/--base64 flag
|
||||||
@@ -174,31 +192,55 @@ class MalwareScanner
|
|||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Loads the whitelist file
|
/**
|
||||||
private function loadWhitelist()
|
* Loads the whitelist files
|
||||||
|
*/
|
||||||
|
public function loadWhitelists()
|
||||||
{
|
{
|
||||||
if (!is_file(__DIR__ . '/whitelist.txt')) {
|
$a = array_merge([__DIR__ . '/whitelist.txt'], $this->customWhitelist);
|
||||||
return;
|
foreach ($a as $file) {
|
||||||
}
|
if (is_file($file)) {
|
||||||
$fp = fopen(__DIR__ . '/whitelist.txt', 'r');
|
$fp = fopen($file, 'r');
|
||||||
while (!feof($fp)) {
|
while (!feof($fp)) {
|
||||||
$line = fgets($fp);
|
$line = fgets($fp);
|
||||||
$this->whitelist[] = substr($line, 0, 32);
|
$hash = substr($line, 0, 32);
|
||||||
|
if (strlen($hash) === 32) {
|
||||||
|
$this->whitelist[] = $hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($fp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addWordpressChecksums($wp_version)
|
public function addWordpressChecksums($wp_version)
|
||||||
{
|
{
|
||||||
$apiurl = 'https://api.wordpress.org/core/checksums/1.0/?version=' . $wp_version;
|
if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $wp_version)) {
|
||||||
$json = json_decode(file_get_contents($apiurl));
|
$this->error('Invalid WordPress version format: ' . $wp_version);
|
||||||
$checksums = $json->checksums;
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
if ($checksums->$wp_version == false) { #no checksum returned
|
$apiurl = 'https://api.wordpress.org/core/checksums/1.0/?version=' . $wp_version;
|
||||||
|
$raw = file_get_contents($apiurl);
|
||||||
|
if ($raw === false) {
|
||||||
$this->error('Cannot load wordpress checksums from: ' . $apiurl);
|
$this->error('Cannot load wordpress checksums from: ' . $apiurl);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($checksums->$wp_version as $file => $checksum) {
|
$json = json_decode($raw);
|
||||||
|
if ($json === null || !isset($json->checksums)) {
|
||||||
|
$this->error('Invalid response from WordPress checksums API');
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$checksums = $json->checksums;
|
||||||
|
if ($checksums === false || empty((array)$checksums)) {
|
||||||
|
$this->error('No checksums returned for WordPress version: ' . $wp_version);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries = isset($checksums->$wp_version) ? $checksums->$wp_version : $checksums;
|
||||||
|
foreach ($entries as $file => $checksum) {
|
||||||
$this->whitelist[] = $checksum;
|
$this->whitelist[] = $checksum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +250,7 @@ class MalwareScanner
|
|||||||
private function parseArgs()
|
private function parseArgs()
|
||||||
{
|
{
|
||||||
$options = getopt(
|
$options = getopt(
|
||||||
'd:e:i:o:abmcxlhkwnsptLj:E',
|
'd:e:i:o:abmcxlhkrwnsptLj:E',
|
||||||
array(
|
array(
|
||||||
'directory:',
|
'directory:',
|
||||||
'extension:',
|
'extension:',
|
||||||
@@ -221,6 +263,7 @@ class MalwareScanner
|
|||||||
'follow-link',
|
'follow-link',
|
||||||
'help',
|
'help',
|
||||||
'hide-ok',
|
'hide-ok',
|
||||||
|
'hide-err',
|
||||||
'hide-whitelist',
|
'hide-whitelist',
|
||||||
'no-color',
|
'no-color',
|
||||||
'no-stop',
|
'no-stop',
|
||||||
@@ -230,7 +273,9 @@ class MalwareScanner
|
|||||||
'output-format:',
|
'output-format:',
|
||||||
'wordpress-version:',
|
'wordpress-version:',
|
||||||
'scan-everything',
|
'scan-everything',
|
||||||
'combined-whitelist'
|
'combined-whitelist',
|
||||||
|
'custom-whitelist:',
|
||||||
|
'disable-stats'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -281,6 +326,9 @@ class MalwareScanner
|
|||||||
if (isset($options['hide-ok']) || isset($options['k'])) {
|
if (isset($options['hide-ok']) || isset($options['k'])) {
|
||||||
$this->setFlagHideOk(true);
|
$this->setFlagHideOk(true);
|
||||||
}
|
}
|
||||||
|
if (isset($options['hide-err']) || isset($options['r'])) {
|
||||||
|
$this->setFlagHideErr(true);
|
||||||
|
}
|
||||||
if (isset($options['hide-whitelist']) || isset($options['w'])) {
|
if (isset($options['hide-whitelist']) || isset($options['w'])) {
|
||||||
$this->setFlagHideWhitelist(true);
|
$this->setFlagHideWhitelist(true);
|
||||||
}
|
}
|
||||||
@@ -313,6 +361,16 @@ class MalwareScanner
|
|||||||
if (isset($options['combined-whitelist'])) {
|
if (isset($options['combined-whitelist'])) {
|
||||||
$this->setFlagCombinedWhitelist(true);
|
$this->setFlagCombinedWhitelist(true);
|
||||||
}
|
}
|
||||||
|
if (isset($options['custom-whitelist'])) {
|
||||||
|
$a = $options['custom-whitelist'];
|
||||||
|
if (!is_array($a)) {
|
||||||
|
$a = array($a);
|
||||||
|
}
|
||||||
|
$this->setCustomWhitelist(array_unique($a));
|
||||||
|
}
|
||||||
|
if (isset($options['disable-stats'])) {
|
||||||
|
$this->setFlagDisableStats(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setExtensions(array $a)
|
public function setExtensions(array $a)
|
||||||
@@ -376,6 +434,11 @@ class MalwareScanner
|
|||||||
$this->flagHideOk = $b;
|
$this->flagHideOk = $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setFlagHideErr($b)
|
||||||
|
{
|
||||||
|
$this->flagHideErr = $b;
|
||||||
|
}
|
||||||
|
|
||||||
public function setFlagHideWhitelist($b)
|
public function setFlagHideWhitelist($b)
|
||||||
{
|
{
|
||||||
$this->flagHideWhitelist = $b;
|
$this->flagHideWhitelist = $b;
|
||||||
@@ -401,6 +464,16 @@ class MalwareScanner
|
|||||||
$this->flagCombinedWhitelist = $b;
|
$this->flagCombinedWhitelist = $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setFlagDisableStats($b)
|
||||||
|
{
|
||||||
|
$this->flagDisableStats = $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCustomWhitelist($a)
|
||||||
|
{
|
||||||
|
$this->customWhitelist = $a;
|
||||||
|
}
|
||||||
|
|
||||||
// @see http://stackoverflow.com/a/13914119
|
// @see http://stackoverflow.com/a/13914119
|
||||||
private function pathMatches($path, $pattern, $ignoreCase = false)
|
private function pathMatches($path, $pattern, $ignoreCase = false)
|
||||||
{
|
{
|
||||||
@@ -465,6 +538,9 @@ class MalwareScanner
|
|||||||
$state = 'WL';
|
$state = 'WL';
|
||||||
$state_color = $this->ANSI_YELLOW;
|
$state_color = $this->ANSI_YELLOW;
|
||||||
} else {
|
} else {
|
||||||
|
if ($this->flagHideErr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$state = 'ER';
|
$state = 'ER';
|
||||||
$state_color = $this->ANSI_RED;
|
$state_color = $this->ANSI_RED;
|
||||||
}
|
}
|
||||||
@@ -501,7 +577,7 @@ class MalwareScanner
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->outputFormat) {
|
if ($this->outputFormat) {
|
||||||
$map = [
|
$map = array(
|
||||||
'%S' => $state,
|
'%S' => $state,
|
||||||
'%T' => $ctime,
|
'%T' => $ctime,
|
||||||
'%M' => $hash,
|
'%M' => $hash,
|
||||||
@@ -509,9 +585,9 @@ class MalwareScanner
|
|||||||
'%P' => $pattern,
|
'%P' => $pattern,
|
||||||
'%C' => $comment,
|
'%C' => $comment,
|
||||||
'%L' => $lineNumber,
|
'%L' => $lineNumber,
|
||||||
];
|
);
|
||||||
} else {
|
} else {
|
||||||
$map = [
|
$map = array(
|
||||||
'%S' => $state_color . '# ' . $state . $this->ANSI_OFF,
|
'%S' => $state_color . '# ' . $state . $this->ANSI_OFF,
|
||||||
'%T' => $this->ANSI_BLUE . $ctime . $this->ANSI_OFF,
|
'%T' => $this->ANSI_BLUE . $ctime . $this->ANSI_OFF,
|
||||||
'%M' => $this->ANSI_BLUE . $hash . $this->ANSI_OFF,
|
'%M' => $this->ANSI_BLUE . $hash . $this->ANSI_OFF,
|
||||||
@@ -519,7 +595,7 @@ class MalwareScanner
|
|||||||
'%P' => $state_color . '#' . $pattern . $this->ANSI_OFF,
|
'%P' => $state_color . '#' . $pattern . $this->ANSI_OFF,
|
||||||
'%C' => $this->ANSI_BLUE . $comment . $this->ANSI_OFF,
|
'%C' => $this->ANSI_BLUE . $comment . $this->ANSI_OFF,
|
||||||
'%L' => $lineNumber,
|
'%L' => $lineNumber,
|
||||||
];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->outputFormat) {
|
if ($this->outputFormat) {
|
||||||
@@ -566,8 +642,8 @@ class MalwareScanner
|
|||||||
private function report($start, $dir)
|
private function report($start, $dir)
|
||||||
{
|
{
|
||||||
$end = time();
|
$end = time();
|
||||||
echo 'Start time: ' . strftime('%Y-%m-%d %H:%M:%S', $start) . PHP_EOL;
|
echo 'Start time: ' . date('Y-m-d H:i:s', $start) . PHP_EOL;
|
||||||
echo 'End time: ' . strftime('%Y-%m-%d %H:%M:%S', $end) . PHP_EOL;
|
echo 'End time: ' . date('Y-m-d H:i:s', $end) . PHP_EOL;
|
||||||
echo 'Total execution time: ' . ($end - $start) . PHP_EOL;
|
echo 'Total execution time: ' . ($end - $start) . PHP_EOL;
|
||||||
echo 'Base directory: ' . $dir . PHP_EOL;
|
echo 'Base directory: ' . $dir . PHP_EOL;
|
||||||
echo 'Total directories scanned: ' . $this->stat['directories'] . PHP_EOL;
|
echo 'Total directories scanned: ' . $this->stat['directories'] . PHP_EOL;
|
||||||
@@ -582,29 +658,38 @@ class MalwareScanner
|
|||||||
* - Fetch and load combined whitelist
|
* - Fetch and load combined whitelist
|
||||||
* - Calls the process and report functions.
|
* - Calls the process and report functions.
|
||||||
*
|
*
|
||||||
* @param $dir
|
* @param string|array $dir A directory path or a list of paths in array
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function run($dir)
|
public function run($dir)
|
||||||
{
|
{
|
||||||
// Make sure the input is a valid directory path.
|
|
||||||
$dir = rtrim($dir, '/');
|
|
||||||
if (!is_dir($dir)) {
|
|
||||||
$this->error('Specified path is not a directory: ' . $dir);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->initializePatterns();
|
$this->initializePatterns();
|
||||||
|
|
||||||
$this->loadWhitelist();
|
$this->loadWhitelists();
|
||||||
|
|
||||||
if ($this->flagCombinedWhitelist && !$this->updateCombinedWhitelist()) {
|
if ($this->flagCombinedWhitelist && !$this->updateCombinedWhitelist()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$start = time();
|
$start = time();
|
||||||
$this->process($dir . '/');
|
|
||||||
$this->report($start, $dir . '/');
|
if (!is_array($dir)) {
|
||||||
|
$dir = array ($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($dir as $path) {
|
||||||
|
// Make sure the input is a valid directory path.
|
||||||
|
$path = rtrim($path, '/');
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
$this->error('Specified path is not a directory: ' . $path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->process($path . '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->flagDisableStats) {
|
||||||
|
$this->report($start, implode(', ', $dir));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,6 +699,12 @@ class MalwareScanner
|
|||||||
{
|
{
|
||||||
$this->stat['files_scanned']++;
|
$this->stat['files_scanned']++;
|
||||||
$fileContent = file_get_contents($path);
|
$fileContent = file_get_contents($path);
|
||||||
|
if ($fileContent === false) {
|
||||||
|
if (!$this->flagHideErr) {
|
||||||
|
echo $this->ANSI_RED . '# ER' . $this->ANSI_OFF . ' # {' . $path . '} (unreadable)' . PHP_EOL;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$found = false;
|
$found = false;
|
||||||
$inWhitelist = false;
|
$inWhitelist = false;
|
||||||
$hash = md5($fileContent);
|
$hash = md5($fileContent);
|
||||||
@@ -644,14 +735,14 @@ class MalwareScanner
|
|||||||
//Returns true if the raw string exists in the file contents.
|
//Returns true if the raw string exists in the file contents.
|
||||||
private function scanFunc_STR(&$pattern, &$content)
|
private function scanFunc_STR(&$pattern, &$content)
|
||||||
{
|
{
|
||||||
return strpos($content, $pattern);
|
return strpos($content, (string)$pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Performs raw string, case insensitive matching.
|
//Performs raw string, case insensitive matching.
|
||||||
//Returns true if the raw string exists in the file contents, ignoring case.
|
//Returns true if the raw string exists in the file contents, ignoring case.
|
||||||
private function scanFunc_STRI(&$pattern, &$content)
|
private function scanFunc_STRI(&$pattern, &$content)
|
||||||
{
|
{
|
||||||
return stripos($content, $pattern);
|
return stripos($content, (string)$pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Performs regular expression matching.
|
//Performs regular expression matching.
|
||||||
@@ -659,7 +750,10 @@ class MalwareScanner
|
|||||||
//Patterns will match multiple lines, though you can use ^$ to match the beginning and end of a line.
|
//Patterns will match multiple lines, though you can use ^$ to match the beginning and end of a line.
|
||||||
private function scanFunc_RE(&$pattern, &$content)
|
private function scanFunc_RE(&$pattern, &$content)
|
||||||
{
|
{
|
||||||
$ret = preg_match('/' . $pattern . '/im', $content, $match, PREG_OFFSET_CAPTURE);
|
$ret = @preg_match('/' . $pattern . '/im', $content, $match, PREG_OFFSET_CAPTURE);
|
||||||
|
if ($ret === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ($ret) {
|
if ($ret) {
|
||||||
return $match[0][1];
|
return $match[0][1];
|
||||||
}
|
}
|
||||||
@@ -724,11 +818,14 @@ class MalwareScanner
|
|||||||
|
|
||||||
private function updateCombinedWhitelist($url = 'https://scr34m.github.io/php-malware-scanner')
|
private function updateCombinedWhitelist($url = 'https://scr34m.github.io/php-malware-scanner')
|
||||||
{
|
{
|
||||||
$latest_hash = trim(file_get_contents($url . '/database/compressed.sha256'));
|
$ctx = stream_context_create(array('http' => array('timeout' => 30)));
|
||||||
|
|
||||||
|
$latest_hash = file_get_contents($url . '/database/compressed.sha256', false, $ctx);
|
||||||
if ($latest_hash === false) {
|
if ($latest_hash === false) {
|
||||||
$this->error('Unable to download database checksum');
|
$this->error('Unable to download database checksum');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$latest_hash = trim($latest_hash);
|
||||||
|
|
||||||
$file = __DIR__ . '/whitelist.dat';
|
$file = __DIR__ . '/whitelist.dat';
|
||||||
if (is_readable($file)) {
|
if (is_readable($file)) {
|
||||||
@@ -743,7 +840,7 @@ class MalwareScanner
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($download) {
|
if ($download) {
|
||||||
$data = file_get_contents($url . '/database/compressed.dat');
|
$data = file_get_contents($url . '/database/compressed.dat', false, $ctx);
|
||||||
if ($data === false) {
|
if ($data === false) {
|
||||||
$this->error('Unable to download database');
|
$this->error('Unable to download database');
|
||||||
return false;
|
return false;
|
||||||
@@ -753,11 +850,12 @@ class MalwareScanner
|
|||||||
$hash = hash_file('sha256', $file);
|
$hash = hash_file('sha256', $file);
|
||||||
if ($hash != $latest_hash) {
|
if ($hash != $latest_hash) {
|
||||||
$this->error('Downloaded database hash mismatch');
|
$this->error('Downloaded database hash mismatch');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = gzdecode(file_get_contents($file));
|
$content = gzdecode(file_get_contents($file));
|
||||||
$this->combined_whitelist = [];
|
$this->combined_whitelist = array();
|
||||||
$this->combined_whitelist_count = 0;
|
$this->combined_whitelist_count = 0;
|
||||||
foreach (explode("\n", $content) as $line) { // faster than strtok, but needs more memory
|
foreach (explode("\n", $content) as $line) { // faster than strtok, but needs more memory
|
||||||
if ($line) {
|
if ($line) {
|
||||||
@@ -786,6 +884,7 @@ class MalwareScanner
|
|||||||
echo ' -x --extra-check Adds GoogleBot and htaccess to Scan List' . PHP_EOL;
|
echo ' -x --extra-check Adds GoogleBot and htaccess to Scan List' . PHP_EOL;
|
||||||
echo ' -l --follow-symlink Follow symlinked directories' . PHP_EOL;
|
echo ' -l --follow-symlink Follow symlinked directories' . PHP_EOL;
|
||||||
echo ' -k --hide-ok Hide results with \'OK\' status' . PHP_EOL;
|
echo ' -k --hide-ok Hide results with \'OK\' status' . PHP_EOL;
|
||||||
|
echo ' -r --hide-err Hide results with \'ER\' status' . PHP_EOL;
|
||||||
echo ' -w --hide-whitelist Hide results with \'WL\' status' . PHP_EOL;
|
echo ' -w --hide-whitelist Hide results with \'WL\' status' . PHP_EOL;
|
||||||
echo ' -n --no-color Disable color mode' . PHP_EOL;
|
echo ' -n --no-color Disable color mode' . PHP_EOL;
|
||||||
echo ' -s --no-stop Continue scanning file after first hit' . PHP_EOL;
|
echo ' -s --no-stop Continue scanning file after first hit' . PHP_EOL;
|
||||||
@@ -793,8 +892,9 @@ class MalwareScanner
|
|||||||
echo ' -t --time Show time of last file change' . PHP_EOL;
|
echo ' -t --time Show time of last file change' . PHP_EOL;
|
||||||
echo ' -L --line-number Display matching pattern line number in file' . PHP_EOL;
|
echo ' -L --line-number Display matching pattern line number in file' . PHP_EOL;
|
||||||
echo ' -o --output-format Custom defined output format' . PHP_EOL;
|
echo ' -o --output-format Custom defined output format' . PHP_EOL;
|
||||||
echo ' -j --wordpress-version Version of wordpress to get md5 signatures' . PHP_EOL;
|
echo ' -j <version> --wordpress-version Version of wordpress to get md5 signatures' . PHP_EOL;
|
||||||
echo ' --combined-whitelist Combined whitelist' . PHP_EOL;
|
echo ' --combined-whitelist Combined whitelist' . PHP_EOL;
|
||||||
|
echo ' --disable-stats Disable statistics output' . PHP_EOL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ function fetch($url, $file = false)
|
|||||||
|
|
||||||
$headers = array(
|
$headers = array(
|
||||||
// drupal suxx
|
// drupal suxx
|
||||||
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15',
|
'Cookie: _px2=eyJ1IjoiZDZhNGM3MjAtYjZmNC0xMWVhLWI2MzMtNzk5YzRmZjM4ZmJkIiwidiI6IjQ0ZTFiMDQwLTRkZGUtMTFlOC1iMWRjLWYxNWU4OTg1NTZjNyIsInQiOjE1OTMwOTc2Mjg2NzAsImgiOiIzNzk5N2RkYTU3ZTI1NGY0ZDM5MmRiMWExNWZhZjhjNTZkMmM5NTZkZDJiZWVkZGVlZDc1MThiNTE5MTFjYzgwIn0=; _ga=GA1.2.2042202377.1525247839; _gat=1; _gid=GA1.2.1034461360.1593095881; has_js=1; _pxff_fp=1; _pxff_rf=1; pxvid=44e1b040-4dde-11e8-b1dc-f15e898556c7',
|
||||||
|
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15',
|
||||||
);
|
);
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ function fetch_jquery($fp)
|
|||||||
foreach ($m[1] as $k => $file) {
|
foreach ($m[1] as $k => $file) {
|
||||||
if (!is_cached($file)) {
|
if (!is_cached($file)) {
|
||||||
echo 'Downloading: ' . 'https://code.jquery.com/' . $file . PHP_EOL;
|
echo 'Downloading: ' . 'https://code.jquery.com/' . $file . PHP_EOL;
|
||||||
$data = fetch('https://code.jquery.com/' . $file);
|
$data = fetch('https://code.jquery.com/' . $file) . PHP_EOL;
|
||||||
if (base64_encode(hash('sha256', $data, true)) != $m[2][$k]) {
|
if (base64_encode(hash('sha256', $data, true)) != $m[2][$k]) {
|
||||||
die('Hash mismatch' . PHP_EOL);
|
die('Hash mismatch' . PHP_EOL);
|
||||||
}
|
}
|
||||||
@@ -160,7 +161,7 @@ function fetch_typo3($fp)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$file = 'type3-' . $release->version . '.tar.gz';
|
$file = 'type3-' . $release->version . '.tar.gz';
|
||||||
fetch_archive($file, $release->url->tar, $release->checksums->tar->sha1, 'sha1');
|
fetch_archive($file, 'https://get.typo3.org' . $release->url->tar, $release->checksums->tar->sha1, 'sha1');
|
||||||
hash_archive($fp, $file);
|
hash_archive($fp, $file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,39 +185,41 @@ function fetch_pagekit($fp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ignored releases are: alpha, beta, rc, dev
|
// Ignored releases are: alpha, beta, rc, dev
|
||||||
function fetch_drupal($fp, $versions)
|
function fetch_drupal($fp)
|
||||||
{
|
{
|
||||||
foreach ($versions as $version => $id) {
|
echo 'Fetching Drupal ' . PHP_EOL;
|
||||||
echo 'Fetching Drupal ' . $version . PHP_EOL;
|
|
||||||
|
|
||||||
$page = 0;
|
$page = 0;
|
||||||
$pages = false;
|
$pages = false;
|
||||||
do {
|
do {
|
||||||
$data = fetch('https://www.drupal.org/project/drupal/releases?api_version%5B%5D=' . $id . '&page=' .$page);
|
$data = fetch('https://www.drupal.org/project/drupal/releases?page=' . $page);
|
||||||
|
|
||||||
// pagination init
|
// pagination init
|
||||||
if ($pages === false && preg_match('/&page=(\d+)">last »<\/a>/', $data, $m)) {
|
if ($pages === false && preg_match('/\?page=(\d+)">last »<\/a>/', $data, $m)) {
|
||||||
$pages = $m[1];
|
$pages = $m[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
preg_match_all(
|
preg_match_all(
|
||||||
'/data-th="Download">(.*?)<a href="(https:\/\/ftp\.drupal\.org\/files\/projects\/(drupal\-([0-9.]+)\.tar\.gz)).*?md5 hash">\s*([a-z0-9]{32})\s*<\/td>/is',
|
'/<a href="(\/project\/drupal\/releases\/(\d\.\d\.\d))">drupal/i',
|
||||||
$data,
|
$data,
|
||||||
$m
|
$m
|
||||||
);
|
);
|
||||||
foreach ($m[3] as $k => $file) {
|
foreach ($m[1] as $k => $ver_uri) {
|
||||||
fetch_archive($file, $m[2][$k], $m[5][$k], 'md5');
|
$ver_data = fetch('https://www.drupal.org' . $ver_uri);
|
||||||
|
if (!preg_match('/<span class="field-content hash">([a-z0-9]+)<\/span>/i', $ver_data, $ver_m)) {
|
||||||
|
die('Missing hash info: ' . $m[2][$k]);
|
||||||
|
}
|
||||||
|
$file = 'drupal-' . $m[2][$k] . '.tar.gz';
|
||||||
|
fetch_archive($file, 'https://ftp.drupal.org/files/projects/' . $file, $ver_m[1], 'md5');
|
||||||
hash_archive($fp, $file);
|
hash_archive($fp, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pages === false) {
|
if ($pages === false) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$page++;
|
$page++;
|
||||||
} while ($page <= $pages);
|
} while ($page <= $pages);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function fetch_joomla($fp, $versions)
|
function fetch_joomla($fp, $versions)
|
||||||
{
|
{
|
||||||
@@ -271,24 +274,7 @@ fetch_jquery($fp);
|
|||||||
fetch_wordpress($fp);
|
fetch_wordpress($fp);
|
||||||
fetch_typo3($fp);
|
fetch_typo3($fp);
|
||||||
fetch_pagekit($fp);
|
fetch_pagekit($fp);
|
||||||
fetch_drupal(
|
fetch_drupal($fp);
|
||||||
$fp,
|
|
||||||
[
|
|
||||||
'9.x' => 39794,
|
|
||||||
'8.x' => 7234,
|
|
||||||
'7.x' => 103,
|
|
||||||
'6.x' => 87,
|
|
||||||
'5.x' => 78,
|
|
||||||
'4.7.x' => 79,
|
|
||||||
'4.6.x' => 80,
|
|
||||||
'4.5.x' => 81,
|
|
||||||
'4.4.x' => 82,
|
|
||||||
'4.3.x' => 83,
|
|
||||||
'4.2.x' => 84,
|
|
||||||
'4.1.x' => 85,
|
|
||||||
'4.0.x' => 86
|
|
||||||
]
|
|
||||||
);
|
|
||||||
fetch_joomla($fp, ['3.0' => 3, '2.5' => 25, '1.5' => 15, '1.0' => 10]);
|
fetch_joomla($fp, ['3.0' => 3, '2.5' => 25, '1.5' => 15, '1.0' => 10]);
|
||||||
|
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
|
|||||||
@@ -275,3 +275,14 @@ fc8f1e9f0ff666af7beb3f61b055c0e8 /core/model/smarty/sysplugins/smarty_internal_
|
|||||||
bb127b5ce56b45e8b4b91de2e60dd9eb /assets/components/googleanalytics/js/mgr/libs/highcharts.js
|
bb127b5ce56b45e8b4b91de2e60dd9eb /assets/components/googleanalytics/js/mgr/libs/highcharts.js
|
||||||
7d7958bb0a9438a8966807f9202d0bce /assets/components/tinymce/jscripts/tiny_mce/plugins/spellchecker/classes/PSpellShell.php
|
7d7958bb0a9438a8966807f9202d0bce /assets/components/tinymce/jscripts/tiny_mce/plugins/spellchecker/classes/PSpellShell.php
|
||||||
3ee0a4d8a06cedc0a56f29e8f351ef72 /pclzip-2-8-2/pclzip.lib.php
|
3ee0a4d8a06cedc0a56f29e8f351ef72 /pclzip-2-8-2/pclzip.lib.php
|
||||||
|
abfd2987afd1f66e3eed50bebbeb6750 /sucuri-scanner-1.8.24/src/base.lib.php
|
||||||
|
78477b67cb223e4504689fef33119884 /sucuri-scanner-1.8.24/src/sitecheck.lib.php
|
||||||
|
e48460f6ef0c911dc5ad558c57bfd52f /sucuri-scanner-1.8.24/src/integrity.lib.php
|
||||||
|
29f34168b7384cca58ba64885461e115 wp-admin/includes/class-pclzip.php -> Wordpress Core 6.0
|
||||||
|
a54895edc1402cf1b7b5ecd3f5d85e6b wp-includes/formatting.php -> Wordpress Core 6.0
|
||||||
|
178f2fbc6a48f605ed84b156103d5366 wp-content/plugins/wordpress-seo/vendor_prefixed/guzzlehttp/guzzle/src/Middleware.php -> Yoast SEO plugin 19.2
|
||||||
|
1e2d246c57d2123aa8938c8263cb1d3d wp-content/plugins/wordpress-seo/admin/tracking/class-tracking-server-data.php -> Yoast SEO plugin 19.2
|
||||||
|
cacb5670ebb2de31976a4b2eb06cac86 wp-content/plugins/worker/src/MWP/ServiceContainer/Abstract.php -> managewp plugin 4.9.14 from managewp.com
|
||||||
|
ffa76b9ff298702a733747521cfdee69 wp-content/plugins/worker/src/MWP/Action/GetState.php -> managewp plugin 4.9.14 from managewp.com
|
||||||
|
ccce5f45d1ac66bd2bebe75d666b5720 wp-content/plugins/redirection/models/regex.php
|
||||||
|
ae810d74d638c611d8bd958777c9ac6a wp-content/plugins/ssl-insecure-content-fixer/includes/nonces.php
|
||||||
|
|||||||
Reference in New Issue
Block a user