bash + python script to find wordpress malware and delete it.

OK so there are three parts to this script.

1. An exclude list
2. The script
3. A python script to check for randomness.

The idea is it finds spammy-looking filenames and then runs a python entropy script to check if the file is random-ish internally. It then offers to delete. Note that real virii have an entropy in the 5.8 region, because they use code obfuscators. If the entropy is lower than that rather skip deleting and manually check the file.

I know about wordfence-cli but I do not like it.

Flags are -f for find and -d to delete. It prompts for each delete.

1. The script

sudo mkdir -p /scripts/wordpress/
sudo vi /scripts/wordpress/find_spam_crap.sh

#!/bin/bash


export TERM='xterm-256color'


# File containing known, safe PHP filenames to ignore

IGNORE_FILE="/scripts/wordpress/find_spam_crap.ignore"


# File containing all known filenames for exhaustive English exclusion

EXCLUDE_FILE="/scripts/wordpress/find_spam_crap.exclude"


# Function to load ignore list from the file

load_ignore_list() {

    if [ -f "$IGNORE_FILE" ]; then

        # Load ignore list into an array

        ignore_list=$(cat "$IGNORE_FILE")

    else

        echo "Ignore file not found: $IGNORE_FILE"

        exit 1

    fi

}


# Function to load exclusion list from the file

load_exclude_list() {

    if [ -f "$EXCLUDE_FILE" ]; then

        # Load exclude list into an array

        exclude_list=$(cat "$EXCLUDE_FILE")

    else

        echo "Exclude file not found: $EXCLUDE_FILE"

        exit 1

    fi

}


# Function to check if a filename is in the ignore list

is_ignored() {

    local filename=$(basename "$1")

    echo "$ignore_list" | grep -q "^$filename$"

}


# Function to check if a filename is in the exclude list

is_excluded() {

    local filename=$(basename "$1")

    echo "$exclude_list" | grep -q "^$filename$"

}


# Function to check if a filename contains numbers

contains_numbers() {

    echo "$1" | grep -q "[0-9]"

}


# Function to find suspicious PHP files with 8-character names

find_suspicious_files() {

    # List of directories to search in

    search_dirs=(

        "/var/www/"

        "/zfs2/filegator/repository/"

        "/var/www/issa.africa/"

    )


    counter=0


    for dir in "${search_dirs[@]}"; do

        # Find PHP files with 8-character names

        for file in $(find "$dir" -name "[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9].php"); do

            counter=$((counter + 1))

            echo -ne "Processing file $counter\r"


            if ! is_ignored "$file" && ! is_excluded "$file" && contains_numbers "$(basename "$file")"; then

                # Check file entropy

                entropy=$(python3 /scripts/wordpress/find_spam_crap_entropy.py "$file")

                echo ""

                echo "____________________"

                echo "$file (Entropy: $entropy)"

                echo "____________________"

                if [ "$FLAG" = "-d" ]; then

                    echo "Do you want to delete this file? $file (y/n)"

                    read -r answer

                    if [ "$answer" = "y" ]; then

                        rm -f "$file"

                        echo "Deleted $file"

                    else

                        echo "Skipped $file"

                    fi

                fi

            fi

        done

    done

}


# Main script logic

if [ -z "$1" ]; then

    echo "Please give a flag of -f to find or -d to delete"

    exit

fi


FLAG=$1


if [ "$FLAG" = "-f" ] || [ "$FLAG" = "-d" ]; then

    # Load ignore list and exclude list

    load_ignore_list

    load_exclude_list

    

    # Find suspicious files (and optionally delete them)

    find_suspicious_files


    if [ "$FLAG" = "-f" ]; then

        # Additional non-PHP finds

        find /var/www/ -name "radio.txt"

        find /var/www/ -name ".????????.mo"

    fi

