Press Enter to search or Esc to close

How to find the infected files your malware scanner misses

How to find the infected files your malware scanner misses

A signature scanner found 135 infected files on a Joomla 4.2.9 site, but a vendor diff of the same backup found 16 more .htaccess backdoors it missed, because a diff checks byte equality, not pattern recognition.

Two passes, two counts

Run a signature scanner on a compromised Joomla site and you get a number. On a rental company's Joomla 4.2.9 site, the first pass flagged 135 malicious files. That's a reasonable result. A reasonable thing to act on.

Then we ran a vendor diff on the same backup. Downloaded the official Joomla 4.2.9 package, hashed every file in it, and compared against the server. It found 16 .htaccess files the scanner had missed entirely. Sixteen backdoors, sitting there, not on any signature list.

The full engine run that followed counted 385 flagged PHP files, 126 .htaccess entries, and 45 payloads named like images and media files. Same site. Same backup. Three different numbers.

That gap is what this post is about.

What a signature scanner actually checks

A signature scanner is a tool that compares website files against a database of known malicious patterns. If a file's contents match a known signature, it's flagged as infected.

That's the entire mechanism. It's genuinely useful against common infections. Most off-the-shelf PHP malware is well-catalogued, and a signature scanner finds it quickly. But it has a hard limit: it can only find what someone already catalogued. Anything novel, anything renamed, anything the list hasn't seen yet passes clean.

On a site that's been compromised for months (this one ran with an attacker inside for at least three months before we touched it), the payload mix tends to include both well-known tools and things adapted specifically for that site. The scanner catches the well-known half. The custom half is invisible to it.

The two hiding tricks on this site

The infection used two techniques to stay off the scanner's list.

Extension spoofing is straightforward. The attacker names a PHP file as if it were an image: cache.jpg, thumb.gif, upload.png. A web server doesn't execute a file based on its name. It executes it based on the handler assigned in the server config or an .htaccess directive. So a PHP payload sits in a media folder, looks like a thumbnail to anyone browsing the file listing, and a scanner checking for PHP files never opens it because the filename doesn't end in .php.

Directory doubling is subtler. The path images/stories/stories/cache.php looks, at a quick glance, like a cache file inside the stories folder inside the images folder. The doubled stories segment is easy to miss in a long listing. It's also not a standard Joomla directory, so there's no clean reference to compare it against without going to the official source package.

Both tricks share the same goal: hide the file somewhere the scanner won't look, or won't recognise what it's seeing.

A signature scanner doesn't verify that your Joomla installation is intact. It verifies that the files it reads don't match a list of known bad patterns. Those are two different claims.

How many of the 16 .htaccess files and 45 image-named payloads that pushed one site's count from 135 scanner findings to 385 would a scan of yours miss? The free Fastw3b Security Check answers that question in writing within 24 hours, from what any visitor can reach. You get a score from 0 to 100, every item keeping it from 100 named with what to do, how critical it is, and whether it's the kind of exposure a routine scanner works around. It's free, nothing runs on your server, and if the score shows something worth pursuing, the $490 Security Audit delivers a written report from a backup in quarantine, with diff evidence on every line. See what your site shows from outside →

How a vendor diff finds them anyway

A vendor diff is a comparison between your server's files and the CMS vendor's official package at the same version. Instead of looking for known bad patterns, it looks for any difference at all.

Here's how it works in practice:

  1. Download the official Joomla 4.2.9 package from downloads.joomla.org.
  2. Hash every file in the package.
  3. Hash every corresponding file in the site backup.
  4. Compare.

A file that differs from the vendor's version is a candidate. A file that exists on the server but not in the official package is a candidate too. The method doesn't care what the file is named, what folder it's in, or what the code inside looks like. If the bytes don't match, it shows up.

That's how the 16 .htaccess files surfaced. Signature scanners typically focus on PHP. An .htaccess file can be weaponised too. It can redirect traffic, serve attacker-controlled content, or associate a non-PHP extension with the PHP engine so that a .jpg file executes as PHP. But .htaccess is not PHP, so many signature lists don't cover it at all.

A vendor diff has no such gap. The 45 image-named payloads appeared for the same reason: the vendor package doesn't include files called cache.jpg inside the media directory. When you compare hashes, those files have no match in the reference set and appear in the diff immediately.

What free command-line tools can and can't do

If you run WordPress, you have a useful free check. The WP-CLI wp core verify-checksums command downloads WordPress core checksums from the official API and compares them against your installation. Running wp plugin verify-checksums --all does the same for any plugin hosted on WordPress.org.

