← All audits

Audit #12 Malicious Closed by wp.org · trunk uncleaned

Scroll To Top Closed on WP.org · 20k+ installs · baseline → head 1.5.3 · by beacon-scan-skill · closed 7d ago

Actor: Benjamin (wp.org @milkitall, GitHub tombenj, tomgolan@gmail.com) — operates the inherited @satrya SVN account
Show full summary

Update-checker hijack with active stored-XSS / RCE primitives served from a Panama-fronted C2.

scroll-top (20,000 active installs) was sold by original author Ga Satrya (@gasatrya) to an actor identified as Benjamin (wp.org @milkitall, GitHub tombenj, email tomgolan@gmail.com) sometime in 2023. Ga Satrya publicly confirmed the sale on 2024-06-13 in an unrelated forum thread for his sibling plugin smart-recent-posts-widget. Benjamin operates the inherited @satrya SVN account.

The wp.org-served trunk version 1.5.3 (committed 2023-11-21 as r2999765 by @satrya with the innocuous message "Creating tag for version 1.5.3") bundles the Yahnis Elsts plugin-update-checker library and registers an update channel pointing at updates.cdnstaticsync.com — a domain registered nine days prior (2023-11-12) via Panamanian privacy-mask registrar, fronted by Cloudflare. From that update endpoint, every wp-cron polling install fetches a tampered v1.5.5 zip containing two new files:

  • inc/class-scroll-top-content-updater.php (236 lines) — daily cron + wp_loaded handler + unauthenticated ?gimme=updates query trigger that POSTs the victim's hostname to https://edge.cdnstaticsync.com/bro/3/<hostname> (same /bro/3/ path convention used by the 2021 anadnet C2), receives JSON of {url, start, newContent} injection directives, and rewrites post_content (or, for Elementor pages, impersonates the first admin via wp_set_current_user and saves through the document model). SSL verification is explicitly disabled (CURLOPT_SSL_VERIFYPEER => false).
  • inc/class-scroll-top-logs.php (21 lines).

Two primitives are wired up on every install: (1) stored XSS via daily content-rewrite — currently active. (2) Full RCE via zip-swap — Benjamin controls the download_url field served by updates.cdnstaticsync.com; replacing the 1.5.5 zip with one containing eval($_POST['c']) reaches every install on the next 12-hour update poll, with full plugin-author filesystem permissions, no wp.org involvement.

Three sibling plugins remain under the same @satrya SVN account Benjamin now operates — advanced-random-posts-widget (10k installs), smart-recent-posts-widget (9k installs), comments-widget-plus (2k installs). Their wp.org trunks scan clean as of 2026-04-24, but each represents a one-commit hijack opportunity. Combined exposure under Benjamin's push access: 41,000 installs.

Same shape as the 2021 anadnet/quick-pagepost-redirect-plugin compromise — clean wp.org code that registers a non-wp.org update source, then the side channel serves the actual payload. Difference: anadnet's domain is NXDOMAIN today; cdnstaticsync.com is alive and serving fresh injections.

🛑
20k+ installs are running compromised code right now.

Site owners should remediate immediately. Plugin author: see the steps below to clear this label.

If you run scroll-top on your site

Verify your install matches the wp.org canonical version:

wp plugin verify-checksums scroll-top

Install the patched build:

⬇ Download patched zip — or install directly via WP-CLI:

wp plugin install https://plugins.captaincore.io/scroll-top-1.5.5-patched.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 scroll-top
wp plugin delete scroll-top

If you're the plugin author

To remove the hijacked label from this plugin:

  1. Push a new SVN tag (1.5.4 or higher) to plugins.svn.wordpress.org with the entire use YahnisElsts\PluginUpdateChecker block removed from scroll-top.php (lines 41-46), the bundled plugin-update-checker/ directory removed, and the require statement that loads it removed.
  2. Stop serving the tampered 1.5.5 zip from updates.cdnstaticsync.com — every install currently running 1.5.5 is back-dooring its own pages daily. Either point updates.cdnstaticsync.com/updates/?action=download&slug=scroll-top at the clean wp.org zip, or take the endpoint offline so installs fail back to wp.org.
  3. The label clears automatically on the next wp beacon scan-deltas once the wp.org trunk no longer matches the puc_update_hijack pattern with a non-wp.org host.

The label clears automatically on the next wp beacon scan-deltas once the cleanup conditions above are met.

Plugins under the same committer's SVN access

satrya holds push access to 5 plugins totalling 49k+ active installs. Each non-target plugin scans clean today but represents a one-commit hijack opportunity.

