Skip to content

Build1 publisher3 min readPublished

Every wiki that ran MediaWiki External Data before 3.7 needs a hunt for planted PHP shells

Automated exploit attempts hit MediaWiki's External Data extension within a day of the 25 September disclosure of CVE-2026-100382, a CVSS 10.0 flaw. Upgrading to 3.7 closes the entry point but leaves behind any PHP shell an attacker already wrote to disk.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Every wiki that ran MediaWiki External Data before 3.7 needs a hunt for planted PHP shells
Generated illustration

What happened

  • From version 3.0 the External Data extension could run local programs on the server, and commands passed through one of its parser functions were not filtered.
  • Exploitation is confirmed in the wild, and the proof of concept is public in Wikimedia Phabricator task T434961.
  • In the reported intrusion, the attacker first queried the API to check whether the extension was installed, then sent a rapid series of POST requests, then retrieved PHP files the payload had just written.
  • According to the write-up, Lajoie found the planted shell in the wiki's skins directory.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure A shell that survives the upgrade can still read LocalSettings.php, so the wiki's database credentials, secret keys and API keys stay within an attacker's reach after patching.
  • cost Every host that ran a pre-3.7 release has to rotate its database password, secret keys and administrator passwords and replace stored API keys, whether or not a shell turns up.
  • decision Scope has to come from the extension list in Special:Version and on disk, because External Data is optional and a MediaWiki build number does not show whether it is installed.

Wikitext is untrusted content. According to a detection write-up on dev.to, External Data's parser function forwarded it into a server-side program invocation, and the default extension setup left that path reachable [3]. The CVE record says no authentication or privilege is required [6]. The write-up concludes that every request reaching a vulnerable wiki is a potential exploitation attempt, successful or not [6].

From the intrusion pattern the post derives four checks [8]. First is a file-system pass over PHP files created since 25 September in writable, web-served directories, with names matching Nx_.php or NX_.php at the front of the queue [8]. Skin and upload directories belong in that first pass [9].

That window opens on the disclosure date. The unfiltered command path dates to version 3.0 [1]. A content-integrity diff has no start date: compare the extension and skin directories against a known-good deployment, and anything an attacker added stands out because it sits outside the platform's version control [11]. I would run the diff first. A shell written before 25 September would fall outside the file-system window and still show up in the diff [2].

In the logs, the post correlates the three intrusion steps from one source address [10]. It calls the last step the most distinctive, because legitimate traffic rarely fetches a file moments after it appears [10]. Its sample filter does not look for that step. The code compiles `api\.php` and prints a line when the pattern matches and the line contains `POST` or `.php` [13]. Every line that matches already contains `.php`, so the `or` test is always true and could be deleted without changing the output [1]. What comes out is every api.php request, GET or POST, and never a GET for a fresh file under skins or uploads [1]. Catching the retrieval needs a second rule for GETs of .php paths other than api.php [1]. To be fair, the author tells readers to treat the output as leads and to correlate source addresses and time clusters [13].

The fourth check is the best advice in the post. It asks operators to verify in the web server's own configuration that upload directories cannot execute PHP [12]. Relying on .htaccess is not proof, because such a rule can be bypassed or ignored depending on configuration [12].

For sizing, a ZoomEye search for `http.body="ExternalData"` returned 1,128 assets at the time of writing [16]. The figure counts response bodies that contain a string [16]. To equal the number of exposed wikis, every hit would have to run a pre-3.7 release and every vulnerable wiki would have to print the string where the crawler looked [3]. The author calls it a scoping hint, not a vulnerability confirmation [16].

Cleanup has an order too. Remove confirmed shells and re-scan, because a partial cleanup leaves the same access path open, then confirm the deployed version on Special:Version as the last step [18].

What to watch

  • An update to Phabricator task T434961 or the CVE record dating first exploitation before 25 September would push the file-system search window back.
  • Published indicators beyond the Nx_.php naming pattern, such as file hashes or source addresses from the observed campaign, would let operators hunt without relying on creation dates.
  • Reports of credentials from LocalSettings.php being used against other systems would turn wiki cleanup into a wider incident for affected operators.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories