DEV Community

velprove
velprove

Posted on • Originally published at velprove.com

Detecting a Hacked WordPress Site: Skimmers and Silent Defacement

The honest take: when an attacker injects a card skimmer, defaces a page, or hijacks your wp-admin, your host dashboard keeps returning a green HTTP 200, because the site is still being served, it is just serving the attacker's version. Velprove is not a security scanner and has no server access. Its browser login monitor signs in to your wp-admin in a real browser, and content assertions check the page a visitor actually loads, so it can flag that your expected checkout or admin markup is gone, or that a known bad marker appeared, as a fast external tripwire. It is complementary to server-side tools like Wordfence or Sucuri, not a replacement for them.

A card skimmer ran on a live store for weeks, and the uptime dashboard never blinked

In September 2024, Sucuri documented a WooCommerce credit card skimmer with a detail worth sitting with. As Sucuri put it in their September 12, 2024 writeup , "All the attackers did was simply edit the checkout page source, either from wp-admin (using a compromised administrator user) or directly through the database." The payload was not even a conspicuous <script> tag. It hid inside a <style> tag and executed through an onload handler once the page finished loading, with the skimmer body heavily obfuscated through custom character substitution and shuffling.

Now hold that next to what every availability check saw. The store was up. The checkout page returned a 200. The cart worked, the product pages loaded, the host status panel was green. A skimmer that quietly copies every card number a customer types can run for weeks this way, because nothing about availability changes: the site stays up and keeps returning 200 while the attacker collects card data on every order. If you run a store, the natural next step is dedicated WooCommerce checkout monitoring , and this post is about the adversarial half of that: not a checkout that broke, a checkout an attacker quietly rewrote.

Why an HTTP 200 dashboard is structurally blind to this

A status-code check asks one question: did the server answer? A compromised site answers fine. It returns a 200 with a fully rendered page, because the page is doing exactly what the attacker wants it to do. This is not the familiar "200 but the page is blank" problem where a build failed and the body is empty. This is a 200 serving a page an attacker now controls: the markup is present, it renders, it just contains a skimmer or a defacement or a login that now belongs to someone else. Availability monitoring is the wrong instrument for it, the same structural reason uptime monitors miss outages like this . The fix is not a better status-code check. It is checking the content of the page a visitor actually loads.

The three ways a compromise shows up on a page a visitor can see

This post is about your site being compromised by an attacker, an injected skimmer, a silent defacement, a hijacked admin, not about a legitimate plugin update breaking your own site, which is a different problem . That sibling is about your site breaking itself when a good-faith update throws a fatal. This one is about the page doing precisely what an attacker intends while your host dashboard shows a green 200. Three patterns dominate the externally visible side of it.

Injected scripts and skimmers. The largest example on record is Balada Injector. Sucuri, in their campaign synopsis , estimated that "since 2017, we estimate that over one million WordPress websites have been infected by this campaign," and noted it "consistently ranks in the top 3 of the infections that we detect and clean." It typically enters through a vulnerable plugin: BleepingComputer reported that Sucuri detected it on over 17,000 WordPress sites in September 2023, more than 9,000 of them through one plugin XSS flaw. The injected code redirects visitors and adds backdoors. To a visitor it is a script that should not be there.

Silent defacement. The content or appearance of a page is changed without the site going down. A pricing page now reads differently, a banner appears that you did not put there, a section is replaced. The page still returns 200 and renders cleanly. Nothing about it is "down." It is just no longer your page.

Hijacked wp-admin. In Sucuri's 2023 hacked-website report , among the sites Sucuri remediated, "malicious WordPress admin users were found in 55.2% of infected databases," and SEO spam appeared on 42.22% of infected sites. That is a remediation-sample figure, not a rate across all WordPress sites, but the direction is clear: when an attacker gets in, control of the admin is a common outcome, and the externally visible consequence is a wp-admin login that no longer behaves the way it should.

What Velprove is not, and read this before you set anything up

This is the part that keeps this post honest, so it goes before the setup, not after it. Velprove is not a security scanner and it has no server access. It does not read your files, it does not scan for malware, it does not do file-integrity monitoring, and it is not a web application firewall or a vulnerability scanner. It sees exactly one thing: the page a visitor's browser receives from the outside.

