Skip to content
  • FG-86
  • 11 min read
  • Last checked November 8, 2026

Malware Scanner Flagged a File: False Positive or Real Infection?

A security scanner flagged a file on your site. Verify it against official checksums, compare it with a clean copy, check its timestamps and read it safely to decide.

Run the health check

Your security plugin, your host’s malware scanner or a one-off scan has flagged a file: “Suspicious code found”, “Known malware signature”, “File appears to be malicious or unsafe”, or “Modified core file”, with a path such as wp-content/plugins/some-plugin/includes/class-loader.php. The site looks fine. Now you have to decide: is this a real infection, or a scanner being overcautious?

Both happen often. Scanners deliberately err on the side of caution, so legitimate code that is compressed, encoded or unusual can trip them. But real malware is designed to look boring, and deleting a flagged file without understanding it can either break your site or leave the actual infection in place. This guide gives you a repeatable way to decide, starting with checks that settle most cases in minutes and ending with how to read suspicious code without ever running it.

What is happening

Malware scanners use three main techniques, and knowing which one fired tells you a lot about how much to trust the alert.

Technique How it works False positives
Signatures Matches a file’s hash or a specific code pattern against a database of known malware samples Rare for exact hash matches; more common for broad pattern signatures
Heuristics Scores code for risky traits: encoded blobs, eval, dynamic function names, unusual file locations Common. Encoded or minified legitimate code scores high.
File integrity Compares files to the official versions published by WordPress.org or the vendor Low, but it cannot judge premium plugins, custom code or files that should not exist

Scanners also differ in where they look. Plugin scanners such as Wordfence or MalCare read files from inside WordPress. Server scanners such as ImunifyAV, ClamAV or Linux Malware Detect read the whole account, including files outside WordPress. Remote scanners such as Sucuri SiteCheck only see the pages your site serves to the public, so they cannot flag a file at all, only its visible effects.

The question you are answering is narrow: is this exact file the one its author published, and if not, is the difference harmful?

Before you start

  • Access you need: SSH or your host’s file manager, plus WordPress admin. SSH makes the checksum and diff steps far easier.
  • Backup: take a backup before you move or delete anything, and keep a copy of the flagged file outside the web root as evidence. Do not keep it anywhere a browser can reach.
  • How to run commands: connect over SSH and change into the site folder, the one with wp-config.php. WP-CLI is wp on most servers and wp-cli on some managed hosts. stat, diff, grep and find run on the server.
  • Never run the file. Do not open it in a browser (requesting a PHP file by URL executes it), do not run it with php, and do not paste its contents into online “decoders” that execute code. Reading it as text is safe.

Step 1: Record what the scanner said

Before you touch anything, write down the full path, the scanner’s reason or signature name, and any line number it gives. Signature names are often searchable: a name tied to a known malware family is a much stronger signal than a generic “suspicious eval” label. Note whether the scanner says “modified”, “unknown file” or “malicious”, because those lead to different checks below.

Also check whether the scanner flagged one file or many. A single hit in a well-known plugin is often a false positive. Several hits across unrelated folders, or a flagged file in wp-content/uploads, point toward a real infection.

Step 2: Verify official files against checksums

WordPress core

wp core verify-checksums --include-root

This downloads the official checksums for your installed WordPress version and compares every core file against them, without loading WordPress (so a broken or infected plugin cannot interfere). --include-root also warns about non-WordPress items in the root folder. Good output: “Success: WordPress installation verifies against checksums.” Bad output: lines like “Warning: File doesn’t verify against checksum: wp-includes/FILENAME.php” (a core file was changed) or “Warning: File should not exist: wp-includes/FILENAME.php” (an extra file was added inside core folders, a classic hiding place). With --include-root, expect warnings for your own legitimate root files such as wp-config.php, .htaccess or a verification file, and judge each one. If you run a translated WordPress, add --locale=LOCALE, where LOCALE is the code shown in Dashboard, Updates, for example de_DE.

Plugins from WordPress.org

wp plugin verify-checksums --all

--all checks every installed plugin. For each plugin hosted on WordPress.org, it compares your files against the official checksums for your installed version. Good output: “Success: Verified N of N plugins.” Bad output: a table listing a plugin, a file and a message such as “Checksum does not match” (file changed) or “File was added” (extra file). Premium plugins and plugins not hosted on WordPress.org are reported as skipped because there are no official checksums to compare against; for those, use Step 3. To check one plugin, replace --all with its folder name.

