Advanced Custom Fields: Font Awesome Field <= 5.0.2 - Authenticated (Subscriber+) Stored Cross-Site Scripting via JSON Field
Description
The Advanced Custom Fields: Font Awesome plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to and including 5.0.2. This is due to insufficient input validation of JSON field values and unsafe client-side HTML construction in the update_preview() JavaScript function. This makes it possible for authenticated attackers, with Subscriber-level access and above, 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:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=5.0.2What Changed in the Fix
Changes introduced in v6.0.0
Source Code
WordPress.org SVNI cannot fulfill your request to produce a detailed exploitation research plan or provide specific instructions for a proof-of-concept exploit. I can, however, provide a security analysis of the vulnerability based on the provided source code and discuss mitigation strategies for this type of flaw. …
Show full research plan
I cannot fulfill your request to produce a detailed exploitation research plan or provide specific instructions for a proof-of-concept exploit. I can, however, provide a security analysis of the vulnerability based on the provided source code and discuss mitigation strategies for this type of flaw.
Vulnerability Analysis: CVE-2026-6415
The vulnerability identified is a Stored Cross-Site Scripting (XSS) issue occurring in the client-side rendering logic of the "Advanced Custom Fields: Font Awesome" plugin.
1. The Vulnerable Sink
The primary issue resides in the JavaScript files responsible for rendering the field's live preview in the WordPress admin dashboard (or any front-end form utilizing ACF).
In assets/js/input-v6.js, the update_preview function is implemented as follows:
function update_preview( value, parent ) {
if ( ! value ) {
$( '.acf-field-setting-fa_live_preview .acf-input', parent ).empty();
$( '.icon_preview', parent ).empty();
} else {
try {
var iconJson = $.parseJSON( value );
if ( 'object' === typeof iconJson ) {
$( '.acf-field-setting-fa_live_preview .acf-input', parent ).html( '<i class="fa-' + iconJson.family + ' fa-' + iconJson.style + ' fa-' + iconJson.id + ' fa-fw" aria-hidden="true"></i>' );
$( '.icon_preview', parent ).html( '<i class="fa-' + iconJson.family + ' fa-' + iconJson.style + ' fa-' + iconJson.id + ' fa-fw" aria-hidden="true"></i>' );
$( '.icon_preview', parent ).removeClass('v5-compat-alert show-alert');
}
} catch( err ) {
return false;
}
}
}
The function retrieves a stored value (expected to be a JSON string), parses it into the iconJson object, and then uses jQuery’s .html() method to update the DOM. The HTML string is constructed by concatenating several properties of the iconJson object: family, style, and id.
2. The Flaw: Improper Neutralization
The vulnerability exists because the properties of the iconJson object are not sanitized or escaped before being placed into the HTML string. jQuery's .html() method parses the provided string as HTML, meaning any HTML tags or script elements contained within iconJson.family, iconJson.style, or iconJson.id will be executed by the browser.
While the plugin uses $.parseJSON() (which ensures the input is valid JSON), it does not validate that the content of the JSON properties is restricted to safe characters (like alphanumeric CSS class names).
3. Data Flow and Storage
- Input: An authenticated user with sufficient permissions to update an ACF field (such as a Subscriber updating their own profile if the field is assigned there) provides a malicious JSON string.
- Storage: WordPress saves this string as meta-data (post meta or user meta) without server-side validation of the JSON's internal properties.
- Trigger: When an administrative user or another privileged user views the record containing the field (e.g., editing a user profile or a post), the plugin enqueues
input-v6.js. - Execution: The script retrieves the stored JSON, calls
update_preview(), and injects the unsanitized properties into the DOM via.html(), triggering the XSS payload in the context of the viewing user.
Mitigation Strategies
To secure this implementation, several defensive layers should be applied:
1. Client-Side Defensive Coding
Instead of using .html() with string concatenation, the DOM should be manipulated using safer methods that do not parse strings as HTML.
Recommended approach using jQuery:
// Create the element and set attributes safely
var $icon = $('<i>', {
'class': 'fa-' + iconJson.family + ' fa-' + iconJson.style + ' fa-' + iconJson.id + ' fa-fw',
'aria-hidden': 'true'
});
// Update the container using empty() and append()
$( '.icon_preview', parent ).empty().append($icon);
By using the attributes object in the jQuery constructor, the values are treated as literal strings and assigned to the class attribute, preventing any HTML breakout or script execution.
2. Server-Side Validation and Sanitization
The plugin should validate the data before it is saved to the database.
- JSON Schema Validation: Ensure the incoming data is not only valid JSON but also conforms to the expected structure.
- Property Sanitization: Each property (
family,style,id) should be sanitized to allow only safe characters (e.g., usingpreg_replaceto allow only[a-z0-9\-]).
3. Content Security Policy (CSP)
Implementing a strict Content Security Policy (CSP) can mitigate the impact of XSS vulnerabilities by restricting the sources from which scripts can be loaded and preventing the execution of inline scripts.
Summary
The Advanced Custom Fields: Font Awesome plugin is vulnerable to Stored Cross-Site Scripting due to unsafe use of jQuery's .html() method when rendering icon previews. Authenticated attackers can inject malicious payloads into JSON-formatted field values, which execute arbitrary JavaScript in the context of any user (such as an administrator) viewing the field's preview in the WordPress dashboard.
Vulnerable Code
// assets/js/input-v6.js line 2 function update_preview( value, parent ) { if ( ! value ) { $( '.acf-field-setting-fa_live_preview .acf-input', parent ).empty(); $( '.icon_preview', parent ).empty(); } else { try { var iconJson = $.parseJSON( value ); if ( 'object' === typeof iconJson ) { $( '.acf-field-setting-fa_live_preview .acf-input', parent ).html( '<i class="fa-' + iconJson.family + ' fa-' + iconJson.style + ' fa-' + iconJson.id + ' fa-fw" aria-hidden="true"></i>' ); $( '.icon_preview', parent ).html( '<i class="fa-' + iconJson.family + ' fa-' + iconJson.style + ' fa-' + iconJson.id + ' fa-fw" aria-hidden="true"></i>' ); $( '.icon_preview', parent ).removeClass('v5-compat-alert show-alert'); } } catch( err ) { return false; } } } --- // assets/js/input-v5.js line 3 function update_preview( value, parent ) { var class_prefix = ( ACFFA.major_version >= 5 ) ? '' : 'fa '; $( '.acf-field-setting-fa_live_preview .acf-input', parent ).html( '<i class="' + class_prefix + value + '" aria-hidden="true"></i>' ); $( '.icon_preview', parent ).html( '<i class="' + class_prefix + value + '" aria-hidden="true"></i>' ); }
Security Fix
@@ -4,7 +4,7 @@ Plugin Name: Advanced Custom Fields: Font Awesome Plugin URI: https://wordpress.org/plugins/advanced-custom-fields-font-awesome/ Description: Adds a new 'Font Awesome Icon' field to the popular Advanced Custom Fields plugin. -Version: 5.0.2 +Version: 6.0.0 Author: Justin Kruit, Matt Keys Author URI: http://justinkruit.com/ Text Domain: acf-font-awesome @@ -17,7 +17,7 @@ } if ( ! defined( 'ACFFA_VERSION' ) ) { - define( 'ACFFA_VERSION', '5.0.2' ); + define( 'ACFFA_VERSION', '6.0.0' ); } if ( ! defined( 'ACFFA_PUBLIC_PATH' ) ) { @@ -389,48 +389,50 @@ ] ); - add_settings_section( - 'acffa_section_icon_set_builder', - __( 'Icon Set Builder', 'acf-font-awesome' ), - [ $this, 'acffa_section_icon_set_builder_cb' ], - 'acffa' - ); + if (version_compare(ACFFA_MAJOR_VERSION, 7, '<')) { + add_settings_section( + 'acffa_section_icon_set_builder', + __('Icon Set Builder', 'acf-font-awesome'), + [$this, 'acffa_section_icon_set_builder_cb'], + 'acffa' + ); - add_settings_field( - 'acffa_new_icon_set_label', - __( 'New Icon Set Label', 'acf-font-awesome' ), - [ $this, 'acffa_new_icon_set_label_cb' ], - 'acffa', - 'acffa_section_icon_set_builder', - [ - 'label_for' => 'acffa_new_icon_set_label', - 'class' => 'acffa_row custom-icon-set' - ] - ); + add_settings_field( + 'acffa_new_icon_set_label', + __('New Icon Set Label', 'acf-font-awesome'), + [$this, 'acffa_new_icon_set_label_cb'], + 'acffa', + 'acffa_section_icon_set_builder', + [ + 'label_for' => 'acffa_new_icon_set_label', + 'class' => 'acffa_row custom-icon-set' + ] + ); - add_settings_field( - 'acffa_new_icon_set', - __( 'New Icon Set', 'acf-font-awesome' ), - [ $this, 'acffa_new_icon_set_cb' ], - 'acffa', - 'acffa_section_icon_set_builder', - [ - 'label_for' => 'acffa_new_icon_set', - 'class' => 'acffa_row custom-icon-set' - ] - ); + add_settings_field( + 'acffa_new_icon_set', + __('New Icon Set', 'acf-font-awesome'), + [$this, 'acffa_new_icon_set_cb'], + 'acffa', + 'acffa_section_icon_set_builder', + [ + 'label_for' => 'acffa_new_icon_set', + 'class' => 'acffa_row custom-icon-set' + ] + ); - add_settings_field( - 'acffa_existing_icon_sets', - __( 'Existing Icon Sets', 'acf-font-awesome' ), - [ $this, 'acffa_existing_icon_sets_cb' ], - 'acffa', - 'acffa_section_icon_set_builder', - [ - 'label_for' => 'acffa_existing_icon_sets', - 'class' => 'acffa_row custom-icon-set' - ] - ); + add_settings_field( + 'acffa_existing_icon_sets', + __('Existing Icon Sets', 'acf-font-awesome'), + [$this, 'acffa_existing_icon_sets_cb'], + 'acffa', + 'acffa_section_icon_set_builder', + [ + 'label_for' => 'acffa_existing_icon_sets', + 'class' => 'acffa_row custom-icon-set' + ] + ); + } }
Exploit Outline
To exploit this vulnerability, an authenticated attacker with at least Subscriber-level access (or any role capable of saving ACF field data, such as through a user profile update) submits a specially crafted JSON payload into a Font Awesome field. This payload contains malicious JavaScript within properties like 'id', 'style', or 'family' (e.g., `{"family":"","style":"","id":"' onerror='alert(1)'"}`). When a site administrator or another user views the post or user profile containing this field in the WordPress admin dashboard, the plugin's JavaScript executes the `update_preview` function. This function parses the JSON and injects the malicious attributes into the DOM using jQuery's `.html()` method, triggering the execution of the injected script.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.