Persistent Cross-Site Scripting (XSS) Vulnerability in WordPress Plugin Quiz And Survey Master
The changelog entry for the latest version of the WordPress plugin Quiz And Survey Master is:
Bug: Fixed recently discovered security issues.
In looking into if that fixed a vulnerability that we should warn customers of our service that use the plugin, we ran the previous version of the plugin through our Plugin Security Checker. That flagged the following code:
While that code was insecure, in our testing it didn’t lead to a vulnerability. It was secured in the new version, though:
195 | <meta property="og:url" content="<?php echo $sharing_page_id . '?result_id=' . esc_attr( $_GET['result_id'] ); ?>" /> |
Looking at the other changes to see if there was a vulnerability fixed, we found vulnerability that still exists in the new version.
In the file /php/classes/class-qmn-quiz-manager.php, the function get_user_ip() gets the IP address of a quiz taker or more accurately, attempts to. The problem with the code, and this a fairly common issue, is that some of the values that are used to determine that are user configurable, so a value other than an IP address can be set to it.
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 | private function get_user_ip() { $ip = __( 'Not collected', 'quiz-master-next' ); $settings = (array) get_option( 'qmn-settings' ); $ip_collection = '0'; if ( isset( $settings['ip_collection'] ) ) { $ip_collection = $settings['ip_collection']; } if ( '1' != $ip_collection ) { if ( $_SERVER['REMOTE_ADDR'] ) { $ip = $_SERVER['REMOTE_ADDR']; } else { $ip = __( 'Unknown', 'quiz-master-next' ); } if ( getenv( 'HTTP_CLIENT_IP' ) ) { $ip = getenv( 'HTTP_CLIENT_IP' ); } elseif ( getenv( 'HTTP_X_FORWARDED_FOR' ) ) { $ip = getenv( 'HTTP_X_FORWARDED_FOR' ); } elseif ( getenv( 'HTTP_X_FORWARDED' ) ) { $ip = getenv( 'HTTP_X_FORWARDED' ); } elseif ( getenv( 'HTTP_FORWARDED_FOR' ) ) { $ip = getenv( 'HTTP_FORWARDED_FOR' ); } elseif ( getenv( 'HTTP_FORWARDED' ) ) { $ip = getenv( 'HTTP_FORWARDED' ); } elseif ( getenv( 'REMOTE_ADDR' ) ) { $ip = getenv( 'REMOTE_ADDR' ); } else { $ip = $_SERVER['REMOTE_ADDR']; } } return $ip; } |
There should be code that validates that the value is an IP address, to prevent that being exploited.
When the value returned from that is used saving a quiz result, no validation or sanitization is done there either:
1170 1171 1172 1173 1174 | public function submit_results( $qmn_quiz_options, $qmn_array_for_variables ) { global $qmn_allowed_visit; $result_display = ''; $qmn_array_for_variables['user_ip'] = $this->get_user_ip(); |
As the proof of concept below shows, the value isn’t escaped when output on the plugin’s Results admin page and can lead to persistent cross-site scripting (XSS). This is a pretty serious, as this type of vulnerability is one that hackers have shown a continued interest in exploiting in WordPress plugins and this plugin has 40,000+ active installations according to wordpress.org.
WordPress Causes Full Disclosure
Because of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.) 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, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.
Proof of Concept
Set the value of the HTTP header “X-FORWARDED-FOR” to:
<script>alert(document.cookie);</script>
Take a quiz.
Now when visiting the plugin’s Results admin page, /wp-admin/admin.php?page=mlw_quiz_results, any available cookies will be shown in an alert box.