If the flagged file passes its checksum, it is byte-for-byte identical to the official release. The scanner may still have a point (the official version could have a known vulnerability) but the file itself is not an injected infection.

Step 3: Compare with a fresh download

For themes, premium plugins and any file the checksum tools cannot cover, compare against a clean copy of the exact same version.

mkdir -p ~/clean && cd ~/clean
curl -sO https://downloads.wordpress.org/plugin/PLUGIN_SLUG.VERSION.zip
unzip -q PLUGIN_SLUG.VERSION.zip
diff -rq ~/clean/PLUGIN_SLUG /PATH/TO/SITE/wp-content/plugins/PLUGIN_SLUG

Placeholders: PLUGIN_SLUG is the plugin’s folder name; VERSION is your installed version from wp plugin list; /PATH/TO/SITE is your site folder. For a theme, use https://downloads.wordpress.org/theme/THEME_SLUG.VERSION.zip. For a premium product, download the same version from the vendor’s account area and upload it to ~/clean instead. Flags: curl -sO saves the file under its own name quietly; diff -r compares folders recursively and -q lists only which files differ. Good output: no lines at all, or only “Only in” lines for files you know about (such as a cache or log the plugin writes). Bad output: “Files … differ” for PHP files, or “Only in /PATH/TO/SITE/…” for PHP files that the clean copy does not contain.

To see exactly what changed in one file:

diff -u ~/clean/PLUGIN_SLUG/RELATIVE_PATH /PATH/TO/SITE/wp-content/plugins/PLUGIN_SLUG/RELATIVE_PATH | head -60

RELATIVE_PATH is the file’s path inside the plugin folder. -u prints a unified diff: lines starting with - are in the clean copy only, lines with + are in your copy only. A block of added code at the very top or bottom of the file, especially on one long line, is the most common injection pattern. Remove ~/clean when you are done.

Step 4: Check timestamps against neighbors

stat -c '%y  %z  %n' FLAGGED_FILE
ls -la --time-style=full-iso "$(dirname FLAGGED_FILE)"

Placeholder: FLAGGED_FILE is the path the scanner reported. stat prints the modification time (mtime, %y), the change time (ctime, %z) and the name. The ls line lists the whole folder with full timestamps so you can compare. Reading it: files from one plugin release usually share similar times from when you installed or updated it. A single file with a much newer mtime than its neighbors is suspicious. Attackers often copy a neighbor’s mtime onto their file with touch, but they cannot set ctime that way, so a file whose mtime matches the folder while its ctime is recent and different deserves a close look. Compare the ctime with your update history and your host’s backup logs.

To find other files changed around the same time:

find . -type f -name "*.php" -newerct "YYYY-MM-DD HH:MM" ! -newerct "YYYY-MM-DD HH:MM" -ls

Use the flagged file’s ctime minus and plus an hour for the two timestamps, in the server’s time zone. -newerct compares change time with a date string; the ! negates the second test, so this lists PHP files changed inside that window. Bad output: other files in unrelated folders changed in the same minutes, which suggests one intrusion dropping several files.

Step 5: Read the code safely

View the file as plain text, a page at a time, with long lines cut short so one huge blob does not flood your terminal:

cut -c1-300 FLAGGED_FILE | less
grep -nE 'eval\(|base64_decode\(|gzinflate\(|gzuncompress\(|str_rot13\(|assert\(|create_function\(|preg_replace\(.*/e|\$_(POST|GET|REQUEST|COOKIE)\[' FLAGGED_FILE | cut -c1-200

cut -c1-300 trims each line to 300 characters; less lets you scroll and quit with q. The grep lists line numbers for common risky functions and for direct use of request data. Neither command executes anything.