That means Velprove detects the symptom, not the cause. It can tell you the expected checkout markup vanished, a known injected marker is present, or the wp-admin login is broken. It cannot tell you which plugin was vulnerable, which file was modified, or that a rogue admin row was written to the database. Server-side tools like Wordfence and Sucuri do that work, scanning files and doing integrity monitoring on the server itself. Velprove is the fast external tripwire that fires from outside the host you are trying to verify. The two are complementary, and the rest of this post is written on that understanding.

The setup: a positive DOM tripwire, a known-bad check, and a wp-admin login monitor

Three monitors cover the externally visible surface of a compromise. The first is the lead, and it is the one almost no uptime tool gives you on a free plan.

A browser login monitor on wp-admin. This is the strongest leg, and it is the differentiator. Velprove opens a real browser, navigates to your wp-login.php, signs in as a dedicated low-privilege test account, and asserts that the expected post-login admin markup rendered. When an attacker changes credentials, locks accounts out, or replaces the login flow, this monitor fails, which is the externally visible face of the hijacked-admin pattern and the 55.2% figure above. The mechanics are walked through in detail in the wp-admin browser login monitor guide . Use a dedicated low-privilege test account, never your real administrator credentials. One configuration detail here is load-bearing: in the monitor's Customize detection options, set Success verification to Page contains text and point it at a stable string only the real admin dashboard renders. Leave it on the default URL-change check, and a hijacked login that still redirects can pass while a visitor is seeing the attacker's page. The post-login markup assertion is what makes this monitor detect the symptom, not just that some redirect happened. A positive body_contains tripwire. Add an HTTP monitor with a body_contains assertion on a stable piece of markup that must be present on a healthy page: a checkout form field name, an admin shell element, a distinctive footer string. If a defacement or a skimmer rewrites the page, that expected markup is the first thing to disappear, and the assertion fails. This is the robust play, because it does not require knowing the attacker's payload in advance. A targeted body_not_contains check. Add a body_not_contains assertion on a specific known bad string only when you or your security tool have already identified one: a specific injected script source, a known exfiltration host, a known defacement banner string. This is a narrower secondary layer, not the primary defense.

One product fact to set expectations correctly: each Velprove monitor probes from a single region. You can choose which of the 5 global regions runs a given monitor, or run separate monitors per region if you want multi-region coverage. There is no "every check from all regions at once."

The honest limitation: a fixed substring cannot catch a rotating skimmer

Velprove's assertions are fixed substring matches, not regular expressions or pattern matching. body_contains checks whether an exact string is present, and body_not_contains checks whether an exact string is absent. That has a direct consequence you should know before you rely on it. Modern skimmers, as the Sucuri Woo skimmer writeup showed, obfuscate their payload with custom character substitution and rotate their exfiltration domains. A body_not_contains assertion catches only a known fixed string. The moment the attacker re-obfuscates or rotates, that exact string changes and the known-bad check goes silent.

This is why the positive tripwire leads and the known-bad check is secondary. Asserting that your expected checkout or admin markup is present does not depend on predicting the attacker's payload. Most page replacements and many injection techniques disturb the legitimate markup, so a positive-presence assertion is the durable signal. The honest framing is: Velprove reliably tells you the expected page is no longer intact, and it can catch a specific known marker you already know about, but it is not a promise to catch every obfuscated or rotating payload. Anyone selling you that promise from the outside of your server is overclaiming.

Where this fits next to Wordfence and Sucuri

Wordfence and Sucuri are dedicated WordPress security platforms. They do server-side malware scanning and file-integrity monitoring: they read the files on your server and tell you when one changed in a way it should not have. That is real, important work, and Velprove does not do it and does not claim to.

Velprove sits in a different and narrower place. It watches the rendered page from outside the server, with no plugin and no server access, and fires fast when the page a visitor loads stops looking like your page. A server-side scanner answers "did a file on my server change?" Velprove answers "did the page my customer sees change?" Those are not the same question, and a real compromise often trips one before the other. The right posture is both: a server-side scanner for file and integrity coverage, an external content tripwire so you hear about the visible symptom quickly even from a network position the attacker does not control.

