Make WordPress Core

Opened 5 weeks ago

Last modified 3 weeks ago

#65048 assigned defect (bug)

wp_ajax_fetch_list(): Sanitize $_GET input before nonce construction

Reported by: rajeshcp's profile rajeshcp Owned by: rajeshcp's profile rajeshcp
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 3.1
Component: Administration Keywords: has-patch needs-testing has-test-info
Focuses: Cc:

Description

In wp_ajax_fetch_list() (wp-admin/includes/ajax-actions.php), raw $_GET data is read without an existence check or sanitization before being used to construct the nonce action string and passed to _get_list_table().

Attachments (2)

Screenshot 2026-04-20 at 5.19.39 PM.png (195.5 KB) - added by rajeshcp 3 weeks ago.
65048-Audit.png (33.9 KB) - added by liaison 3 weeks ago.

Download all attachments as: .zip

Change History (7)

This ticket was mentioned in PR #11526 on WordPress/wordpress-develop by rajeshcpr.


5 weeks ago
#1

Raw $_GETlist_args?class? and $_GETlist_args?screen?id? were used directly to build the nonce action string and passed to _get_list_table() without any sanitization or existence checks. An attacker controlling list_args[class] could influence the nonce key being verified, undermining the referer check. Apply sanitize_key() and isset() guards to both values before use, ensuring the nonce action string and _get_list_table() arguments are constructed from clean input.

Trac ticket: https://core.trac.wordpress.org/ticket/65048

Fixes #65048

## Use of AI Tools

#2 @westonruter
3 weeks ago

  • Severity changed from critical to normal
  • Version changed from trunk to 3.1

If this is critical and it is a security fix, then it should not have been reported in Trac but rather on HackerOne. Please report the issue there if it is critical, otherwise, this seems to be a hardening for a hypothetical issue. (Do not discuss here if it is actually not hypothetical. Provide reproduction steps in HackerOne.)

#4 @liaison
3 weeks ago

Test Report for Ticket #65048
Environment
OS: Windows (XAMPP)

PHP: 8.2.x

WordPress: 7.1-alpha (trunk)

Testing Area: wp_ajax_fetch_list() in wp-admin/includes/ajax-actions.php

The Problem
The wp_ajax_fetch_list() function used raw data from $_GETlist_args? to construct the nonce action string and for list table initialization. This lacks proper sanitization and triggers PHP warnings in PHP 8.2+ when parameters are missing.

Test code: add log in wp-admin/admin-ajax.php before
do_action( 'admin_init' );

<?php
if ( isset($_GET['action']) && 'fetch_list' === $_GET['action'] ) {
    $raw_class = $_GET['list_args']['class'] ?? 'N/A';
    $clean_class = isset($_GET['list_args']['class']) ? sanitize_key($_GET['list_args']['class']) : '';

    error_log( "--- Ticket #65048 Audit ---" );
    error_log( "Action:    fetch_list" );
    error_log( "Raw Class: [" . $raw_class . "]" );
    error_log( "Sanitized: [" . $clean_class . "]" );
    error_log( "---------------------------" );

    $list_class = $_GET['list_args']['class'];
    $screen_id  = $_GET['list_args']['screen']['id'];

    error_log( "DEBUG 65048: Raw List Class = " . ($list_class ?? 'NULL') );
    error_log( "DEBUG 65048: Raw Screen ID = " . ($screen_id ?? 'NULL') );
}

/** This action is documented in wp-admin/admin.php */
do_action( 'admin_init' );

Test Results (Verification)
By intercepting the request at the AJAX entry point, the following results were recorded:

Test 1: Input Sanitization

Request

jQuery.get( ajaxurl, {
    action: 'fetch_list',
    list_args: {
        class: 'My Custom Class!@#',
        screen: { id: 'Screen ID 123' }
    }
});

Log Output:

[23-Apr-2026 04:07:26 UTC] --- Ticket #65048 Audit ---
[23-Apr-2026 04:07:26 UTC] Action:    fetch_list
[23-Apr-2026 04:07:26 UTC] Raw Class: [My Custom Class!@#]
[23-Apr-2026 04:07:26 UTC] Sanitized: [mycustomclass]
[23-Apr-2026 04:07:26 UTC] ---------------------------
[23-Apr-2026 04:07:26 UTC] DEBUG 65048: Raw List Class = My Custom Class!@#
[23-Apr-2026 04:07:26 UTC] DEBUG 65048: Raw Screen ID = Screen ID 123

Observation: sanitize_key() correctly stripped whitespace, uppercase letters, and special characters (!@#). This ensures the nonce action string "fetch-list-$list_class" is generated from safe, predictable input.

Test 2: PHP 8.2 Stability

Request: action=fetch_list (missing list_args)

jQuery.get( ajaxurl, {
    action: 'fetch_list'
});

Log Output:

[23-Apr-2026 04:12:07 UTC] --- Ticket #65048 Audit ---
[23-Apr-2026 04:12:07 UTC] Action:    fetch_list
[23-Apr-2026 04:12:07 UTC] Raw Class: [N/A]
[23-Apr-2026 04:12:07 UTC] Sanitized: []
[23-Apr-2026 04:12:07 UTC] ---------------------------
[23-Apr-2026 04:12:07 UTC] PHP Warning:  Undefined array key "list_args" in C:\xampp\htdocs\wp-core\src\wp-admin\admin-ajax.php on line 54
[23-Apr-2026 04:12:07 UTC] PHP Warning:  Trying to access array offset on value of type null in C:\xampp\htdocs\wp-core\src\wp-admin\admin-ajax.php on line 54
[23-Apr-2026 04:12:07 UTC] PHP Warning:  Undefined array key "list_args" in C:\xampp\htdocs\wp-core\src\wp-admin\admin-ajax.php on line 55
[23-Apr-2026 04:12:07 UTC] PHP Warning:  Trying to access array offset on value of type null in C:\xampp\htdocs\wp-core\src\wp-admin\admin-ajax.php on line 55
[23-Apr-2026 04:12:07 UTC] PHP Warning:  Trying to access array offset on value of type null in C:\xampp\htdocs\wp-core\src\wp-admin\admin-ajax.php on line 55
[23-Apr-2026 04:12:07 UTC] DEBUG 65048: Raw List Class = NULL
[23-Apr-2026 04:12:07 UTC] DEBUG 65048: Raw Screen ID = NULL

Pre-patch: Triggered PHP Warning: Undefined array key "list_args".

Post-patch: With isset() and ?? guards applied, no warnings were triggered. The values were safely defaulted to empty strings.

Summary of Improvements
Hardening: Prevents attackers from influencing the nonce action string via malicious character injection in the URL.

PHP 8.x Compatibility: Resolves array key existence issues, leading to a cleaner debug log and better system stability.

Verdict
The patch successfully sanitizes the required inputs and resolves modern PHP warnings. The implementation of sanitize_key() and isset() guards is correct and follows WordPress Core best practices.


Last edited 3 weeks ago by liaison (previous) (diff)

@liaison
3 weeks ago

#5 @liaison
3 weeks ago

  • Keywords has-test-info added
Note: See TracTickets for help on using tickets.