← All audits

Audit #40 Suspicious

Kirki – Freeform Page Builder, Website Builder & Customizer · 500k+ installs · baseline 5.2.3 → head 6.0.6 · closed 27d ago

Actor: Themeum (took over the kirki slug 2023-05-18, replaced the original Customizer Framework with the Droip page builder rebrand at v6.0.0 on 2026-04-29)
Show full summary

Verdict: SUSPICIOUS. The legitimate Kirki Customizer Framework (Aristeides Stathopoulos, 2014–2023, 500,000 active installs, used by hundreds of WordPress themes as a dependency) was effectively replaced at v6.0.0 (released 2026-04-29) with a rebrand of Droip — an unrelated no-code page builder by Themeum, the company that received SVN commit access on 2023-05-18 via the literal commit "Contributor updated to Themeum".

The plugin header itself documents the substitution: the description changed from "The Ultimate WordPress Customizer Framework" to "an all-in-one no-code builder that empowers users to build professional-grade WordPress sites without writing any code," and the bootstrap code now explicitly guards if ( ! class_exists('KirkiProMain') && ! class_exists('KirkiMain') && ! class_exists('Droip') ) — confirming it ships as a downstream sibling of Themeum's Droip product. The MIT LICENSE file was deleted in the same release. Within days, the wp.org forum filled with reports from sites running themes like XStore, Lexend, Shoptimizer, Buddy X, and Gwangi — all broken with PHP fatal errors after auto-updating from kirki 5.x to 6.x.

The replacement code introduces a ComponentLibrary/controller/CompLibFormHandler.php REST surface with 6 routes (kirki-login, kirki-register, kirki-forgot-password, kirki-change-password, kirki-retrieve-username, kirki-comment) all wired to a permission_callback that unconditionally returns true. The session-bound nonce these routes verify is mintable by any anonymous visitor via wp_create_nonce in the unauth context — ElementGenerator::add_nonce_to_element() documents this with the comment "Always returns consistent nonce for same user + action for ~12 hours." Empirical testing on a controlled local site (poc-kirki.localhost) confirmed: unauth POST → auto-approved comment insertion succeeds (comment_approved=1, bypassing moderation); unauth registration with arbitrary user_meta injection succeeds (limited blast — meta is force-prefixed); the documented "phishing-vector password reset" + "arbitrary comment edit" primitives fail with PHP TypeErrors because the handler logic is structurally broken. Production error_reporting(E_ALL) + ini_set('display_errors', 1) calls in Ajax/Apps.php cause those PHP errors to render a Whoops stack trace with full filesystem paths back to anonymous callers (FPD-class info disclosure on every error). A download_zip_from_remote() helper at HelperFunctions.php:3801 explicitly disables SSL verification (verify_peer=false, verify_peer_name=false) for ZIP extraction into wp-content/, gated by a KIRKI_USERS_DEFAULT_FULL_ACCESS = array('administrator', 'editor') config that grants Editor-level users full plugin access.

This is not malware in the exfiltration-to-attacker sense — no third-party C2, no time-bombs, no obfuscated payloads. But the cumulative pattern — supply-chain takeover of a critical-mass framework, hostile replacement breaking the dependents, broken security primitives + structural defects in the replacement code, FPD via production error reporting, LICENSE file removed — warrants a public SUSPICIOUS verdict for site admins evaluating whether to keep this plugin installed.

⚠️
Pattern detected — pending vendor response or further evidence.

Not yet confirmed malicious. Site owners should treat with caution; plugin author should review the cleanup steps.

If you run kirki on your site

Verify your install matches the wp.org canonical version:

wp plugin verify-checksums kirki

A patched build isn't yet published for this audit. Check the security advisories index or remove the plugin until one is available.

If you're the plugin author

## For site admins running kirki

Your site is not compromised. This is a supply-chain takeover with structural-defect concerns, not active malware. There is no confirmed exfiltration of data to attacker infrastructure, no time-bomb, and no hidden persistence in the codebase.

However, this plugin has fundamentally changed what it does. If you installed it as a Customizer Framework (the original purpose, used by many WordPress themes as a dependency), the v6.0.0 update has likely broken your site or theme integration.

### Decision tree

Was kirki installed as a dependency by your theme?

| Theme | Status | |---|---| | XStore, Lexend, Shoptimizer, Buddy X, Gwangi | Confirmed broken by v6.x update (forum reports) | | Any theme listed on wp.org as requiring "Kirki Customizer Framework" | Likely affected |

Roll back to v5.2.3. Use a tool like WP-Rollback or manual SVN checkout from https://plugins.svn.wordpress.org/kirki/tags/5.2.3/. Pin the version to prevent auto-update. Watch for your theme vendor to either provide a patch or stop depending on kirki.

Did you install kirki directly to use the no-code page builder (Droip)?

The codebase is bug-ridden as of v6.0.6. Multiple unauth REST endpoints exist with permission_callback returning true. Most exploit chains are PHP-broken in the current release, but new fixes from the vendor may inadvertently complete them. Specific concerns:

  • Anonymous visitors can post auto-approved comments on any post (comment_approved=1 bypassing moderation) — comment spam vector
  • Anonymous visitors can enumerate users by email + DoS their password-reset cycle
  • Production error_reporting(E_ALL) + display_errors=1 causes any PHP error to render a Whoops stack trace with full filesystem paths back to anonymous callers
  • Editors (not just admins) are granted FULL_ACCESS capability, including the ability to upload remote ZIP files with SSL verification disabled

If you must use this plugin: disable comments on any public posts/pages, set WP_DEBUG_DISPLAY=false in wp-config.php to suppress the Whoops handler, and restrict Editor accounts to trusted users only.

Are you not actively using kirki at all?

Deactivate and delete. A 500K-install plugin sitting inactive that has changed identity and gained a bug-ridden REST surface is unnecessary attack surface.

### How to verify your situation

```bash # 1. Check installed version wp plugin get kirki --field=version

# 2. Check if your active theme depends on kirki grep -ril "kirki" wp-content/themes/$(wp option get template)/

# 3. Check for unauth-reachable REST endpoints (kirki 6.x adds these) curl -sI "https://your-site.example/wp-json/KirkiComponentLibrary/v1/kirki-comment" \ -X OPTIONS \ | head -5 # A 200 response confirms the unauth REST surface is exposed ```

### Reporting / further action

This plugin has not been closed by wp.org as of this audit. Site admins who believe wp.org should review the slug-takeover behavior can report via the wp.org plugin team: plugins@wordpress.org with subject "Kirki slug takeover concern" and link to this audit.

The plugin's actual security findings have been recorded against WP Registry audit IDs 705 and 783 (findings 45195–45198). Cross-reference at https://findings.wpregistry.io/lookup/kirki?type=plugin.

### Historical context

The original Kirki Customizer Framework by Aristeides Stathopoulos was a beloved, well-maintained library used by many WordPress themes for over a decade. The ownership transitions (aristath → davidvongries → themeum, with themeum's first commit on 2023-05-18) were not unusual on their own — open-source maintainership changes hands. The concern is the v6.0.0 release in April 2026 that replaced the framework with an unrelated product while keeping the slug, name, and 500,000-site distribution channel.

The original framework's API is still partially present in the v6.x codebase (under customizer/), but the new top-level entry point and the Droip-rebranded UI mean themes that depend on the legacy API may or may not work depending on which code paths they hit.

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

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

Tutor LMS – eLearning and online course solution — clean code, same SVN account (latent risk)
100k+
Tutor LMS Elementor Addons — clean code, same SVN account (latent risk)
30k+
TutorMate — clean code, same SVN account (latent risk)
10k+
WP Mega Menu — clean code, same SVN account (latent risk)
9k+
Qubely – Advanced Gutenberg Blocks — clean code, same SVN account (latent risk)
8k+
WP Crowdfunding — clean code, same SVN account (latent risk)
3k+
Certificate customizer for Tutor LMS — clean code, same SVN account (latent risk)
1k+
Tutor LMS BunnyNet Integration — clean code, same SVN account (latent risk)
1k+
Tutor LMS Divi Modules — clean code, same SVN account (latent risk)
1k+
Tutor LMS – Migration Tool — clean code, same SVN account (latent risk)
1k+
Oxygen Tutor LMS — clean code, same SVN account (latent risk)
300
Growfund – Ultimate Donation & Crowdfunding Solution — clean code, same SVN account (latent risk)
70
WP Page Builder — closed by wp.org
WP Support Desk — closed by wp.org

Plugin version history

Every release on wp.org for this plugin, color-coded by relationship to the incident. The compromise window shows where the wp.org Plugin Review Team deleted the malicious tags from SVN — those versions cannot be re-downloaded today.

  1. Clean 30 earlier releases before the incident
    • 3.0.40
    • 3.0.41
    • 3.0.42
    • 3.0.43
    • 3.0.44
    • 3.0.45
    • 3.1.0
    • 3.1.1
    • 3.1.2
    • 3.1.3
    • 3.1.4
    • 3.1.5
    • 3.1.6
    • 3.1.7
    • 3.1.8
    • 3.1.9
    • 4.0.19
    • 4.0.20
    • 4.0.21
    • 4.0.22
    • 4.0.23
    • 4.0.24
    • 4.1
    • 4.2.0
    • 5.0.0
    • 5.1.0
    • 5.1.1
    • 5.2.0
    • 5.2.1
    • 5.2.2
  2. 5.2.3 Last clean Last clean release before incident
  3. 🛑 Compromise window 19 days · 2026-04-10 → 2026-04-29

    Malicious releases pushed during this window were deleted from SVN by the wp.org Plugin Review Team. Last malicious tag: 6.0.6.

  4. 6.0.0 PRT cleanup PRT cleanup release — incident closed
  5. 6.0.1 Clean Clean (post-cleanup)
  6. 6.0.2 Clean Clean (post-cleanup)
  7. 6.0.3 Clean Clean (post-cleanup)
  8. 6.0.4 Clean Clean (post-cleanup)
  9. 6.0.5 Clean Clean (post-cleanup)
  10. 6.0.6 Clean Clean (post-cleanup)
  11. 6.0.7 Clean Clean (post-cleanup)
  12. 6.0.8 Clean Clean (post-cleanup)
  13. 6.0.9 Current Current release

Timeline

DateEvent
2014-05-27Kirki Customizer Framework added to wp.org by Aristeides Stathopoulos (aristath)
2014–2023aristath is primary committer (154 commits). Plugin grows to ~500,000 active installs, used as Customizer-framework dependency by hundreds of WordPress themes
2020-04-23 → 2023-02-08davidvongries (David von Gries) joins as secondary maintainer (32 commits)
2023-05-18Ownership transition. Commit r2914504 by new committer themeum with message "Contributor updated to Themeum". Themeum becomes the sole active maintainer
2023-06-13 → 2026-04-10Themeum-era maintenance: v4.2.0 → v5.2.3. Cadence consistent with traditional Customizer-framework patches
2026-04-29v6.0.0 released — the rebrand. Plugin Name changes from "Kirki Customizer Framework" to "Kirki"; description changes to "all-in-one no-code builder"; 116 new files added, 7 deleted (incl. LICENSE), 51 modified
2026-04-29 → 2026-05-08v6.0.1 → v6.0.5 — five rapid hotfix releases in 10 days as themes break across the install base
2026-04-29 → 2026-04-30Forum complaints flood in: "Why have you ruined such a great plugin?", "XStore Sites Broken by Kirki 6.x Update", "Website broken after update to v6", "Site broken after update to v6" (12 replies), "Disable Builder" (10 replies), plus reports against Lexend, Shoptimizer, Buddy X, and Gwangi themes
2026-05-15Current release v6.0.6 (audited here)

Committer history (hijack-indicator data)

CommitterFirst seenRoleCommits
plugin-master2014-05-27wp.org plugin-team bot1
aristath (Aristeides Stathopoulos)2014-05-27Original author154
deployer (deployer.seravo.com)2016-03-20Benign helper (resolved as benign_company_employee in events table)2
davidvongries (David von Gries)2020-04-23Second-era maintainer (transitional)32
themeum (Themeum)2023-05-18Current owner — initial commit literally titled "Contributor updated to Themeum"24+

Rebrand boundary diff (v5.2.3 → v6.0.0)

git diff --shortstat v5.2.3 v6.0.0
 174 files changed, 35578 insertions(+), 1189 deletions(-)

File-status breakdown: 116 added, 7 deleted, 51 modified.

Plugin header — the smoking gun

- Plugin Name: Kirki Customizer Framework
- Plugin URI: https://www.themeum.com
- Description: The Ultimate WordPress Customizer Framework
- Author: Themeum
- Copyright (c) 2023, Themeum
- License: https://opensource.org/licenses/MIT
+ Plugin Name: Kirki
+ Plugin URI: https://kirki.com
+ Description: Kirki is an all-in-one no-code builder that empowers users to build
+              professional-grade WordPress sites without writing any code.
+ Author: Kirki
+ Author URI: https://kirki.com

Bootstrap guard explicitly references Droip

// In kirki.php after the v6.0.0 rebrand:
if ( ! class_exists( 'KirkiProMain' ) && ! class_exists( 'KirkiMain' ) && ! class_exists( 'Droip' ) ) {
    require_once __DIR__ . '/vendor/autoload.php';
    require_once __DIR__ . '/customizer/class-customizer.php';
}

The Droip class check confirms the new code is the Droip page builder rebranded under the kirki slug.

Notable deletions in v6.0.0

FileSignificance
LICENSEMIT license file removed (no replacement)
assets/images/kirki-logo.jpgOriginal framework branding
customizer/controls.js, customizer/preview.jsCore Customizer integration scripts that themes depend on
customizer/packages/controls/tabs/edd/EDD_SL_Plugin_Updater.phpLegacy EDD updater (this was the source of the prior code_pattern event that resolved as fp_edd_updater_library)
customizer/lib/upgrade-notifications.phpLegacy upgrade-notification module

Notable additions in v6.0.0 (the Droip codebase)

FileWhat it adds
ComponentLibrary/controller/CompLibFormHandler.php6 unauth REST routes for login / register / forgot-password / change-password / retrieve-username / comment, all with permission_callback returning true
ComponentLibrary/controller/ElementGenerator.phpFrontend element rendering — add_nonce_to_element() mints unauth nonces for the REST surface
ComponentLibrary/controller/ShowUserMetadata.phpNew user-meta display surface
ComponentLibrary/index.phpdefine('KIRKI_COMPONENT_LIBRARY_APP_PREFIX', 'KirkiComponentLibrary')
config.phpTop-level config: KIRKI_USERS_DEFAULT_FULL_ACCESS = array('administrator', 'editor')
assets/fonts/Inter-*.woff (4 weights)Droip's UI font family
assets/js/kirki-editor.min.js, assets/js/kirki.min.jsDroip's editor + frontend JS

Forgejo comparison: v5.2.3 vs v6.0.0

Hijack-indicator matrix

IndicatorResult
Sole committer for ≥2 years?No — three-stage ownership progression (aristath 2014–2023 → davidvongries 2020–2023 → themeum 2023→)
Sudden new committer event?Yes — themeum's first commit on 2023-05-18 is literally titled "Contributor updated to Themeum", signaling a deliberate ownership transition
Author profile / plugin-purpose drift?Yes — author transitioned from individual contributor → corporate vendor; plugin purpose entirely changed at v6.0.0 (Customizer Framework → no-code page builder)
Code-level malware patterns?Structural defects, not active backdoors — broken unauth REST exploits (PHP TypeErrors halt the chain), production error_reporting causing Whoops/FPD, FULL_ACCESS gate grants Editor full plugin access by default, download_zip_from_remote() with verify_peer=false. No confirmed exfiltration / C2 / time-bomb
Outbound C2 / known bad domains?Telemetry to vendor-controlled kirki.com only (no third-party C2). LICENSE file removed in 6.0.0 (compliance signal)
New SVN credentials before mass-impact release?The themeum credentials were added 2023-05-18; the rebrand release shipped 3 years later (2026-04-29). Not a fresh-credential takeover — Themeum had legitimate maintainership and used it to ship the rebrand
Cross-cutting ecosystem damage?Yes — forum reports confirm at least 5 major themes (XStore, Lexend, Shoptimizer, Buddy X, Gwangi) broke with PHP fatal errors when sites auto-updated from kirki 5.x to 6.x

Empirically verified security findings on v6.0.6

WP Registry findingAudit-rated severityEmpirical reality
45195 — "Unauthenticated arbitrary email with valid password-reset link (phishing vector)"highBroken in v6.0.6. Handler reaches get_password_reset_key() but nl2br(array) TypeError halts before wp_mail fires. User-enum + reset-key DoS work as side effects. Severity downgraded to low
45196 (INSERT path) — "Unauthenticated auto-approved comment insertion bypassing moderation"mediumConfirmed working. Unauth POST to /wp-json/KirkiComponentLibrary/v1/kirki-comment with anon nonce inserts wp_comments row with comment_approved=1. CVSS ~5.3 (below Patchstack 6.5 floor). sanitize_text_field() prevents XSS lift
45196 (EDIT path) — "edits arbitrary comment IDs"mediumBroken in v6.0.6. Handler line 126 references undefined $comment_id (should be $existing_comment_id). Whoops TypeError before wpdb->update fires
45197 — "REST routes use return-true permission_callback for builder collection rendering"lowConfirmed. get_item_permissions_check() returns true for all 6 REST routes (login/register/comment/forgot/change/retrieve). Structural defect — downstream impact depends on individual handler completeness
45198 — "Unauthenticated registration creates user-meta from attacker-named keys"lowWorks but limited blast radius. All injected meta gets force-prefixed with KirkiComponentLibrary_. Attempted privesc via wp_capabilities injection results in KirkiComponentLibrary_wp_capabilities meta key only — actual role stays subscriber. No privesc

Audit-749 (v6.0.4) additionally surfaced 14 findings (2C, 7H, 3M, 2L) including download_zip_from_remote() SSRF→RCE chain, SQL injection via bx_get_media, object injection via maybe_unserialize on attacker-supplied template data. The vulnerable code paths still exist in v6.0.6 source but are gated by FULL_ACCESS — which by default config grants the capability to administrators and editors only.

Comparable cases

  • scroll-top (Beacon audit ID 12) — Plugin Update Checker library hijack by Benjamin actor; classic new-committer-replaces-update-channel pattern. Kirki differs in that the takeover happened years earlier (2023-05) and the malicious change is a product-substitution rather than an update-channel hijack, but the supply-chain shape is the same.
  • siteguarding (Beacon audit ID 28) — multi-plugin portfolio takeover with backdoor. Kirki is single-plugin but the install-base impact (500K) and the dependent-theme blast radius are comparable.
  • greenshift (Beacon audit ID 29, benign closed_by_wporg) — closure-explainer for guideline violations. Kirki could similarly land at benign-guideline if the takeover were just a rebrand without security defects; the cumulative pattern here lifts to suspicious.

What would lift the verdict to MALICIOUS

  • Confirmed working exploit chain on a Customer/Subscriber/Unauth actor (the bugs as documented are structurally broken in v6.0.6 due to PHP TypeErrors)
  • Outbound HTTP request to non-vendor-controlled infrastructure with exfiltrated data
  • Hidden persistence (cron-jobs, options-table backdoors) — none found in source review
  • Hardcoded credentials or magic-header authentication backdoors — none found

The codebase is incomplete and bug-ridden rather than actively malicious.