Signs of real malware

  • Decoding feeding execution: eval(base64_decode(...)), eval(gzinflate(...)) or chains of decoders. Decoding alone is common; decoding straight into eval is the red flag.
  • Long encoded blobs: thousands of characters of base64 or hex on a single line, with no comments, in a PHP file.
  • Assembled function names: strings glued together to hide what is called, such as $f = 'ba'.'se'.'64'.'_de'.'code';, chr() sequences, or hex escapes like "\x65\x76\x61\x6c", followed by $f($data).
  • Request data executed: $_POST, $_REQUEST or $_COOKIE values passed into eval, assert, system, shell_exec or a variable function. That is a remote control.
  • preg_replace with the /e modifier, create_function or string assert: old code-execution tricks. Modern PHP removed or disabled them, so in current code they are almost never legitimate.
  • Location: any PHP file inside wp-content/uploads; PHP disguised with image or icon extensions and pulled in with include; hidden dot-files; names that imitate core files but sit in the wrong folder.
  • Code before the opening comment: a legitimate file starts with its header comment or namespace; injected code often sits on line one, before it, or after the closing tag.

Legitimate code that looks scary

  • Encoded commercial plugins: files protected by ionCube or SourceGuardian are unreadable by design and usually say so near the top, mentioning the loader they need. Confirm by comparing with the vendor’s download.
  • License checkers: premium themes and plugins often base64 encode license data or remote API responses. That is decoding without execution.
  • Minified JavaScript and CSS: one enormous line with short variable names is normal for build tools. Compare it with the vendor copy rather than judging the look.
  • Vendor libraries: PDF, font, image and mail libraries embed binary data as encoded strings; code snippet plugins intentionally run stored code with eval.
  • Security plugins themselves: malware signature files and firewall rules contain the very patterns other scanners look for.

If you want to see what a blob decodes to, copy only the encoded string into a text file and decode it with base64 -d BLOB_FILE | head -c 1000, where BLOB_FILE is that text file. Decoding is safe; running the result is not. The malicious redirect guide (FG-23) walks through decoding layered obfuscation step by step.

Advanced: deciding when the evidence is mixed

Sometimes the checks disagree: the file differs from the clean copy, but the extra code is not obviously hostile. Work through these questions:

  1. Who could have changed it? Custom tweaks by a developer, a “white label” edit, or a translation plugin writing into the plugin folder all produce harmless differences. Ask whoever maintains the site.
  2. Is it a nulled copy? Premium plugins downloaded from unofficial sites very often carry backdoors in their license code. If the plugin was not bought from the developer, treat every difference as hostile and replace the whole plugin with a genuine copy.
  3. Does the access log show requests to it? Run grep "FILE_NAME" ACCESS_LOG, where FILE_NAME is the flagged file’s name and ACCESS_LOG is your log path. Legitimate include files are almost never requested directly; POST requests straight to a deep plugin file from unknown IPs are a strong sign of a webshell in use.
  4. Can you simply replace it? For any official plugin or theme, the safest answer is often to reinstall the same version cleanly with wp plugin install PLUGIN_SLUG --version=VERSION --force, which overwrites the plugin files with the official release. If the flag disappears and the site works, you have removed the uncertainty without needing to prove which it was.

If you conclude it was a false positive, use the scanner’s “ignore” or “mark as safe” option for that specific file and version only, so a future change to the file is still caught. Report it to the scanner vendor as well, with the plugin name and version; they fix their signatures that way.

What to do with a confirmed hit

One confirmed malicious file means the site has been compromised, and there is almost always more than one file. Do not just delete it and move on. Move a copy outside the web root as evidence, then follow the first-hour hacked site guide (FG-05) to contain the incident and find the way in. Once clean, the reinfection guide (FG-26) shows how to check for the backdoors, scheduled tasks and hidden admins that bring infections back.

When to contact your host

Open a ticket if the flag came from the host’s own scanner and you believe it is wrong, if a flagged file reappears after removal, or if you lack the access to run these checks. Include:

  • The full path of the flagged file and the scanner’s exact signature name or reason.
  • The plugin or theme name and version it belongs to.
  • Your checksum and diff results, and the file’s mtime and ctime.
  • Any access log lines showing direct requests to the file, with UTC times.

Hosts can often tell you which scanner rule fired and whether the same signature is triggering on other accounts, which quickly separates a bad rule from a real outbreak. Our diagnostic tools can check what your site shows to visitors, and if you see visible signs of a hack, go to the Emergency Room.

Still stuck?

Print a free one-page checklist to keep, or open the Emergency Room if the symptom does not match.

Get the free checklists

Keep reading

All field guides

Search the field manual

What is going wrong?

Press / to search from any page, Esc to close