├── .dockerignore
├── .github
└── workflows
│ ├── docker.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── data
├── php.yar
├── samples
│ ├── artificial
│ │ ├── bypasses.php
│ │ ├── dodgy.php
│ │ └── obfuscated.php
│ ├── classic
│ │ ├── ajaxshell.php
│ │ ├── angel.php
│ │ ├── b374k.php
│ │ ├── c100.php
│ │ ├── c99.php
│ │ ├── cyb3rsh3ll.php
│ │ ├── r57.php
│ │ ├── simattacker.php
│ │ └── sosyete.php
│ ├── cpanel.php
│ ├── freepbx.php
│ ├── obfuscators
│ │ ├── cipher_design.php
│ │ ├── online_php_obfuscator.php
│ │ └── phpencode.php
│ ├── real
│ │ ├── awvjtnz.php
│ │ ├── exceptions.php
│ │ ├── guidtz.php
│ │ ├── ice.php
│ │ ├── include.php
│ │ ├── nano.php
│ │ ├── ninja.php
│ │ ├── novahot.php
│ │ ├── srt.php
│ │ └── sucuri_2014_04.php
│ └── undetected
│ │ └── smart.php
├── whitelist.yar
└── whitelists
│ ├── custom.yar
│ ├── drupal.yar
│ ├── magento1ce.yar
│ ├── magento2.yar
│ ├── phpmyadmin.yar
│ ├── prestashop.yar
│ ├── symfony.yar
│ └── wordpress.yar
├── go.mod
├── go.sum
├── main.go
├── tests.sh
└── utils
├── generate_whitelist.py
├── magento1_whitelist.sh
├── magento2_whitelist.sh
└── mass_whitelist.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .github/
3 | utils/
4 | php-malware-finder
5 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Docker
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | tags:
8 | - '*'
9 | pull_request:
10 | branches:
11 | - master
12 |
13 | jobs:
14 |
15 | docker-image:
16 | name: Build Image
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - name: Checkout code
21 | uses: actions/checkout@v3
22 |
23 | - name: Setup docker
24 | uses: docker/setup-buildx-action@v2
25 |
26 | - name: Log into container registry
27 | uses: docker/login-action@v2
28 | with:
29 | registry: ghcr.io
30 | username: ${{ github.repository_owner }}
31 | password: ${{ secrets.GITHUB_TOKEN }}
32 |
33 | - name: Build image
34 | run: make docker
35 |
36 | - name: Test image
37 | run: make docker-tests
38 |
39 | - name: Publish image
40 | if: github.event_name != 'pull_request'
41 | run: make docker-publish
42 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 |
10 | release:
11 | name: Release
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - name: Set up Go
18 | uses: actions/setup-go@v3
19 | with:
20 | go-version: '^1.18'
21 |
22 | # apt repos don't have YARA v4.2, install it from git
23 | - name: Install YARA
24 | run: |
25 | git clone --depth 1 https://github.com/virustotal/yara.git
26 | cd yara
27 | bash ./build.sh
28 | sudo make install
29 | cd ..
30 |
31 | - name: Build
32 | run: make
33 |
34 | - name: Create release
35 | uses: ncipollo/release-action@v1
36 | with:
37 | artifacts: "php-malware-finder"
38 | token: ${{ secrets.GITHUB_TOKEN }}
39 | allowUpdates: true
40 | omitBody: true
41 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test Suite
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 |
13 | test:
14 | name: Test
15 | runs-on: ubuntu-latest
16 | strategy:
17 | matrix:
18 | go_version:
19 | - '~1.17'
20 | - '^1.18'
21 |
22 | steps:
23 | - name: Checkout code
24 | uses: actions/checkout@v3
25 |
26 | - name: Setup Go
27 | uses: actions/setup-go@v3
28 | with:
29 | go-version: ${{ matrix.go_version }}
30 |
31 | # apt repos don't have YARA v4.2, install it from git
32 | - name: Install YARA
33 | run: |
34 | git clone --depth 1 https://github.com/virustotal/yara.git
35 | cd yara
36 | bash ./build.sh
37 | sudo make install
38 | cd ..
39 |
40 | - name: Run tests
41 | run: |
42 | make
43 | make tests
44 | env:
45 | LD_LIBRARY_PATH: /usr/local/lib
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | php-malware-finder
2 | .idea
3 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | First off, thank you for considering contributing to php-malware-finder.
4 |
5 | ### 1. Where do I go from here?
6 |
7 | If you've noticed a bug, an undetected sample or have a question,
8 | [search the issue tracker](https://github.com/nbs-system/php-malware-finder/issues)
9 | to see if someone else has already created a ticket. If not, go ahead and
10 | [make one](https://github.com/nbs-system/php-malware-finder/issues/new)!
11 |
12 | ### 2. Fork & create a branch
13 |
14 | If this is something you think you can fix,
15 | then [fork php-malware-finder](https://help.github.com/articles/fork-a-repo) and
16 | create a branch with a descriptive name.
17 |
18 | A good branch name would be (where issue #325 is the ticket you're working on):
19 |
20 | ```sh
21 | git checkout -b add_new_sample_wp_bruteforcer
22 | ```
23 |
24 | ### 3. Get the test suite running
25 |
26 | Just type `make tests`, the testsuite will be run automatically.
27 |
28 | ### 6. Make a Pull Request
29 |
30 | At this point, you should switch back to your master branch and make sure it's
31 | up to date with our upstream master branch:
32 |
33 | ```sh
34 | git remote add upstream git@github.com:nbs-system/php-malware-finder.git
35 | git checkout master
36 | git pull upstream master
37 | ```
38 |
39 | Then update your feature branch from your local copy of master, and push it!
40 |
41 | ```sh
42 | git checkout add_new_sample_wp_bruteforcer
43 | git rebase master
44 | git push --set-upstream origin add_new_sample_wp_bruteforcer
45 | ```
46 |
47 | Finally, go to GitHub and [make a Pull Request](https://help.github.com/articles/creating-a-pull-request) :D
48 |
49 | Travis CI will [run our test suite](https://travis-ci.org/nbs-system/php-malware-finder).
50 | We care about quality, so your PR won't be merged until all tests are passing.
51 |
52 | ### 7. Keeping your Pull Request updated
53 |
54 | If a maintainer asks you to "rebase" your PR, they're saying that a lot of code
55 | has changed, and that you need to update your branch so it's easier to merge.
56 |
57 | To learn more about rebasing in Git, there are a lot of [good](http://git-scm.com/book/en/Git-Branching-Rebasing)
58 | [resources](https://help.github.com/articles/interactive-rebase) but here's the suggested workflow:
59 |
60 | ```sh
61 | git checkout add_new_sample_wp_bruteforcer
62 | git pull --rebase upstream master
63 | git push --force-with-lease add_new_sample_wp_bruteforcer
64 | ```
65 |
66 | ### 8. Merging a PR (maintainers only)
67 |
68 | A PR can only be merged into master by a maintainer if:
69 |
70 | 1. It is passing CI.
71 | 2. It has no requested changes.
72 | 3. It is up to date with current master.
73 |
74 | Any maintainer is allowed to merge a PR if all of these conditions are met.
75 |
76 | ### 9. Shipping a release (maintainers only)
77 |
78 | 1. Make sure that all pending and mergeable pull requests are in
79 | 2. Make sure that the all the tests are passing, with `make tests`
80 | 3. Update the Debian changelog in `./debian/changelog` with `dch -i`
81 | 4. Commit the result
82 | 5. Create a tag for the release:
83 |
84 | ```sh
85 | git checkout master
86 | git pull origin master
87 | make tests
88 | git config user.signingkey 498C46FF087EDC36E7EAF9D445414A82A9B22D78
89 | git config user.email security@nbs-system.com
90 | git tag -s v$MAJOR.$MINOR.$PATCH -m "v$MAJOR.$MINOR.$PATCH"
91 | git push --tags
92 | ```
93 |
94 | 6. Build the debian package with `make deb`
95 | 7. Create the [release on github](https://github.com/nbs-system/php-malware-finder/releases)
96 | 8. Do the *secret release dance*
97 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS build
2 | WORKDIR /app
3 |
4 | # install build dependencies
5 | RUN apk add --no-cache build-base git && \
6 | apk add --no-cache -X https://dl-cdn.alpinelinux.org/alpine/edge/testing yara-dev
7 |
8 | # copy and build PMF
9 | COPY . .
10 | RUN make
11 |
12 | FROM golang:alpine
13 | LABEL org.opencontainers.image.source="https://github.com/jvoisin/php-malware-finder"
14 | WORKDIR /app
15 |
16 | # install dependencies
17 | RUN apk add --no-cache libressl && \
18 | apk add --no-cache -X https://dl-cdn.alpinelinux.org/alpine/edge/testing yara
19 |
20 | # copy binary from build container
21 | COPY --from=build /app/php-malware-finder /app
22 |
23 | ENTRYPOINT ["/app/php-malware-finder", "-v", "-a", "-c", "/data"]
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean rebuild update-deps tests help docker docker-tests docker-publish
2 |
3 | NAME := php-malware-finder
4 | TAG_COMMIT := $(shell git rev-list --abbrev-commit --all --max-count=1)
5 | VERSION := $(shell git describe --abbrev=0 --tags --exact-match $(TAG_COMMIT) 2>/dev/null || true)
6 | IMAGE_VERSION := $(VERSION)
7 | DATE := $(shell git log -1 --format=%cd --date=format:"%Y%m%d%H%M")
8 | ifeq ($(VERSION),)
9 | VERSION := $(DATE)
10 | IMAGE_VERSION := latest
11 | endif
12 | LDFLAGS := "-X main.version=$(VERSION)"
13 | GO_FLAGS := -o $(NAME) -ldflags $(LDFLAGS)
14 | IMAGE_REGISTRY := ghcr.io
15 | IMAGE_REGISTRY_USER := jvoisin
16 | IMAGE_NAME := $(IMAGE_REGISTRY)/$(IMAGE_REGISTRY_USER)/$(NAME)
17 |
18 | all: php-malware-finder
19 |
20 | php-malware-finder: ## Build application
21 | @go build $(GO_FLAGS) .
22 |
23 | clean: ## Delete build artifacts
24 | @rm -f $(NAME)
25 |
26 | rebuild: clean all ## Delete build artifacts and rebuild
27 |
28 | update-deps: ## Update dependencies
29 | @go get -u .
30 | @go mod tidy -v
31 |
32 | tests: php-malware-finder ## Run test suite
33 | @bash ./tests.sh
34 |
35 | docker: ## Build docker image
36 | docker pull $(IMAGE_NAME):latest || true
37 | docker build --pull --cache-from=$(IMAGE_NAME):latest -t $(IMAGE_NAME):latest .
38 | docker tag $(IMAGE_NAME):latest $(IMAGE_NAME):$(IMAGE_VERSION)
39 |
40 | docker-tests: ## Run docker image against the samples folder
41 | @(docker run --rm -v $(shell pwd)/data/samples:/data $(IMAGE_NAME):latest && exit 1) || (test $$? -eq 255 || exit 1)
42 |
43 | docker-publish: ## Push docker image to the container registry
44 | @docker push $(IMAGE_NAME):latest
45 | @(test "$(IMAGE_VERSION)" != "latest" && docker push $(IMAGE_NAME):$(IMAGE_VERSION)) || true
46 |
47 | help: ## Show this help
48 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # PHP Malware Finder
4 |
5 | ```
6 | _______ __ __ _______
7 | | ___ || |_| || |
8 | | | | || || ___|
9 | | |___| || || |___ Webshell finder,
10 | | ___|| || ___| kiddies hunter,
11 | | | | ||_|| || | website cleaner.
12 | |___| |_| |_||___|
13 |
14 | Detect potentially malicious PHP files.
15 | ```
16 |
17 | ## What does it detect?
18 |
19 | PHP-malware-finder does its very best to detect obfuscated/dodgy code as well as
20 | files using PHP functions often used in malwares/webshells.
21 |
22 | The following list of encoders/obfuscators/webshells are also detected:
23 |
24 | * [Bantam](https://github.com/gellin/bantam)
25 | * [Best PHP Obfuscator]( http://www.pipsomania.com/best_php_obfuscator.do )
26 | * [Carbylamine]( https://code.google.com/p/carbylamine/ )
27 | * [Cipher Design]( http://cipherdesign.co.uk/service/php-obfuscator )
28 | * [Cyklodev]( http://sysadmin.cyklodev.com/online-php-obfuscator/ )
29 | * [Joes Web Tools Obfuscator]( http://www.joeswebtools.com/security/php-obfuscator/ )
30 | * [P.A.S]( http://profexer.name/pas/download.php )
31 | * [PHP Jiami]( http://www.phpjiami.com/ )
32 | * [Php Obfuscator Encode]( http://w3webtools.com/encode-php-online/ )
33 | * [SpinObf]( http://mohssen.org/SpinObf.php )
34 | * [Weevely3]( https://github.com/epinna/weevely3 )
35 | * [atomiku]( http://atomiku.com/online-php-code-obfuscator/ )
36 | * [cobra obfuscator]( http://obfuscator.uk/example/ )
37 | * [nano]( https://github.com/UltimateHackers/nano )
38 | * [novahot]( https://github.com/chrisallenlane/novahot )
39 | * [phpencode]( http://phpencode.org )
40 | * [tennc]( http://tennc.github.io/webshell/ )
41 | * [web-malware-collection]( https://github.com/nikicat/web-malware-collection )
42 | * [webtoolsvn]( http://www.webtoolsvn.com/en-decode/ )
43 | * [Kraken-ng]( https://github.com/kraken-ng/ )
44 |
45 |
46 | Of course it's **trivial** to bypass PMF,
47 | but its goal is to catch kiddies and idiots,
48 | not people with a working brain.
49 | If you report a stupid tailored bypass for PMF, you likely belong to one (or
50 | both) category, and should re-read the previous statement.
51 |
52 | ## How does it work?
53 |
54 | Detection is performed by crawling the filesystem and testing files against a
55 | [set](https://github.com/jvoisin/php-malware-finder/blob/master/php-malware-finder/php.yar)
56 | of [YARA](http://virustotal.github.io/yara/) rules. Yes, it's that simple!
57 |
58 | Instead of using a *hash-based* approach,
59 | PMF tries as much as possible to use semantic patterns, to detect things like
60 | "a `$_GET` variable is decoded two times, unzipped,
61 | and then passed to some dangerous function like `system`".
62 |
63 | ## Installation
64 |
65 | ### From source
66 |
67 | - Install Go >= 1.17 (using your package manager, or [manually](https://go.dev/doc/install))
68 | - Install libyara >= 4.2 (using your package manager, or [from source](https://yara.readthedocs.io/en/stable/gettingstarted.html))
69 | - Download php-malware-finder: `git clone https://github.com/jvoisin/php-malware-finder.git`
70 | - Build php-malware-finder: `cd php-malware-finder && make`
71 |
72 | or replace the last 2 steps with `go install github.com/jvoisin/php-malware-finder`,
73 | which will directly compile and install PMF in your `${GOROOT}/bin` folder.
74 |
75 | ## How to use it?
76 |
77 | ```
78 | $ ./php-malware-finder -h
79 | Usage:
80 | php-malware-finder [OPTIONS] [Target]
81 |
82 | Application Options:
83 | -r, --rules-dir= Alternative rules location (default: embedded rules)
84 | -a, --show-all Display all matched rules
85 | -f, --fast Enable YARA's fast mode
86 | -R, --rate-limit= Max. filesystem ops per second, 0 for no limit (default: 0)
87 | -v, --verbose Verbose mode
88 | -w, --workers= Number of workers to spawn for scanning (default: 32)
89 | -L, --long-lines Check long lines
90 | -c, --exclude-common Do not scan files with common extensions
91 | -i, --exclude-imgs Do not scan image files
92 | -x, --exclude-ext= Additional file extensions to exclude
93 | -u, --update Update rules
94 | -V, --version Show version number and exit
95 |
96 | Help Options:
97 | -h, --help Show this help message
98 | ```
99 |
100 | Or if you prefer to use `yara`:
101 |
102 | ```
103 | $ yara -r ./data/php.yar /var/www
104 | ```
105 |
106 | Please keep in mind that you should use at least YARA 3.4 because we're using
107 | [hashes]( https://yara.readthedocs.org/en/latest/modules/hash.html ) for the
108 | whitelist system, and greedy regexps. Please note that if you plan to build
109 | yara from sources, libssl-dev must be installed on your system in order to
110 | have support for hashes.
111 |
112 | Oh, and by the way, you can run the *comprehensive* testsuite with `make tests`.
113 |
114 | ### Docker
115 |
116 | If you want to avoid having to install Go and libyara, you can also use our
117 | docker image and simply mount the folder you want to scan to the container's
118 | `/data` directory:
119 |
120 | ```
121 | $ docker run --rm -v /folder/to/scan:/data ghcr.io/jvoisin/php-malware-finder
122 | ```
123 |
124 | ## Whitelisting
125 |
126 | Check the [whitelist.yar](https://github.com/jvoisin/php-malware-finder/blob/master/php-malware-finder/whitelist.yar) file.
127 | If you're lazy, you can generate whitelists for entire folders with the
128 | [generate_whitelist.py](https://github.com/jvoisin/php-malware-finder/blob/master/php-malware-finder/utils/generate_whitelist.py) script.
129 |
130 | ## Why should I use it instead of something else?
131 |
132 | Because:
133 | - It doesn't use [a single rule per sample](
134 | https://github.com/Neo23x0/signature-base/blob/e264d66a8ea3be93db8482ab3d639a2ed3e9c949/yara/thor-webshells.yar
135 | ), since it only cares about finding malicious patterns, not specific webshells
136 | - It has a [complete testsuite](https://github.com/jvoisin/php-malware-finder/actions), to avoid regressions
137 | - Its whitelist system doesn't rely on filenames
138 | - It doesn't rely on (slow) [entropy computation]( https://en.wikipedia.org/wiki/Entropy_(information_theory) )
139 | - It uses a ghetto-style static analysis, instead of relying on file hashes
140 | - Thanks to the aforementioned pseudo-static analysis, it works (especially) well on obfuscated files
141 |
142 | ## Licensing
143 |
144 | PHP-malware-finder is
145 | [licensed](https://github.com/jvoisin/php-malware-finder/blob/master/php-malware-finder/LICENSE)
146 | under the GNU Lesser General Public License v3.
147 |
148 | The _amazing_ YARA project is licensed under the Apache v2.0 license.
149 |
150 | Patches, whitelists or samples are of course more than welcome.
151 |
--------------------------------------------------------------------------------
/data/php.yar:
--------------------------------------------------------------------------------
1 | import "hash"
2 | include "whitelist.yar"
3 |
4 | /*
5 | Detect:
6 | - phpencode.org
7 | - http://www.pipsomania.com/best_php_obfuscator.do
8 | - http://atomiku.com/online-php-code-obfuscator/
9 | - http://www.webtoolsvn.com/en-decode/
10 | - http://obfuscator.uk/example/
11 | - http://w3webtools.com/encode-php-online/
12 | - http://www.joeswebtools.com/security/php-obfuscator/
13 | - https://github.com/epinna/weevely3
14 | - http://cipherdesign.co.uk/service/php-obfuscator
15 | - http://sysadmin.cyklodev.com/online-php-obfuscator/
16 | - http://mohssen.org/SpinObf.php
17 | - https://code.google.com/p/carbylamine/
18 | - https://github.com/tennc/webshell
19 |
20 | - https://github.com/wireghoul/htshells
21 |
22 | Thanks to:
23 | - https://stackoverflow.com/questions/3115559/exploitable-php-functions
24 | */
25 |
26 | global private rule IsPhp
27 | {
28 | strings:
29 | $php = /<\?[^x]/
30 |
31 | condition:
32 | $php and filesize < 5MB
33 | }
34 |
35 | rule NonPrintableChars
36 | {
37 | strings:
38 | /*
39 | Searching only for non-printable characters completely kills the perf,
40 | so we have to use atoms (https://gist.github.com/Neo23x0/e3d4e316d7441d9143c7)
41 | to get an acceptable speed.
42 | */
43 | $non_printables = /(function|return|base64_decode).{,256}[^\x09-\x0d\x20-\x7E]{3}/
44 |
45 | condition:
46 | (any of them) and not IsWhitelisted
47 | }
48 |
49 |
50 | rule PasswordProtection
51 | {
52 | strings:
53 | $md5 = /md5\s*\(\s*\$_(GET|REQUEST|POST|COOKIE|SERVER)[^)]+\)\s*===?\s*['"][0-9a-f]{32}['"]/ nocase
54 | $sha1 = /sha1\s*\(\s*\$_(GET|REQUEST|POST|COOKIE|SERVER)[^)]+\)\s*===?\s*['"][0-9a-f]{40}['"]/ nocase
55 | condition:
56 | (any of them) and not IsWhitelisted
57 | }
58 |
59 | rule ObfuscatedPhp
60 | {
61 | strings:
62 | $eval = /(<\?php|[;{}])[ \t]*@?(eval|preg_replace|system|assert|passthru|(pcntl_)?exec|shell_exec|call_user_func(_array)?)\s*\(/ nocase // ;eval( <- this is dodgy
63 | $eval_comment = /(eval|preg_replace|system|assert|passthru|(pcntl_)?exec|shell_exec|call_user_func(_array)?)\/\*[^\*]*\*\/\(/ nocase // eval/*lol*/( <- this is dodgy
64 | $b374k = "'ev'.'al'"
65 | $align = /(\$\w+=[^;]*)*;\$\w+=@?\$\w+\(/ //b374k
66 | $weevely3 = /\$\w=\$[a-zA-Z]\('',\$\w\);\$\w\(\);/ // weevely3 launcher
67 | $c99_launcher = /;\$\w+\(\$\w+(,\s?\$\w+)+\);/ // http://bartblaze.blogspot.fr/2015/03/c99shell-not-dead.html
68 | $nano = /\$[a-z0-9-_]+\[[^]]+\]\(/ //https://github.com/UltimateHackers/nano
69 | $ninja = /base64_decode[^;]+getallheaders/ //https://github.com/UltimateHackers/nano
70 | $variable_variable = /\${\$[0-9a-zA-z]+}/
71 | $too_many_chr = /(chr\([\d]+\)\.){8}/ // concatenation of more than eight `chr()`
72 | $concat = /(\$[^\n\r]+\.){5}/ // concatenation of more than 5 words
73 | $concat_with_spaces = /(\$[^\n\r]+\. ){5}/ // concatenation of more than 5 words, with spaces
74 | $var_as_func = /\$_(GET|POST|COOKIE|REQUEST|SERVER)\s*\[[^\]]+\]\s*\(/
75 | $comment = /\/\*([^*]|\*[^\/])*\*\/\s*\(/ // eval /* comment */ (php_code)
76 | condition:
77 | (any of them) and not IsWhitelisted
78 | }
79 |
80 | rule DodgyPhp
81 | {
82 | strings:
83 | $basedir_bypass = /curl_init\s*\(\s*["']file:\/\// nocase
84 | $basedir_bypass2 = "file:file:///" // https://www.intelligentexploit.com/view-details.html?id=8719
85 | $disable_magic_quotes = /set_magic_quotes_runtime\s*\(\s*0/ nocase
86 |
87 | $execution = /\b(popen|eval|assert|passthru|exec|include|system|pcntl_exec|shell_exec|base64_decode|`|array_map|ob_start|call_user_func(_array)?)\s*\(\s*(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/ nocase // function that takes a callback as 1st parameter
88 | $execution2 = /\b(array_filter|array_reduce|array_walk(_recursive)?|array_walk|assert_options|uasort|uksort|usort|preg_replace_callback|iterator_apply)\s*\(\s*[^,]+,\s*(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/ nocase // functions that takes a callback as 2nd parameter
89 | $execution3 = /\b(array_(diff|intersect)_u(key|assoc)|array_udiff)\s*\(\s*([^,]+\s*,?)+\s*(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))\s*\[[^]]+\]\s*\)+\s*;/ nocase // functions that takes a callback as 2nd parameter
90 |
91 | $htaccess = "SetHandler application/x-httpd-php"
92 | $iis_com = /IIS:\/\/localhost\/w3svc/
93 | $include = /include\s*\(\s*[^\.]+\.(png|jpg|gif|bmp)/ // Clever includes
94 | $ini_get = /ini_(get|set|restore)\s*\(\s*['"](safe_mode|open_basedir|disable_(function|classe)s|safe_mode_exec_dir|safe_mode_include_dir|register_globals|allow_url_include)/ nocase
95 | $pr = /(preg_replace(_callback)?|mb_ereg_replace|preg_filter)\s*\([^)]*(\/|\\x2f)(e|\\x65)['"]/ nocase // http://php.net/manual/en/function.preg-replace.php
96 | $register_function = /register_[a-z]+_function\s*\(\s*['"]\s*(eval|assert|passthru|exec|include|system|shell_exec|`)/ // https://github.com/nbs-system/php-malware-finder/issues/41
97 | $safemode_bypass = /\x00\/\.\.\/|LD_PRELOAD/
98 | $shellshock = /\(\)\s*{\s*[a-z:]\s*;\s*}\s*;/
99 | $udp_dos = /fsockopen\s*\(\s*['"]udp:\/\// nocase
100 | $various = "
51 |
52 |
53 |
54 |
55 |
56 |