That's real coverage for WordPress core and directory-hosted plugins. Run it if you haven't.

Here's what it can't do:

  • There's no equivalent for Joomla. No official joomla verify-checksums command exists.
  • Premium plugins and themes aren't covered, and neither is anything not hosted on the wordpress.org repository.
  • It doesn't check .htaccess files.
  • It runs inside the site it's checking. If the PHP environment has been modified to interfere with those API calls, the results can't be trusted. The Security Audit runs the diff from outside the site, against a backup in quarantine, where the site can't affect the comparison.

For a full vendor diff on Joomla, or on any site where you can't be certain the running environment is clean, you need a comparison that happens outside the site.

What you can check today

Start with the free steps. They don't catch everything, but they catch a lot of what's sitting unreviewed on small sites right now.

If you're on WordPress, run wp core verify-checksums and wp plugin verify-checksums --all. A modified file inside /wp-includes/ is a stronger signal than a modified plugin file. Core files have no legitimate reason to change after installation.

Pull a file listing sorted by modification date. Files modified in an unusual cluster, especially in media or cache directories, are worth a closer look. A PHP file in uploads/ or images/ with a .jpg extension and a recent modification date is a candidate worth opening.

Open your .htaccess files and read them. Your root .htaccess will have a few standard Joomla or WordPress directives. An .htaccess in a media subfolder almost certainly shouldn't exist. If one does, read every line.

Check your CMS version. Joomla 4.x reached end of life in August 2025 and no longer receives security patches. Joomla 5.x is the current supported branch. Running an end-of-life version doesn't mean you're already compromised, but every known unpatched vulnerability stays open with no fix coming.

One honest caveat: a vendor diff gives you a list of files that differ from the official package. That list will include legitimate customisations too, a site's own template, a plugin's local config, anything a developer changed on purpose. Deciding which difference is a customisation and which is a payload is the reviewer's work. That's why every finding in the Security Audit report carries the full evidence: the diff line, the file path, and the context that separates a legitimate change from a backdoor.


Frequently asked questions

Does a clean malware scan mean my site is safe? No. A clean result means none of the scanned files matched a known signature. On the rental company's site, several early scans came back clean before a vendor diff found 16 .htaccess backdoors. A clean scanner result and an uncompromised site are two different things.

What Joomla versions still receive security patches in 2026? Joomla 5.x is the current supported branch. Joomla 4.x reached end of life on 17 August 2025 and no longer receives security patches. A site on Joomla 4.x carries every known unfixed vulnerability in that release line with no patches coming.

Can I run a vendor diff myself without a security service? Yes, for a basic comparison. Download the official package for your exact installed version, unzip it alongside a clean copy of your site files, and run diff -r to list the differences. The challenge is reading the output: a production Joomla site returns hundreds of differences against a clean package, most of them legitimate. Separating a backdoor from a customisation takes familiarity with the directory structure.

Why do attackers use .htaccess files? An .htaccess file controls how Apache handles requests in its directory. An attacker can use one to add a PHP handler that executes a .jpg payload, redirect outbound requests through an attacker-controlled server, or whitelist IP addresses that skip authentication checks. They're also often missed in cleanups because they're not PHP files.

How long can a site stay infected without anyone noticing? On the rental company's site, the evidence pointed to at least three months of attacker access before cleanup began. Most small-site infections are found because the site starts behaving oddly (redirecting visitors, showing spam, or getting flagged by search engines), not because a scheduled scan flagged them.

Check it free, today. The Security Check runs on Joomla, WordPress, and PHP frameworks such as Laravel or Symfony. Hand-written custom PHP comes back as a review, quoted after a first look. It reads from outside your site, within 24 hours, and returns a score from 0 to 100 with every item keeping it from 100 named, rated by severity, and explained.

If the score shows something worth pursuing, the Security Audit is $490, delivered in 3 days from a files-only backup in quarantine. Nothing on your live site is touched. The $490 fee isn't credited against cleanup. The report is yours to take anywhere: a host, a developer, or an insurer.

Cleanup is a custom quote after the audit, priced on what the report found before any work starts. It closes with the same diff run again on the cleaned site, so the clean bill is something you can verify. The report gives you the facts a breach-notification decision rests on; whether you're required to notify anyone is a legal question for your jurisdiction.

Security Monitoring is launching soon: scheduled outside checks for audited or cleaned sites, at $29/mo or $290/yr.

Have your site read from outside →

Related Articles

  • Client Login

    Restore password
  • New Registration

Make sure @fastw3b.com email domain is white-listed in your email client to restore password, verify registration, get order confirmations, etc.