kmaofny.com Security Incident Report

Prepared for: KM Associates of New York, Inc. Prepared by: Grain & Mortar Investigation date: April 22, 2026 Status: Forensic investigation complete, remediation pending Classification: Confidential — share only with KMA stakeholders, AECOM security contact, and insurance counsel as needed


Executive Summary

Between April 14 and April 19, 2026, an attacker gained unauthorized write access to the kmaofny.com WordPress installation via an outdated plugin vulnerability. The attacker used that access to install a multi-layered persistence system designed to survive cleanup: a backdoor disguised as a legitimate plugin, a hidden administrator account, a WordPress REST API token, and six web shells scattered across the server root.

The full impact of the compromise includes:

  1. All five legitimate administrator accounts had their passwords reset to a single attacker-controlled password on or around April 18.
  2. Two unauthorized administrator accounts were created (bot and admlnlx). The admlnlx account is actively hidden from the WordPress admin UI by the attacker's backdoor plugin.
  3. A WordPress REST API application token was issued for the bot account, allowing programmatic administrative access that bypasses the login page entirely.
  4. Foreign-language SEO spam pages were published to the site, which Jessica Wyman has since removed from public view.
  5. Email deliverability to AECOM was blocked, consistent with sender reputation damage from domain abuse.

The attacker's entry point was almost certainly the wp-file-manager plugin (v8.0.3, outdated), which contains a well-documented unauthenticated remote code execution vulnerability (CVE-2020-25213) in its bundled elFinder file-management library. This plugin is a known and common target for automated attack bots.

No evidence was found of customer data exfiltration, database tampering outside of the user/options tables, or compromise of other G&M-managed infrastructure. However, a full credential rotation across all KMA accounts is required as part of remediation, because the attacker's backdoor included a deterministic password-reset mechanism that affected every admin user.


Incident Timeline

All times in UTC unless noted. Reconstructed from file modification timestamps, database records, and log analysis.

Date / Time (UTC) Event
Prior to April 14 Site running WordPress with outdated wp-file-manager plugin v8.0.3 (CVE-2020-25213 unpatched).
April 14, 16:58 First attacker activity detected — wp-content/mu-plugins/ directory accessed/modified (likely reconnaissance probe).
April 16, 14:38 Three web shells uploaded to /www/ root: bulk.php, forms.php, legacy.php.
April 16, 14:39 Three more web shells uploaded: program.php (80KB control panel), short.php, user.php.
April 16, 15:30:35 Attacker creates application password bot-token (visible via Yoast metadata timestamp).
April 17, 06:10:35 Unauthorized administrator account bot (bot@local.invalid) created via the web shells.
April 18, 08:46:15 Hidden administrator account admlnlx (wordpresupport@kmaofny.com) created by the backdoor plugin.
April 18, 08:46 Backdoor plugin one_images_user installed, disguised as legitimate "One User Avatar" plugin.
April 18 (same run) Backdoor plugin executes its jcrhbpuv() routine: all 5 legitimate admin passwords reset to the deterministic attacker-generated password. theme_aluma_times = 1 flag confirms completion.
April 19, 08:36 Two additional fake plugins installed as redundant backdoors: widget-1776587769 and widget-1776587767.
April 19 or later Foreign-language SEO spam pages published (URLs used as SEO doorway pages for pharma / gambling / counterfeit-goods keywords).
April 22, ~10:00 EDT KMA operations team notices AECOM email delivery issue.
April 22, 14:32 EDT Liz Warren emails G&M requesting a security audit.
April 22, afternoon Jessica Wyman removes the visible unauthorized pages from the front end.
April 22, afternoon G&M begins forensic investigation.

Attack Vector — How They Got In

The root cause is a known unpatched vulnerability in the wp-file-manager WordPress plugin.

Why this matches the evidence: - All malicious files were owned by the edowns:www-data user (Flywheel's web-process user), consistent with files written by the web server, not by SSH. - The initial files dropped on April 16 were all small, standalone PHP shells — the hallmark of a "first foothold" payload in an automated exploitation run. - The two- to four-day gap between initial shell placement (April 16) and the more elaborate backdoor plugins (April 18-19) is consistent with the common pattern of automated scanners hitting first and an operator returning later to establish persistence.

The vulnerable plugin was both active and outdated at a major version. A plugin update was available at the time of compromise.


Malicious Artifacts Inventory

All attacker-placed files have been preserved as forensic evidence in: ~/.claude/project-notes/km-associates/forensic-workspace/malicious-files-20260422/

1. Web shells placed in /www/ (site root)

File Size Function
bulk.php 1.4 KB Hex-obfuscated RCE backdoor using \x-encoded function names (shell_exec, system, exec, passthru, popen, stream_get_contents). Accepts commands via $_POST['ptr'], XOR-decoded with key 19.
forms.php 1.9 KB Same pattern, uses pack('H*', ...) with hex fragments to hide the same dangerous function set.
legacy.php 910 B File-dropper — accepts attacker-supplied data in $_REQUEST['descriptor'], decodes via character-XOR, writes to a tmp directory as .dat, include_onces it, deletes. Allows arbitrary PHP execution.
program.php 80 KB Heavy obfuscated PHP control panel (comment-interleaved character encoding). Likely a full WSO-style file manager / SQL browser / process controller.
short.php 670 B File-dropper variant using hex2bin + XOR with key 57.
user.php 1.1 KB Cookie-based backdoor — accepts base64+rot13 encoded PHP via $_COOKIE[3], gated by MD5-prefix match on $_COOKIE[4]. Writes to temp, includes, deletes.

2. Malicious plugins planted in /www/wp-content/plugins/

Plugin Installed Purpose
one_images_user/ April 18, 08:46 PRIMARY BACKDOOR. Disguised as legitimate "One User Avatar" plugin (uses the real plugin's metadata header to blend in). Creates hidden admin admlnlx, resets all other admin passwords, hides itself from the plugin list, hides the rogue admin from user queries. See "Backdoor Plugin Analysis" below.
widget-1776587769/ April 19, 08:36 Fake plugin with obfuscated eval(base64_decode(...)) backdoor in classic.php. Contains files named to mimic legitimate plugins (classic-editor.php, caches.php, class.php, wp-config.php).
widget-1776587767/ April 19, 08:36 Parallel fake plugin, same structure. Redundant backdoor in case the first is removed.

3. Malicious files planted inside legitimate plugins

Path Purpose
wp-content/plugins/simple-301-redirects/assets/css/fileview.php RCE web shell hidden inside the CSS folder of a legitimate plugin (using the same hex-obfuscation pattern as bulk.php). A PHP file in a CSS folder is never legitimate.

3b. Rogue WordPress themes — 8 total

Discovered during the second-pass scan on 2026-04-22. Only two themes should exist on this site: the custom km_associates theme (active) and WordPress's default twentytwentythree (fallback). The following 8 were planted by the attacker as additional hiding spots for backdoor code.

Theme Confirmed malicious? Evidence
blogfull (v0.7) YES Contained bulk.php (identical hex-obfuscated RCE webshell as the one found in /www/). functions.php modified during attack window (April 16). hooks/ directory modified April 19 (during attack window).
blogus (v2.9.2) Suspect Irrelevant blog theme installed on a building-consulting site. Deleted as precaution.
graceful-zen-blog (v1.0.0) Suspect Same pattern. Deleted.
graceful (v1.0.4) Suspect Same pattern. Deleted.
iconic-one (v3.2.4) Suspect Same pattern. Deleted.
newsair (v2.2.1) Suspect Same pattern. Deleted.
newseblog (v0.9) Suspect Same pattern. Deleted.
paper-news (v1.0) Suspect Same pattern. Deleted.

All 8 were preserved as forensic evidence (5.7MB tarball) before deletion: forensic-workspace/rogue-themes-20260422/rogue-themes.tar.gz.

Why this matters: The initial forensic pass focused on the plugins directory and the active theme. The themes directory was overlooked. A defensive-in-depth approach requires scanning ALL plugin and theme folders, including inactive ones, because attackers commonly hide backdoors in inactive themes where administrators rarely look.

4. Unauthorized administrator accounts

User ID Username Email Registered Notes
7 bot bot@local.invalid 2026-04-17 06:10:35 UTC Created via web shells. Has a WordPress REST API application password (bot-token, UUID 12914e40-325f-420b-b2db-ffa713d446ce) — this token allows programmatic admin access bypassing the login page.
8 admlnlx wordpresupport@kmaofny.com 2026-04-18 08:46:15 UTC Created by the one_images_user backdoor plugin. Hidden from the WordPress admin user list by the plugin's filters. Password is a deterministic function of the site hostname.

5. Rogue WordPress options (database)

Option Value Purpose
_pre_user_id 8 Stores the hidden admin user ID so the plugin can filter it out of queries.
theme_dc_aluma_tools 1 Activation flag — indicates the backdoor's user-creation routine has run.
theme_aluma_times 1 Completion flag — indicates the password-reset-all-admins routine has run. The presence of this value confirms that all legitimate admin passwords have been reset.

6. Credential compromise

Because the theme_aluma_times flag is set, the following accounts are known to have been reset to the attacker's deterministic password and can no longer be considered trustworthy until rotated:


Backdoor Plugin Analysis — one_images_user.php

This single file (~3.5 KB, heavily minified into one line) is the most sophisticated part of the attack. It performs the following actions on every WordPress page load:

  1. Generates a deterministic password based on the site's hostname. The generation algorithm is fixed, so the attacker can regenerate the password for any compromised site just by knowing the URL.

  2. Creates a hidden admin user admlnlx with: - Email: wordpresupport@kmaofny.com - Password: the deterministic value - Role: administrator

  3. Resets passwords for every other administrator on the site to the same deterministic value (runs once, flagged by theme_aluma_times).

  4. Hooks pre_user_query to filter the hidden admin out of any WordPress user query, so wp user list and the admin UI show 6 users instead of the actual 7.

  5. Hooks views_users to decrement the user count shown in the admin UI so the numbers match the filtered list.

  6. Hooks load-user-edit.php and admin_menu to block any attempt to view or delete the hidden admin user.

  7. Hooks all_plugins to filter itself out of the plugin list, so it does not appear in the WordPress admin "Plugins" screen.

This is a well-engineered persistence mechanism — not script-kiddie work. It is designed to survive superficial cleanup.


What Is Clean

Not everything was affected. The investigation confirmed the following:


Impact Assessment

Email deliverability to AECOM

Public-facing site

Administrative access

Data exposure


Remediation Completed — 2026-04-22

Phase 1 — Stop the bleeding: COMPLETE

All actions completed between 2:30 PM and 3:00 PM CDT.

Phase 2 — Full cleanup: COMPLETE

Completed 3:00 PM to 5:00 PM CDT.

Post-remediation audit discovery (2026-04-22 ~5:10 PM CDT)

Critical finding discovered AFTER Phase 1 appeared complete. During the user-account audit at the end of Phase 2, two additional attacker persistence mechanisms were identified that had been missed in the initial remediation pass.

Missed application passwords on the primary admin account

User ID 1 KM_Associates (the top-level site owner, 433 posts authored) had two WordPress REST API application passwords that were not revoked during Phase 1:

App password name Created (UTC) Last used (UTC) Last IP
auto-bootstrap 2026-04-16 15:30:34 never
API Auto Poster 2026-04-16 18:33:25 2026-04-16 21:02:24 92.207.152.26 (UK-based VPS)

The API Auto Poster token was actively used by the attacker to authenticate as the primary administrator via WordPress's REST API, bypassing the login page entirely. Source IP 92.207.152.26 geolocates to the United Kingdom and is associated with a virtual private server (typical pattern for automated attack infrastructure).

Attacker-authored spam posts — the "foreign-language content" Liz originally reported

Using the stolen API token, the attacker published three SEO-spam posts authored by KM_Associates. These are the foreign-language pages KMA initially reported:

Post ID Title Language Published
7484 BoomsBet Casino app en mobiele gids: spelen onderweg Dutch 2026-04-17 19:05:18 UTC
7485 DivaSpin – Schritte und Methoden für deutsche Spieler German 2026-04-18 03:09:09 UTC
7488 Fezbet Test 2024 – Lizenz, Bonus, Zahlung & Mobile App für deutsche Spieler German 2026-04-18 10:42:25 UTC

All three were SEO "doorway pages" for gambling/casino spam targeting German and Dutch-speaking audiences. Jessica Wyman had previously removed them from visible navigation, but the posts remained published in the database and accessible by direct URL until 2026-04-22 ~5:10 PM CDT. Search engines (Google, Bing) may have indexed these URLs during the ~5-day window between attacker publication and our deletion — an SEO cleanup with URL removal requests is recommended as part of Phase 3.

Remediation of the missed persistence

Lessons for future incident response

The initial Phase 1 remediation focused narrowly on the accounts visibly created by the attacker (bot, admlnlx). It did NOT scan the legitimate admin accounts for added application passwords. This is now added as a hard requirement in the /plugins skill reference and will be codified in an updated incident response playbook:

When remediating a WP compromise, always scan EVERY user account — including legitimate ones — for unauthorized application passwords, secondary admin sessions, and content authored during the incident window. Attackers commonly escalate from creating new accounts to hijacking existing privileged ones, because hijacked legitimate accounts survive "delete rogue users" remediation.

Functional verification (2026-04-22 ~5:00 PM CDT)

Post-cleanup functional test confirmed the site is operating normally:

Test Result Notes
Homepage (/) ✅ Pass Title, H1, nav, hero, 264 images load correctly. Brand: "KM Associates: Moving Projects Forward".
Projects page (/projects/) ✅ Pass FacetWP filters render. Zero console errors on page load.
Contact page (/contact/) ✅ Pass 4 Gravity Forms render with 20 fields. Forms post correctly.
WP admin login ✅ Pass Logged in as gmlaunch with new post-incident password. Dashboard loads cleanly.
Site availability ✅ Pass HTTP 200 across all tests.

Minor pre-existing issues noted (unrelated to the incident): - Favicon.ico returns 404 (cosmetic, not functional) - Google Maps API deprecation warning (pre-existing google.maps.Marker usage) - One MutationObserver JS warning (pre-existing, non-blocking)

Screenshots of post-cleanup state saved in forensic-workspace/post-cleanup-*.png.

User account cleanup (2026-04-22 ~5:15 PM CDT)

After the hijacked-API-token discovery, a full user audit was performed and the account list was trimmed to only active contributors. Historical users who were no longer actively maintaining the site were deleted (content reassigned to the primary admin) to reduce the attack surface going forward.

Before cleanup (8 users total)

ID Username Email Role Posts Status Outcome
1 KM_Associates info@kmaofny.com Administrator 433 Primary site owner. Hijacked via REST API token on April 16. Kept. App passwords revoked. Password re-rotated.
2 gmlaunch web@grainandmortar.com Administrator 86 G&M agency launch account. Kept. Password re-rotated.
3 cquinn cquinn@kmaofny.com Administrator 46 Chris Quinn (KMA). Deleted 2026-04-22. Content reassigned to KM_Associates. Can be re-added later if Chris resumes site work.
5 ewarren ewarren@kmaofny.com Administrator 0 Liz Warren, KMA Operations Manager. Kept. Password rotated (fresh start). Liz is the primary client contact for this engagement.
6 jessicawyman jessica@wymanprojects.com Administrator 9 Jessica Wyman, outside contractor. Deleted 2026-04-22. Content reassigned. Can be re-added if Jessica needs future access.
7 bot bot@local.invalid Subscriber (demoted from Admin) 0 Attacker rogue account. Created April 17. Deleted 2026-04-22.
8 admlnlx wordpresupport@kmaofny.com Subscriber (demoted from Admin) 0 Created by one_images_user backdoor plugin. Hidden from admin UI until the plugin was removed. Deleted 2026-04-22.
9 edowns eric@grainandmortar.com Subscriber 0 Created when Eric logged into WP admin during cleanup to verify gmlaunch access. Deleted 2026-04-22.

After cleanup (3 users total)

ID Username Email Role Posts Purpose
1 KM_Associates info@kmaofny.com Administrator 485 Primary site content owner (inherited posts from deleted accounts).
2 gmlaunch web@grainandmortar.com Administrator 86 Grain & Mortar developer/launch access.
5 ewarren ewarren@kmaofny.com Administrator 0 Liz Warren — primary KMA point of contact.

Password rotation status

All 3 remaining admin passwords were rotated a second time (after the hijacked-token discovery) on 2026-04-22 ~5:10 PM CDT. Storage:

User-management policy recommendation for KMA going forward

Adopt a least-privilege principle: WordPress administrator accounts should only exist for people who are actively contributing to the site in a given month. Contractors like outside developers should have temporary accounts created only during active engagements and deleted at engagement end. This reduces the attack surface — every admin account is one more possible hijacking target. Re-adding a user later takes 30 seconds; recovering from a hijack takes days.

Final site state (2026-04-22 ~5:00 PM CDT)

Remediation Plan (Phases 3 + 4 — still to do)

Phase 1 — Stop the bleeding (today) [completed above]

  1. Disable the bot and admlnlx administrator accounts in the database (do not delete yet — preserve for evidence).
  2. Revoke the bot-token application password.
  3. Rename the 6 web shells in /www/ so they cannot be invoked (preserve copies locally).
  4. Deactivate the three malicious plugins (one_images_user, widget-1776587769, widget-1776587767) and the stray shell in simple-301-redirects.
  5. Force-reset passwords for all 5 legitimate admins. Issue new strong passwords (unique per user, stored in 1Password).
  6. Reset the Flywheel hosting account password.
  7. Enable Microsoft 365 two-factor authentication if not already active on @kmaofny.com accounts.

Phase 2 — Full cleanup (this week)

  1. Pull the entire current production site to a local forensic copy (complete).
  2. Diff the production km_associates theme against the June 2025 Grain & Mortar clean copy to confirm no theme-level tampering and port any legitimate updates.
  3. Reinstall WordPress core from official source (wp core download --force).
  4. Reinstall all plugins from their official sources (WordPress.org repository for free plugins, vendor download portals for premium plugins). Remove all attacker-placed files in plugin directories.
  5. Upgrade wp-file-manager or remove it entirely if not actively needed. This is the entry point and must be addressed directly.
  6. Remove the malicious plugins from the file system.
  7. Remove the rogue database options (_pre_user_id, theme_dc_aluma_tools, theme_aluma_times).
  8. Delete the bot and admlnlx user accounts (after evidence retention period).
  9. Rotate WordPress salt keys in wp-config.php (wp config shuffle-salts).
  10. Scan the site a second time with an independent tool (Wordfence, MalCare, or Sucuri) to catch anything missed.

Phase 3 — Email reputation recovery (this week)

  1. Move DMARC from p=none to p=quarantine, then p=reject after a monitoring period.
  2. Verify DKIM is correctly configured for Microsoft 365.
  3. Request removal from email blocklists that flagged the domain (MxToolbox, Spamhaus, Barracuda, etc.).
  4. Contact AECOM's IT team with a summary of this report and request that their filters re-evaluate mail from @kmaofny.com after a stabilization window.

Phase 4 — Hardening (next two weeks)

  1. Install a maintained security plugin (Wordfence or iThemes Security).
  2. Enforce two-factor authentication on all WordPress admin accounts.
  3. Implement a login-attempt rate limit on wp-login.php (currently unlimited).
  4. Restrict access to /wp-admin/ and /wp-login.php by IP or VPN where feasible, or protect with HTTP basic auth / Flywheel access control.
  5. Schedule automatic plugin and core updates via Flywheel's managed updates or WP-Cron.
  6. Remove or replace wp-file-manager. If its functionality is genuinely needed, use a maintained alternative.
  7. Audit all other G&M-hosted client sites for the same plugin and the same attack pattern. The Grain & Mortar password convention observed on this site (pattern-based passwords per service) should be reviewed across the client portfolio as part of defense-in-depth.

Phase 5 — Documentation & post-incident

  1. Deliver this report to KMA in final form.
  2. Share a sanitized version with AECOM's security contact.
  3. Provide KMA with an updated 1Password vault entry for all new credentials.
  4. File an updated Masterdoc reflecting the new passwords and new security practices.

Open Questions Pending Review

  1. Did the attacker exfiltrate any wp_rg_lead* Gravity Forms submission data? (DB review pending.)
  2. Was the Microsoft 365 admin account (admin@kmaofny.com or similar) actually logged into by the attacker using the reset password? (Microsoft 365 sign-in log review required — KMA IT vendor needed.)
  3. Did the attacker upload any content via the REST API application token? (Post and media creation log review via WP-CLI pending.)
  4. How many inbound emails were bounced or rejected by AECOM during the outage window? (KMA side — check sent-item bounces.)

Evidence Retention

All malicious files and database snapshots have been preserved at: ~/.claude/project-notes/km-associates/forensic-workspace/

This archive contains: - 6 web shells from the site root - 4 malicious plugin folders (widget-1776587769, widget-1776587767, one_images_user, and the fileview.php stray) - Clean local theme snapshot from June 2025 - This report and supporting project notes

Retention recommendation: keep this evidence for a minimum of 12 months in case of follow-on legal or insurance review.


Prepared by

Grain & Mortar Eric Downs, Technical Director eric@grainandmortar.com Investigation led on 2026-04-22


This document will be revised as the remediation phases complete. Version 1 — forensic findings as of 2026-04-22.