Tag: wordpress security

  • WordPress Malware Scan: The Playbook, Step By Step

    WordPress Malware Scan: The Playbook, Step By Step

    Your site can look perfectly fine to you and still be selling pills to Google.

    That is the unsettling part of modern WordPress malware. The owner loads the homepage, sees the real homepage, and moves on. The crawler gets a different page. The client finds out when a customer asks why the search result mentions casinos.

    Short answer: a useful WordPress malware scan checks four places, not one: what the site shows to crawlers, what is sitting in the files, what is sitting in the database, and what has been set up to bring the problem back. Then it verifies every hit before it accuses anything, and it says plainly which areas it could not reach.

    What this post is, and is not: this is a practical walkthrough of how a read-only malware scan is built and how to read its output. It is not a claim that any scanner can promise a site is clean forever, and it does not describe a cleanup. Detection and removal are different jobs, and mixing them is how good files get deleted.

    An alert says something matched. A report says what it is, how sure we are, and what we could not see.

    Six numbered cards listing the WP Malware Scan steps: gateway and environment, external cloaking scan, filesystem malware sweep, database and persistence scan, verify and attribute findings, and forensic malware report. A note underneath says detection only, nothing is deleted, quarantined or edited.
    Six read-only steps, from environment to report.

    Why A Single Scan Falls Short

    Most people meet malware scanning as one button. It reads the files, matches them against a list, and prints a number.

    That catches the loud cases. It misses the quiet ones, and the quiet ones are the ones that last.

    Where can an attacker leave something behind?

    • In a PHP file that only runs when the visitor looks like a search engine.
    • In an option row in the database, stored encoded so a plain text search finds nothing.
    • In a scheduled task that quietly rewrites the payload an hour after you delete it.
    • In an administrator account that does not show up in the normal user list.
    • In an application password, which can survive a normal password reset.

    A scanner that reads files only will never see the second, third, fourth or fifth. A scanner that reads your pages only as a browser will never see the first. Each blind spot is a place to hide.

    Three Ways To Look At The Same Infection

    The same compromise looks different depending on where you stand. That is why one view alone is rarely enough.

    ViewWhat it tells youWhat it cannot prove alone
    The page as a crawler sees itWhether search engines are being shown something your visitors are notWhere the code lives, or whether anything else is planted
    The files on diskWhich files are modified, unexpected or behave like backdoorsWhether a match is malicious or a legitimate library that looks odd
    The databaseInjected content, off-domain settings, hidden accounts, odd optionsWhether the file that wrote it is still there to write it again

    A file match is a clue. A cloaked page is evidence of a symptom. A database row is a place to look. None of them is a verdict until someone reads it in context.

    Three views of one infection: the page a crawler sees, the files on disk and the database, with what each cannot prove alone
    Each view catches what the others cannot prove.

    A Small Test You Can Run Today

    Before you trust any scanner, including ours, ask a simpler question. Does your site show the same page to everyone?

    Pick five URLs: the homepage, two recent posts, one page from your sitemap, and one odd query string. Then request each one three ways and compare. Write down:

    • The page title for each request.
    • The social preview title and image.
    • Any words in the body that do not belong on your site.
    • Any script or image loaded from a host you do not recognise.

    A basic way to see it from a terminal:

    curl -s -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://example.com/ | grep -i "<title>"
    curl -s -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" https://example.com/ | grep -i "<title>"
    curl -s -A "Googlebot/2.1 (+http://www.google.com/bot.html)" "https://example.com/?g=casino" | grep -i "<title>"

    If the titles differ, or a query string produces a page that does not exist in your sitemap or your editor, stop and investigate. Two different titles are not proof of malware on their own, because some sites legitimately serve different markup to bots. But an unexplained difference is exactly what a scan is meant to surface.

    The Playbook, Step By Step

    WP Malware Scan is a Protuno security playbook, currently in beta. It runs a fixed sequence of six steps, and it only reads the site. It changes nothing: nothing is deleted, nothing is quarantined, nothing is edited. It needs the Protuno plugin on the site, because it runs its checks from inside it. It is not on the public playbooks page yet, so treat it as a new playbook that is still being rolled out.

    #StepWhat it does
    1Malware Scan Gateway And EnvironmentReads the WordPress and PHP versions, the real database table prefix, multisite status and key paths, then records what the scan can and cannot reach.
    2External Cloaking ScanRequests a bounded set of URLs as a crawler, a social preview bot and a desktop browser, and compares what each one is shown.
    3Filesystem Malware SweepLooks across the web root, WordPress core, plugins, themes, uploads, mu-plugins and drop-ins for modified, unexpected or suspicious files. Reports candidates, not verdicts.
    4Database And Persistence ScanReads options, posts, users and scheduled tasks, plus startup rules such as .htaccess, for injected content and ways back in. Select queries only.
    5Verify And Attribute FindingsReads each candidate in context and sorts it into confirmed, needs human review, or false positive. Looks for the likely way in.
    6Forensic Malware ReportWrites one honest report with a status, the evidence, and a described remediation plan. Executes nothing.

    The order matters. The first step decides what the others are allowed to claim. The next three collect candidates. The fifth step is the only one allowed to turn a candidate into a finding. The sixth cannot invent anything the earlier steps did not produce.

    Step 1: Know What You Can Reach

    Before it looks for anything, the scan records its own limits: can it read files, search content, query the database, make web requests, read the debug log. Server-level scheduled jobs and web server access logs sit outside what a WordPress plugin can read, so they are written down as coverage gaps from the start. It also reads the real table prefix instead of assuming wp_.

    Step 2: Look Like Google

    This is the step most scanners skip. The scan requests a bounded set of URLs, the homepage, up to around fifteen sitemap URLs and a few recent posts, as Googlebot, as a social preview bot and as a normal desktop browser. It compares titles, social titles and images, and checks the body for spam words such as casino, slot, pharmacy and replica. It also flags images or scripts loaded from hosts that commonly serve spam payloads.

    If the real server address is known, it asks the origin directly too, because a clean cached copy at a CDN can hide a dirty origin. If it is not known, that is recorded as a gap, not a pass. It also tries a couple of query strings as a crawler, because some spam pages only exist when a particular URL is requested.

    Step 3: Candidates Only

    The filesystem step works through the places files hide: odd folders and loose PHP in the web root, modified core files, plugins on disk but not active, PHP in uploads, mu-plugins and drop-ins, known web shell patterns and obfuscated code that runs input.

    Core files are compared with the official WordPress checksum list for the exact version installed. If that reference cannot be fetched, the step must say core integrity could not be checked. It may not compare against an empty reference and call a file modified. A false alarm that sends an agency hunting a compromise that does not exist is treated as worse than an honest “not checked”. Vendor folders and minified files are listed but never marked safe to auto-clean, because legitimate code uses the same tricks malware does.

    Step 4: The Database And The Way Back

    The database step reads every table with select queries only. In options it looks for injected scripts, off-domain site addresses and known indicator keys. It also decodes values that look like base64 and tests the decoded text, because an encoded payload contains none of the obvious markers.

    It compares the number of administrators in the database with the number in the user list; a gap means a hidden account. It lists application passwords by name, never the secret. For comments it reads only for injected markup. It does not count spam or advise emptying the spam or trash folders, because that trash is the undo path for comment cleanup.

    Step 5: Read Before You Confirm

    Every candidate is read in context and labelled confirmed, needs human, or false positive. Unsure means needs human, never confirmed. A documented list covers legitimate things that look suspicious, such as snippet plugins that store PHP in uploads on purpose.

    The step also tries to work out how the attacker got in, using the debug log where available. Without access logs, the report says attribution is limited. It maps everything that could rewrite a payload, so those can be switched off together, and if no vulnerability feed key is set it says that check was not assessed, never “no vulnerabilities”.

    Step 6: Say What Is Actually Known

    The report ends in one of four states:

    StatusWhat it means
    InfectedAt least one finding was confirmed, in a file, the database or a cloaked page.
    SuspiciousNothing confirmed, but at least one item needs a human to look.
    CleanNothing confirmed, nothing needing review, and full coverage with no gaps.
    IncompleteNothing confirmed, but some areas could not be scanned, or an earlier step did not finish.

    The last one is the important one. “No malware found in what could be scanned” is a different sentence from “your site is clean”. The playbook is written so that the word clean is earned, never assumed, and so that a layer that did not finish is never reported as clear.

    WP Malware Scan: six read-only steps.

    What A Read-Only Malware Scan Does Not Solve

    • It does not remove anything. Cleanup needs an off-site backup you hold and a human confirming each item.
    • It cannot see server-level persistence or web server access logs from inside WordPress.
    • It cannot promise the site stays clean after today.
    • It does not fix the way in. A stolen password or an outdated plugin will let the next one through.

    Questions To Ask Your Current Scanner

    Whatever you use today, these separate a scan from a signature match:

    • Does it ever request pages as a crawler, or only read files?
    • Does it check core files against the official checksums for your exact version?
    • Does it read the database, including encoded option values?
    • Does it look for hidden administrators and leftover application passwords?
    • When it finds a match, does it read it in context before calling it malware?
    • When it cannot reach a place, does it tell you, or stay silent?

    If the answer to the last one is silence, the green tick means less than it looks like.

    Which Check Are You Actually Running?

    If you need to knowThe evidence you needThe next action
    Whether search engines see spam you do notCrawler and browser responses comparedInvestigate any difference before touching files
    Whether WordPress core was alteredFiles compared against the official checksum listReinstall from a clean source, after a backup
    Whether something is hidden in the databaseOption, post and user rows read in full contextReview by a person, with a backup in hand
    Whether an old vulnerability is the doorInstalled versions matched against a vulnerability feedSee vulnerability monitoring
    Whether it will come backA map of every item that can rewrite the payloadNeutralise them together, then watch for reinfection

    Fixing Findings In The Right Order

    When a scan reports a confirmed finding, the order of work matters more than the speed.

    PriorityActionWhy this order
    1Take a full off-site backup of files and databaseYou need an undo that the attacker cannot touch
    2Note a health baselineSo you can tell whether your fix broke something
    3Reinstall legitimate core and plugin files from clean sourcesReplaces modified files without guessing
    4Neutralise everything that can rewrite the payload, togetherDelete one writer first and it regenerates
    5Move confirmed malware aside rather than deleting itQuarantine can be reversed
    6Rotate secrets and revoke rogue application passwordsA reset alone leaves some access in place
    7Prove it clean as a crawler and watch for ten to fourteen daysReinfection often shows up later

    Two practical rules sit under all of it. Restore point first, because you are about to change a live site. Re-check after each change, because the fix itself can break things. See verify a WordPress backup for why a backup you have not tested is only a hopeful file.

    A Five-Site Audit You Can Do This Week

    Pick five client sites. For each one, write down:

    • The site and who owns it.
    • Whether pages looked the same as a crawler and as a browser.
    • Whether the number of administrators matches the user list.
    • The date core files were last checked against official checksums.
    • Which areas nobody can currently see: server logs, scheduled jobs, the origin behind a CDN.

    Then choose one outcome for each item you found:

    • Fix now: confirmed, backup taken, plan agreed.
    • Test first: looks suspicious, reproduce it on a copy.
    • Replace: a plugin you cannot verify, swap it for a maintained one. See what happens when a plugin leaves the repository.
    • Remove: unused plugins and themes, which still count as attack surface.
    • Accept with an owner and a date: a known gap, written down, with a review date.
    • Investigate: unexplained difference, needs a person.

    Stopping the next break-in is half the job, so read the hardening checklist and the wp-config exposure check next.

    Why A Malware Scan Goes Stale

    A scan is a photograph. It tells you about the moment it ran.

    A clean result on Monday says nothing about a plugin installed on Tuesday, a password reused on Wednesday, or a vulnerability published on Thursday. The real value of a scan is not one report. It is the habit of running it again, comparing the answers, and noticing what changed.

    Coverage gaps matter as much as findings. A gap you wrote down is a question you can answer next month. A gap nobody recorded is where the next problem lives.

    Where Protuno Fits

    Protuno’s Rook is the Security Super Agent. WP Malware Scan is one of its playbooks, and it is read-only: it reports what it found, how sure it is and what it could not see, and a human decides what happens next. It does not clean, quarantine or delete anything. It is new and not on the public playbooks page yet; the current public security playbooks are listed on the playbooks page.

    For the wider picture of how security checks fit together, read WordPress security monitoring and WordPress security audit.

    The Point

    A site is not clean because nobody has complained yet.

    It is clean when the places that matter have been looked at, the matches have been read, and the gaps have been named.

    When did you last look at your site the way Google does?

  • WordPress Security Audit: The Playbook, Step By Step

    WordPress Security Audit: The Playbook, Step By Step

    A security grade is only useful if it tells you what it did not check.

    That sounds like a small detail. It decides whether an “A” means your site is safe, or only that the scan never reached the parts that would have lowered it.

    Short answer: a good WordPress security audit reads the site in passes, checks the site directly, tries to verify what it finds, and then gives one grade with the gaps written beside it. A grade with no gaps listed is a guess dressed as a result.

    What this post is, and is not: it is a practical look at how a graded audit is built, and what one real run on a test site produced. It is not a claim that every finding below is exploitable, or that a grade replaces a human review.

    Two cards side by side. The left card shows a security grade of C with an asterisk and a score of 78.6 out of 100. The right card lists the eight audit steps, with two marked as errored and the injection scan marked as not finished.
    A grade only means something next to the coverage behind it.

    Why A Single Grade Misleads

    Most people meet security as a number. A plugin shows a green badge. A report says 92 out of 100.

    The number is easy to read. What it hides is the question underneath it: what was actually looked at?

    A site can score well because it passed every check that ran, while the checks that matter most never ran at all. A timeout, a missing permission or a blocked feed can quietly remove half the evidence. The score still arrives.

    That is why a useful audit shows two things together: the grade, and the coverage behind it.

    Four Passes, Not One Scan

    A WordPress site can be exposed in different ways, and each way needs different evidence.

    PassWhat it looks atWhat it cannot tell you on its own
    Configuration and exposureSettings and files a visitor can reach from outside, such as logs, headers, version details and usernamesWhether any installed software has a known flaw
    Vulnerable componentsInstalled core, plugins and themes, matched against public advisory dataWhether a plugin that is not on wordpress.org has a problem
    Access controlCode paths that should check who is asking before they actWhether a flagged path can really be abused
    Injection and executionCode that handles input, uploads, requests and dynamic executionAnything, if the scan did not finish
    Four labelled columns showing configuration and exposure, vulnerable components, access control, and injection and execution, with the type of evidence each pass reads.
    Four passes, each reading a different kind of evidence.

    The audit is the work of putting these four side by side, so one pass can cover the blind spot of another. If you want the monitoring side of the same question, WordPress Security Monitoring covers how to keep checking after the first audit.

    A Small Test You Can Run Today

    Pick one staging site, or a site you are authorised to manage. Open these in a private window and write down what each one returns:

    /debug.log
    /?author=1
    /readme.html
    /wp-json/wp/v2/users

    Then look at the response headers for the home page and note which of these are present: Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Referrer-Policy and Content-Security-Policy.

    A response is not automatically a vulnerability. It is evidence to review. The useful question is whether the exposure is needed, and who it helps.

    The Playbook, Step By Step

    WP Security Audit is a Protuno security playbook: a fixed sequence written down before it runs, so it asks the same questions every time. It only reads the site and changes nothing. There are eight steps, as they appear in the Protuno dashboard. It is not on the public playbooks page yet, so treat it as a new playbook that is still being rolled out.

    #StepWhat it does
    1Recon And FactsReads the live site: WordPress and PHP versions, theme, plugins, admin count and whether registration is open
    2Config And Exposure AuditChecks what an unauthenticated visitor can reach: debug log, headers, version disclosure, usernames, directory listing
    3Vulnerable Components AuditMatches installed core, plugins and themes against public vulnerability data
    4Security Scan, Access ControlSweeps plugin and theme code for routes that may skip an access check
    5Security Scan, Injection, Execution And Supply ChainLooks for injection, upload, request-forgery and dynamic-execution patterns
    6Verify And Triage FindingsChecks each finding against the real code before it is reported, and ranks it
    7Posture Score, A To FTurns the evidence into a grade, and marks any area it could not grade
    8ReportWrites the plain-language report with a fix for each finding

    Step six is the one that matters most, because an unverified finding is an accusation and a verified one is a task. It is also the step that errored in the real run below, so the findings in this post come from the direct checks in steps one to four, not from step six.

    What It Found On A Real Site

    The video below walks through the result of one real audit run on a WordPress test site, on 24 September 2026. Steps one to four and step seven completed. Step five did not finish, because its code scan timed out three times. Steps six and eight errored. That is part of the result, so it is shown rather than hidden.

    • Grade: C* with a score of 78.6 out of 100. The asterisk marks that one area was left ungraded.
    • Two critical findings: the debug log was publicly readable at about 1.4 MB, and the only admin account had no two-factor authentication.
    • Warnings: the admin username was revealed through the author archive, three security headers were missing, the WordPress version was disclosed in several places, plain HTTP was still served, there was no login lockout, and PHP errors were printed to visitors.
    • Low priority: in-dashboard plugin and theme installs were still allowed.
    • Passed: the checked components had no known vulnerabilities, and the core configuration checks passed, including file permissions, security salts, the file-edit lock and closed registration.
    • Not finished: step five (injection and execution) timed out three times, and steps six (verify and triage) and eight (report) errored. The report page still showed the findings from steps one to four and the score from step seven, and it states that the unfinished checks were unassessed, not clean.
    • Candidates, not confirmed: the access-control sweep flagged 65 candidates and one request-forgery candidate across 4,557 files. None was confirmed exploitable, and the report says so.
    WP Security Audit on a real test site, 24 September 2026.

    The honest part is the useful part. A scan that times out and still prints “no issues” is worse than a scan that says “this part did not run”. The grade here is lower than it might have been, and it is also more believable.

    What An Audit Does Not Solve

    An audit is one moment in time, and security monitoring covers what changes after it. Neither is a full security programme.

    It cannot, by itself, tell you whether:

    • the site is being attacked right now;
    • a plugin that is not on wordpress.org has a known flaw;
    • a flagged code path can really be abused by a real visitor;
    • a file changed after the scan finished; or
    • the site has already been compromised.

    Those are different questions that need different checks. This is also why the vulnerability monitoring question and the audit question are kept apart.

    Reading The Findings

    These are the main findings from that run, ordered by what to fix first. The smaller ones are listed in the run summary above.

    PriorityFindingWhy it matters
    CriticalPublic debug logIt can reveal paths, plugin names and errors to anyone. See WordPress Debug Log Exposed
    CriticalNo two-factor authentication on the adminOne stolen password is then enough
    WarningUsername shown by the author archiveIt hands an attacker half of a login. See WordPress REST User Enumeration
    WarningMissing security headersThey are cheap to add and easy to verify. See WordPress Security Headers
    WarningVersion disclosed in several placesRemoving one tag does not remove the others
    WarningPlain HTTP still servedVisitors and bots can still reach the site without encryption
    WarningNo login lockoutRepeated password guesses are not slowed down
    WarningPHP errors visible to visitorsDebug output belongs in a private log
    LowIn-dashboard plugin installs still allowedLow priority if you use the dashboard for updates

    Two practical rules keep this safe. Take a restore point before changing anything that can affect the site. And re-check the finding after the change: “the setting was changed” is not the same as “the exposure is gone and the site still works.” The hardening checklist lists the fixes in a suggested order.

    When The Audit Cannot See A Plugin

    One line in the report deserves its own section. Three plugins on the test site were private or off-repository, so public vulnerability feeds had nothing to say about them. The report listed them as “verify with the vendor” rather than counting them as clean.

    That distinction matters for any agency running custom or premium code. “No known vulnerabilities” can mean nothing was found, or it can mean nothing was checked. A good report tells you which. For a related risk, see WordPress Removed Plugin Risk.

    A Five-Site Audit You Can Do This Week

    Pick five sites you are responsible for. For each one, record:

    • WordPress and PHP versions
    • Whether a public debug log exists
    • Whether the author archive reveals a username
    • Which security headers are present
    • Whether the admin accounts use two-factor authentication
    • Whether failed logins are limited
    • Which plugins are private or off-repository
    • The owner, the next action and the date it was last checked

    You will learn two things quickly: which site has the biggest gap, and which of your checks would have been silently skipped. For each site, write the decision beside the finding: fix now, test first, accept with an owner and a date, or investigate. A finding without a decision is just a better-organised inbox.

    Where Protuno Fits

    In the Protuno dashboard, WP Security Audit sits under the Security agent, Rook. It is a read-only playbook with eight steps that reports a grade with its coverage beside it, and it does not change the site. Fixes stay a human decision, made with the evidence in front of you. It is new and not on the public playbooks page yet; the current public security playbooks are listed on the playbooks page.

    The Point

    A security grade should make you ask better questions, not stop asking them.

    A C that tells you what it could not check is worth more than an A that does not.

    So before asking “what is our score?”, ask the question underneath it: what did this audit actually look at, and what did it skip?