Contents

WordPress Let One Request Read Your Database for 227 Days

 

Ethical Hacking Complete Course Zero to Expert

Hack like black hat hackers. Penetration testing, Kali Linux, WiFi and web hacking, and the hacker mindset behind it.

→ Take the full course
 
Contents

A hole in WordPress handed your database to someone who never logged in. For 227 days it took one request. It needed no password and no plugin, just an address that has been part of WordPress since December 2020. ๐Ÿง

The patch is the problem.

WordPress is open source, so the repair had to be published. It went up on Friday afternoon: three commits, three files, timestamped 16:27 UTC. Put the old version next to the new one and you can read what was broken.

The researchers who found it are trying to hold that line anyway. Adam Kues at Searchlight Cyber reported it, and their write-up says: “Given the severity of the bug and to give defenders time to patch, we are not releasing technical details at this time.”

The code below came from WordPress itself.

Since WordPress 5.6, in December 2020, there has been an address on a WordPress site that lets you send several instructions in one go. It is meant for developers building on top of a site. It sits at /wp-json/batch/v1, and you do not have to be logged in to reach it.

Start with the easier half. Ask a site for posts and say “not written by these authors”, and WordPress takes your list and drops it into the database query. This is the code that was live until Friday:

1
2
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";

The catch sits one line above it. WordPress only forced those values into plain numbers when they arrived as a list. Send them a different way and your text went into the query untouched. That is what SQL injection is: your input stops being data and becomes part of the command.

The repair turns whatever you send into a clean list of numbers:

1
$author__not_in_id_list = wp_parse_id_list( $query_vars['author__not_in'] );

Now the other half, and this is the one the coverage skips.

When you send a batch, WordPress builds two lists side by side. One holds which handler will serve each of your instructions. The other holds whether each one passed its checks. Later, WordPress walks the instructions by number and reads position one from the first list and position one from the second, and so on.

That only works if both lists stay the same length.

Until Friday, they did not. If one instruction in your batch had a path WordPress could not read, it went into the second list but not the first. From that point the numbering slipped by one, and each following instruction was carried out by the handler belonging to its neighbour.

That is the route confusion. The fix is one line:

1
$matches[] = $single_request;

And there is something on the page that is supposed to tell site owners what to do. Under the heading Mitigation, the advisory says:

“The best way to protect yourself is to update WordPress to version XXX as soon as possible.”

XXX. A placeholder that never got filled in. I checked again this morning, more than fourteen hours after it went live, and it is still sitting there. The page that exists to name the safe version does not name it.

WordPress has been here before. In January 2017 a researcher at Sucuri found a hole in this same interface. WordPress fixed it quietly and waited a week before saying anything, for the reason given now: give sites time. When it went public, attackers read the repair and worked backwards. Wordfence counted more than 1.5 million defaced pages within days.

Here is what happened this time. The patch went public at 16:27 UTC. The first public repository claiming a working exploit appeared on GitHub at 20:18 UTC the same evening. Three hours and fifty one minutes.

By this morning there were twenty four of them. Scanners, Docker labs with a vulnerable WordPress inside, and full write-ups of the root cause. One says plainly that its analysis came from comparing the old version against the new one.

There was no leak. They read the patch.

Now the part where I want to be careful with you, because this is where most of the coverage will be wrong in a week.

Searchlight calls this remote code execution. So do WordPress and Cloudflare. But read the independent write-ups that went up overnight and a more precise picture appears.

What is certain: an unauthenticated SQL injection with full read access to the database. Password hashes, the contents of wp_options.

Getting from reading the database to running code needs three more things to line up. The database user has to hold a privilege that cPanel and managed hosts do not hand out. There has to be a directory MySQL may write to that the web server also serves. And the file that lands there has to be readable by the web server. On ordinary shared hosting that does not line up, and it stays a readable database.

Cloudflare put a condition on it too, in writing: the code execution path works “when a persistent object cache is not in use”. Searchlight says the attack has no preconditions. Those two statements do not agree, and nothing has been published to settle it.

So treat it this way. Assume the database was readable. Treat the rest as disputed, and watch who publishes next.

A small business has a site somebody built three years ago and then moved on. Auto-updates got switched off during a plugin conflict and never switched back. The owner logs in twice a year to change the opening hours. WordPress release notes on a Friday in July do not reach that owner.

That site is sitting there right now, with the map to the hole public.

Do this today:

  • โ†’ Log in, open Dashboard, then Updates. You want 7.0.2, or 6.9.5 on the 6.9 branch
  • โ†’ Affected: 6.9.0 through 6.9.4, and 7.0.0 through 7.0.1. See one of those numbers and you are exposed right now
  • โ†’ On 6.8 you do not have the batch flaw, but you do have the same SQL injection. 6.8.6 fixes it
  • โ†’ Cannot update yet? Block both /wp-json/batch/v1 and rest_route=/batch/v1 at your firewall. Blocking only the first one leaves the second way in wide open
  • โ†’ Ran a vulnerable version on a site that matters? Change the admin passwords and rotate your salts and keys. Password hashes were readable

The two flaws are logged as CVE-2026-63030 and CVE-2026-60137.

Take a chain like this apart and it comes down to one input that was never cleaned. At the end of it somebody is reading a database that is not theirs. That is the skill I teach in my ethical hacking course. You inject by hand first, so you understand what you are doing. Then SQLMap. Then command injection, where web input turns into control of the machine:

โ†’ Join my complete ethical hacking course

Hacking is not a hobby but a way of life.

Sources:

WordPress 7.0.2 Release | Searchlight Cyber Advisory | WordPress Core Patch Diff

 
NEWSLETTER

Stay updated

Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.

By Bulls Eye

Jolanda de koff โ€ข email โ€ข donate

My name is Jolanda de Koff and on the internet, I'm also known as Bulls Eye. Ethical Hacker, Penetration tester, Researcher, Programmer, Self Learner, and forever n00b. Not necessarily in that order. Like to make my own hacking tools and I sometimes share them with you. "You can create art & beauty with a computer and Hacking is not a hobby but a way of life ...

I โ™ฅ open-source and Linux