Set this up in the next ten minutes, free

You do not need a paid plan to put this in place. Velprove's free plan includes 10 monitors, one browser login monitor at a 15-minute interval, a 5-minute HTTP interval, email alerts, and a choice of 5 global regions, with no credit card required. That is enough for the wp-admin browser login monitor plus the body_contains positive tripwire and a targeted body_not_contains check on the same page.

The browser login monitor is the leg to set up first, because a hijacked admin is both common and the hardest of the three to notice on your own. Point it at your wp-login.php, sign in with a dedicated low-privilege test account, and let it run. Then layer the positive content tripwire on your checkout or a high-value page. None of it touches your server, and all of it runs from outside the host you are trying to trust. Pair it with a server-side scanner and you have covered both the file and the page. Start with the free plan. No credit card required.

Frequently Asked Questions

How do I detect if my WordPress site has been hacked?

From the outside, you watch the symptom on the page a real visitor loads: the expected checkout or admin markup is missing, a known injected marker appeared, or the wp-admin login no longer works. Velprove does this with a body_contains assertion on the markup that should be present, a body_not_contains assertion on a known bad string, and a browser login monitor that signs in to wp-admin. That is symptom detection. It does not replace a server-side scanner that reads your files for malware and file-integrity changes. Run both.

Does Velprove need a plugin or server access to detect a compromise?

No. Velprove is not a security scanner and has no access to your server, files, or database. It reads only the externally rendered page, the same one a visitor's browser receives. It cannot scan files for malware or do file-integrity monitoring, and it asks for nothing to be installed on the site. That boundary is the point: it is a fast external tripwire that runs independently of the host you are trying to verify, and it is complementary to, not a replacement for, a server-side security tool.

Can an uptime monitor catch a credit card skimmer on my WooCommerce checkout?

It can catch the symptom, with one honest caveat. A body_contains assertion that the expected checkout form markup is intact is the robust play, because a skimmer that tampers with the checkout often disturbs that markup. A body_not_contains assertion on a specific known bad string catches that exact string. The caveat: assertions are fixed substring matches, not patterns, and modern skimmers obfuscate and rotate their payload, so the positive presence check is the durable one and the known-bad check is a targeted secondary, not a promise to catch every variant.

Why does my uptime monitor still show green when my site is hacked?

Because the site is still being served. A status-code check asks whether the server answered, and a compromised site answers fine: it returns a 200 with a fully rendered page. The page is just doing exactly what the attacker wants. The skimmer collects cards, the defaced content is live, the hijacked admin is theirs, and every layer that only watches availability reports healthy. You need a check that inspects the content of the page a visitor actually loads, not just the status line.

Is Velprove a replacement for Wordfence or Sucuri?

No, and it is not trying to be. Wordfence and Sucuri are server-side WordPress security platforms that scan your files for malware and do file-integrity monitoring on the server. Velprove has no server access and does none of that. It watches the rendered page from the outside as a fast external tripwire. The two answer different questions: a server-side scanner tells you a file changed, Velprove tells you the page a visitor sees changed. Use both. They cover different layers of the same problem.

What should I assert to detect a defaced or skimmed page?

Lead with a positive tripwire. In Velprove, use a body_contains assertion on a stable piece of markup that must be present on a healthy page: a checkout form field name, an admin shell element, a footer string the attacker is unlikely to preserve when they replace the page. Then add a body_not_contains assertion on a specific known bad string only if you or your security tool have already identified one. The positive check is more robust because it does not depend on knowing the attacker's exact payload in advance, which with obfuscated and rotating skimmers you usually do not.

Can Velprove tell me if someone created a fake admin account?

Not directly, and being honest about that matters. Velprove has no server access and cannot read your WordPress user table, so it cannot tell you a rogue admin row exists. What it can catch is the symptom: a browser login monitor that signs in to wp-admin with a dedicated low-privilege test account will fail when an attacker has changed credentials, locked accounts out, or replaced the login flow. That is the externally visible consequence of a hijacked admin, not the database state itself. For the underlying account audit you still need a server-side tool.

Top comments (0)