elif [ "$FLAG" = "-d" ]; then

    # Read the list of WordPress directories from a file and delete PHP files in certain directories

    while IFS= read -r i; do

        for file in $(find /var/www/$i/wp-content/cache/ -name "*.php"); do

            echo "Do you want to delete this file? $file (y/n)"

            read -r answer

            if [ "$answer" = "y" ]; then

                rm -f "$file"

                echo "Deleted $file"

            else

                echo "Skipped $file"

            fi

        done

        for file in $(find /var/www/$i/.AppleDouble/ -name "*.php"); do

            echo "Do you want to delete this file? $file (y/n)"

            read -r answer

            if [ "$answer" = "y" ]; then

                rm -f "$file"

                echo "Deleted $file"

            else

                echo "Skipped $file"

            fi

        done

    done < /scripts/wordpress/list.txt

    for file in $(find /var/www/ -name "radio.txt"); do

        echo "Do you want to delete this file? $file (y/n)"

        read -r answer

        if [ "$answer" = "y" ]; then

            rm -f "$file"

            echo "Deleted $file"

        else

            echo "Skipped $file"

        fi

    done

    for file in $(find /var/www/ -name ".????????.ico"); do

        echo "Do you want to delete this file? $file (y/n)"

        read -r answer

        if [ "$answer" = "y" ]; then

            rm -f "$file"

            echo "Deleted $file"

        else

            echo "Skipped $file"

        fi

    done

    

    for file in $(find /var/www/ -name ".????????.mo"); do

        echo "Do you want to delete this file? $file (y/n)"

        read -r answer

        if [ "$answer" = "y" ]; then

            rm -f "$file"

            echo "Deleted $file"

        else

            echo "Skipped $file"

        fi

    done

fi




2. The exclude list

sudo ln -s /scripts/wordpress/find_spam_crap.ignore /scripts/wordpress/find_spam_crap.exclude

sudo vi /scripts/wordpress/find_spam_crap.ignore


rcf50prt.php

XSalsa20.php

Crypto32.php

ChaCha20.php

BLAKE2b.php

Curve25519.php

ChaCha20.php

Poly1305.php

Util.php

Xsalsa20.php

ChaCha20.php

Curve25519.php

HSalsa20.php

Salsa20.php

X25519.php

ChaCha20.php

Ed25519.php

Poly1305.php

SipHash.php

XChaCha20.php

activate.php

activity.php

addmedia.php

archives.php

autoload.php

bookmark.php

builders.php

calendar.php

carousel.php

category.php

checkbox.php

checkout.php

check.php

clearfy.php

collapse.php

colophon.php

comments.php

config.php

controller.php

controls.php

currency.php

database.php

doctypes.php

dropdown.php

elements.php

embedded.php

extended.php

facebook.php

featured.php

features.php

flamingo.php

form-tag.php

freedoms.php

frontend.php

function.php

functions.php

generate.php

gonzales.php

gradient.php

gzdecode.php

handling.php

headline.php

language.php

layout.php

lib.php

licenses.php

lightbox.php

link-add.php

linkedin.php

loginout.php

loop-faq.php

magazine.php

MailPoet.php

mail-tag.php

mainMenu.php

manage.php

manifest.php

masthead.php

maxitems.php

megamenu.php

messages.php

meta-box.php

minitems.php

ms-admin.php

ms-blogs.php

ms-files.php

ms-sites.php

ms-users.php

my-sites.php

nav-menu.php

networks.php

nulllock.php

output.php

overview.php

page-faq.php

particle.php

Partners.php

password.php

patterns.php

payments.php

plugin-compat.php

polaroid.php

polylang.php

position.php

post-nav.php

post-new.php

products.php

profiler.php

promobox.php

quadmenu.php

readonly.php

register.php

renderer.php

requests.php

Requests.php

required.php

response.php

rest-api.php

revision.php

sanitize.php

Sanitize.php

schedule.php

Security.php

seedprod.php

seopress.php

settings.php

showcase.php

showmeta.php

shutdown.php

sidebars.php

singular.php

sitemaps.php

site-new.php

specials.php

standard.php

switcher.php

taxonomy.php

template.php

textarea.php

text-faq.php

timezone.php

tracking.php

translit.php

ubermenu.php

uploader.php

user-new.php

Validate.php

vertical.php

ViewTest.php

wp-login.php




3. The python script

sudo vi /scripts/wordpress/find_spam_crap_entropy.py


import sys

import math


def calculate_entropy(filename):

    try:

        with open(filename, 'rb') as f:

            data = f.read()

        if not data:

            return 0

        # Calculate frequency of each byte value

        freq = [0] * 256

        for byte in data:

            freq[byte] += 1

        # Calculate entropy

        entropy = 0.0

        for count in freq:

            if count > 0:

                prob = count / len(data)

                entropy -= prob * math.log2(prob)

        return entropy

    except Exception as e:

        print(f"Error processing file {filename}: {e}")

        return 0


if __name__ == "__main__":

    if len(sys.argv) != 2:

        print("Usage: python3 find_spam_crap_entropy.py <filename>")

        sys.exit(1)

    filename = sys.argv[1]

    print(calculate_entropy(filename))


Popular posts from this blog

enabling web interface updates over FTP or directly

changing php version

commandline wordfence (wf-cli)