```html ```
top of page

The Signature Failed to Verify, So It Was Accepted. That Is Not a Typo — It Is CVE-2026-15981, It Is Being Exploited Right Now, and We Had the Detection Rule Three Weeks Before Anyone Saw It Used.

  • Writer: Patrick Duggan
    Patrick Duggan
  • 1 day ago
  • 6 min read

PHP has a function that checks cryptographic signatures. It does not return true or false. It returns three things: 1 if the signature is valid, 0 if it is invalid, and −1 if OpenSSL fell over and could not perform the check at all.


The miniOrange SAML plugin tested that result with a loose boolean check.


In PHP, −1 is truthy.


So the one answer that means I have no idea whether this is genuine, something went wrong was read as this is genuine. Send a signature malformed enough to make OpenSSL throw an error, and the door opens — because the check broke, not despite it. You do not need to forge anything. You need to be bad at forging in a very specific way.


That is [CVE-2026-15981](https://analytics.dugganusa.com/api/v1/dredd/kev-gap?cve=CVE-2026-15981), CVSS 9.8, and attackers are using it on WordPress sites today to log in as administrator.



The second one is worse, in the way that only elegant bugs are worse


The same plugin also shipped [CVE-2026-61979](https://analytics.dugganusa.com/api/v1/dredd/kev-gap?cve=CVE-2026-61979), CVSS 8.1, and this one is almost beautiful.


A SAML response arrives and it is allowed to name its own signature algorithm. The attacker names HMAC-SHA1 rather than RSA. HMAC needs a shared secret, so the plugin reaches for the trusted key it has on file — which is an RSA public key, in PEM form — and uses that as the HMAC secret.


Sit with that. The secret is a public key. Publishing it is the entire point of it. It is on the identity provider's metadata endpoint, available to anyone who asks, including the attacker. So the attacker computes a completely valid MAC using the very key that was supposed to verify them, and the plugin nods and logs them in as whoever they said they were.


They signed the message with the lock.


Anyone who lived through the JWT alg confusion era of 2015 recognises this immediately. Same bug, new protocol, eleven years later. The lesson that time was never let the attacker choose the algorithm, and it is still the lesson, and here we are.





Our receipt, and the comparator that makes it mean something


We have a hard rule here: no lead claim ships without the external comparator printed next to our own timestamp. We published a false one in July and had to correct it in public, and the correction is still on this blog. So:



Date

Source

Our exploit-harvester collects a public PoC for CVE-2026-15981 and extracts the plugin path as a detection rule

26 July 2026, 12:00 UTC

exploit-harvesterfirst-party

DigitalOcean detects the first observed exploit attempt — a compromised WordPress admin session traced to an unauthorised source

16 August 2026

comparator

Patchstack publishes root-cause analysis and the full edition matrix

21 August 2026

comparator

CISA KEV listing

not as of 25 August 2026

checked today


Twenty-one days between our detection rule existing and anyone observing this bug being used in anger.


That is not a discovery claim and I want to be precise about it, because the industry is full of people quoting an ingest timestamp as if it were a scoop. The CVE existed before we saw it. Somebody else found the bug. What our cron did was notice that working exploit code had gone public and write down what it targeted, three weeks before the first victim we know about. We hold nothing at all for CVE-2026-61979 — that one we were nowhere near.


The thing we can defend is the shape of the lead: we are early at weaponisation, not at disclosure. Nobody reliably beats the field at disclosure. But the window between the exploit is downloadable and your logs contain it is where a defender actually lives, and that window was three weeks wide on this one.



Go and grep, right now, and it costs nothing


Our harvester extracted the target path out of the exploit code: /wp-content/plugins/miniorange-saml-20-single-sign-on/, alongside /wp-login.php.


If you run this plugin, search your access logs for POSTs to the SAML assertion consumer endpoint under that plugin path, and cross-reference against sessions that ended up with administrator capability. The bug produces a successful authentication, so the tell is not an error — it is a login that worked and should not have. Look for admin sessions whose IP or user agent does not match that user's history.


Six IP addresses were observed scanning miniOrange SSO endpoints during this campaign: [207.211.214.41](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=207.211.214.41), [79.127.224.14](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=79.127.224.14), [102.91.71.83](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=102.91.71.83), [162.243.116.148](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=162.243.116.148), [84.201.6.54](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=84.201.6.54) and [64.225.25.188](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=64.225.25.188). Patchstack characterises the activity as opportunistic scanning rather than a targeted campaign.


We have put those into our feed at confidence 70, attributed, with the reasoning in the record. They are rented cloud hosts — DigitalOcean and Yandex Cloud — and rented hosts get reassigned, so a defender who blocks on them today may be blocking somebody's staging server next month. That is why they carry a 70 and not a 90. Our feed publishes what we know with a score attached; you choose the threshold your posture can afford. Pull at 80 and these do not appear. Pull at 30 and they do, because you are hunting rather than blocking. That decision is yours and it was never ours to make for you.



The part that will actually bite people: one slug, seven editions


Here is the operational trap, and it has nothing to do with cryptography.


The plugin ships under a single WordPress slugminiorange-saml-20-single-sign-on — with seven independently versioned editions behind it. The patched version is different for every one:



Edition

Patched at

Free

5.4.5

Premium, single site

13.0.4

Standard, single site

17.0.6

Premium / Enterprise / All-Inclusive, multisite

20.2.8

Enterprise / All-Inclusive, single site

26.0.3

VIP, single site

32.0.8

VIP, multisite

35.0.7


Read that column. Version 17.0.6 is patched. Version 20.0.0 is not. A bigger number that is older code, because it is a different edition wearing the same name.


So "are you on the latest version?" — the question every managed-WordPress dashboard, every client status report, and every compliance checklist asks — has seven different correct answers here, and six of them are wrong for you. Automated update tooling that reasons about version ordering across a single slug is going to get this wrong in whichever direction is most convenient for the attacker.


And the person most likely to get it wrong is the one running the free edition on a site they maintain for a client, for a church, for a food bank, because that is who runs free editions. This is a distribution failure stacked on top of a crypto failure, and the second one lands hardest on the people with the least slack.



What to do tonight, in order


Identify which of the seven editions you actually have — the version number alone will not tell you, so check the plugin header or your purchase record. Update to that edition's patched version. Then invalidate every existing session, because patching the bug does not evict anyone who already walked through it. Then audit your administrator accounts for users you did not create, and check the modification dates on your theme and plugin files.


If you can, put the SSO endpoint behind an IP restriction or a WAF rule while you do it. This bug is unauthenticated and pre-session — there is nothing to rate-limit and no failed-login counter that will ever tick.



Sources


CVE-2026-15981 (CVSS 9.8, authentication bypass via improper handling of openssl_verify() return values) and CVE-2026-61979 (CVSS 8.1, signature-algorithm confusion), in the miniOrange SAML 2.0 Single Sign On WordPress plugin. Discovered by the DigitalOcean security team; root-cause analysis, the seven-edition matrix and the scanning IP set published by Patchstack, 21 August 2026. Reported by The Hacker News, 25 August 2026.


Our PoC collection timestamp, the extracted plugin-path detection rule and the confidence-70 IP records are from our own exploit-harvester and iocs index, source=exploit-harvester and source=manual-batch-miniorange-saml-scanning.


Capped at 95 percent, as always: we have no first-party telemetry of a successful compromise against a specific site, the scanning IP set is Patchstack's rather than ours and all six came back clean in our own corpus with zero corroborating hits, and KEV listings can land at any hour — if this is catalogued between our check and your read, good.




If you manage WordPress for other people, the ten-minute job tonight is not reading this post — it is working out which edition each of your clients is actually on. I would genuinely like to know how many of you can answer that from a dashboard without opening a file. Rate this post below.




Every indicator in this post is in the feed. Free.

1.58M+ IOCs, STIX 2.1 / TAXII, 88% novel vs ThreatFox, exploited-CVE leads ahead of CISA. No credit card — a free API key in 30 seconds, and you can audit every claim above against the live endpoints.



Was this useful? Thirty seconds, no cookies, no tracking, no third parties, your address hashed and never stored. If the box below does not load, the same question lives at https://analytics.dugganusa.com/nps.html?post=the-signature-failed-to-verify-so-it-was-accepted-that-is-not-a-typo-it-is-cve-2026-15981-it-is



Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page