Scroll To Top — COMPROMISED — this audit
20k+
Advanced Random Posts Widget — closed by wp.org
10k+
Smart Recent Posts Widget — closed by wp.org
9k+
TJ Custom CSS — clean code, same SVN account (latent risk)
8k+
Recent Comments Widget Plus — closed by wp.org
2k+

IOCs extracted (11)

Kind Value Confidence
code_pattern ?gimme=updates high
code_pattern /bro/3/ high
code_pattern cdnstaticsync high
code_pattern gouldbenjamin135@gmail.com high
code_pattern milkitall high
code_pattern tombenj medium
domain cdnstaticsync.com high
domain edge.cdnstaticsync.com high
domain gouldbenjamin135@gmail.com high
domain updates.cdnstaticsync.com high
filename class-scroll-top-content-updater.php high

Timeline

  1. scroll-top first published by Ga Satrya
  2. tombenj/scroll-top first appears on GitHub (Benjamin starts mirroring satrya's code)
  3. @milkitall wp.org account created
  4. r2989225 by @satrya "modify author" — removes satrya from contributors, leaves milkitall
  5. cdnstaticsync.com registered (Panama privacy, Cloudflare-fronted)
  6. r2999765 by @satrya "Creating tag for version 1.5.3" — bundles PUC library, adds buildUpdateChecker call ← the hijack lands
  7. internal last_updated on cdnstaticsync.com endpoint — when Benjamin assembled the tampered 1.5.5
  8. Ga Satrya publicly confirms ownership transfer on wp.org forum
  9. investigation; wp.org still serves 1.5.3 with the hijack; cdnstaticsync.com still serves the tampered 1.5.5

Audit #12 — scroll-top

  • Plugin: scroll-top (Scroll To Top)
  • Active installs: 20,000
  • Event: #728 code_pattern · critical · 2026-04-24 23:25:12
  • Baseline version: none — no clean pre-suspect release
  • Head version: 1.5.3
  • Working dir: /tmp/wpbeacon-audits/audit-12

Summary

Update-checker hijack with active stored-XSS / RCE primitives served from a Panama-fronted C2.

scroll-top (20,000 active installs) was sold by original author Ga Satrya (@gasatrya) to an actor identified as Benjamin (wp.org @milkitall, GitHub tombenj, email tomgolan@gmail.com) sometime in 2023. Ga Satrya publicly confirmed the sale on 2024-06-13 in an unrelated forum thread for his sibling plugin smart-recent-posts-widget. Benjamin operates the inherited @satrya SVN account.

The wp.org-served trunk version 1.5.3 (committed 2023-11-21 as r2999765 by @satrya with the innocuous message "Creating tag for version 1.5.3") bundles the Yahnis Elsts plugin-update-checker library and registers an update channel pointing at updates.cdnstaticsync.com — a domain registered nine days prior (2023-11-12) via Panamanian privacy-mask registrar, fronted by Cloudflare. From that update endpoint, every wp-cron polling install fetches a tampered v1.5.5 zip containing two new files:

  • inc/class-scroll-top-content-updater.php (236 lines) — daily cron + wp_loaded handler + unauthenticated ?gimme=updates query trigger that POSTs the victim's hostname to https://edge.cdnstaticsync.com/bro/3/<hostname> (same /bro/3/ path convention used by the 2021 anadnet C2), receives JSON of {url, start, newContent} injection directives, and rewrites post_content (or, for Elementor pages, impersonates the first admin via wp_set_current_user and saves through the document model). SSL verification is explicitly disabled (CURLOPT_SSL_VERIFYPEER => false).
  • inc/class-scroll-top-logs.php (21 lines).

Two primitives are wired up on every install: (1) stored XSS via daily content-rewrite — currently active. (2) Full RCE via zip-swap — Benjamin controls the download_url field served by updates.cdnstaticsync.com; replacing the 1.5.5 zip with one containing eval($_POST['c']) reaches every install on the next 12-hour update poll, with full plugin-author filesystem permissions, no wp.org involvement.

Three sibling plugins remain under the same @satrya SVN account Benjamin now operates — advanced-random-posts-widget (10k installs), smart-recent-posts-widget (9k installs), comments-widget-plus (2k installs). Their wp.org trunks scan clean as of 2026-04-24, but each represents a one-commit hijack opportunity. Combined exposure under Benjamin's push access: 41,000 installs.

Same shape as the 2021 anadnet/quick-pagepost-redirect-plugin compromise — clean wp.org code that registers a non-wp.org update source, then the side channel serves the actual payload. Difference: anadnet's domain is NXDOMAIN today; cdnstaticsync.com is alive and serving fresh injections.

Verdict

malicious

Added files (0)

_No new files between baseline and head — the wp.org trunk is the hijack-shipping version. The 236-line content-updater class only ships in the side-channel 1.5.5 zip from updates.cdnstaticsync.com, not in the wp.org trunk._

Suspicious pattern hits (7)

puc_update_hijack — 2 hits

  • scroll-top.php:42$UpdateChecker = PucFactory::buildUpdateChecker(the hijack call
  • plugin-update-checker/Puc/v5p2/PucFactory.php:54 — library boilerplate (the factory's own definition; structural FP — bundled with every PUC user)

cdnstaticsync.com / updates.cdnstaticsync.com / cdnstaticsync — 3 hits

base64_decode — 2 hits

  • plugin-update-checker/Puc/v5p2/Vcs/GitLabApi.php:334 — library implementation detail (unrelated to backdoor)
  • plugin-update-checker/Puc/v5p2/Vcs/GitHubApi.php:319 — same

IOCs to extract

The full IOC set was inserted into wp_wpbeacon_iocs prior to finalize. Re-listing here for audit-source linkage:

  • kind: domain, value: cdnstaticsync.com, confidence: high
  • kind: domain, value: updates.cdnstaticsync.com, confidence: high
  • kind: domain, value: edge.cdnstaticsync.com, confidence: high
  • kind: code_pattern, value: cdnstaticsync, confidence: high
  • kind: code_pattern, value: /bro/3/, confidence: high
  • kind: filename, value: class-scroll-top-content-updater.php, confidence: high
  • kind: code_pattern, value: ?gimme=updates, confidence: high
  • kind: code_pattern, value: milkitall, confidence: high
  • kind: code_pattern, value: tombenj, confidence: medium

Full diff

The relevant diff is between the wp.org trunk 1.5.3 and the side-channel 1.5.5 zip from updates.cdnstaticsync.com (per the incident writeup at the public writeup on anchor.host):

$ diff -rq wporg/scroll-top cdn/scroll-top
Only in cdn/scroll-top/inc: class-scroll-top-content-updater.php   (new, 236 lines)
Only in cdn/scroll-top/inc: class-scroll-top-logs.php              (new, 21 lines)
Files wporg/scroll-top/readme.txt and cdn/scroll-top/readme.txt differ   (Stable tag: 1.5.3 → 1.5.5)
Files wporg/scroll-top/scroll-top.php and cdn/scroll-top/scroll-top.php differ   (version bump + 2 require_once)

Attribution

  • Original author (sold the plugin): Ga Satrya — wp.org @gasatrya, GitHub gasatrya (created 2011), blog ctaflow.com, Bandung Indonesia.
  • Hijacker: Benjamin — wp.org @milkitall (created 2023-10-29, sole forum reply to date is the smart-recent-posts-widget XSS thread on 2024-06-13), GitHub tombenj (created 2011, 17 public repos), email tomgolan@gmail.com. Public repos include forks of Polymarket CTF Exchange, Solana core, XMRig Monero miner, UMA CTF adapter, HuggingFace AutoTrain.
  • SVN account operated by Benjamin: wp.org @satrya (the SVN account that came with the plugin sale; display name still "Ga Satrya"). All commits attributed to satrya after 2023-10-29 are Benjamin pushing through the inherited account.

Timeline

  • 2014-02-22 — scroll-top first published by Ga Satrya
  • 2018-05-13 — tombenj/scroll-top first appears on GitHub (Benjamin starts mirroring satrya's code)
  • 2023-10-29 — @milkitall wp.org account created
  • 2023-11-05 — r2989225 by @satrya "modify author" — removes satrya from contributors, leaves milkitall
  • 2023-11-12 — cdnstaticsync.com registered (Panama privacy, Cloudflare-fronted)
  • 2023-11-21 — r2999765 by @satrya "Creating tag for version 1.5.3" — bundles PUC library, adds buildUpdateChecker call ← the hijack lands
  • 2024-02-25 — internal last_updated on cdnstaticsync.com endpoint — when Benjamin assembled the tampered 1.5.5
  • 2024-06-13 — Ga Satrya publicly confirms ownership transfer on wp.org forum
  • 2026-04-24 — investigation; wp.org still serves 1.5.3 with the hijack; cdnstaticsync.com still serves the tampered 1.5.5