Tags:
Details
Affected Software: My Calendar WordPress Plugin
Fixed in Version: >1.7.2
Issue Type: SQL Injection
Original Code: Found Here
This week's bug was a subtle mistake in the usage of an escaping routine. It seems the developer understood the dangers of SQL injection and therefore used an escaping routine to sanitize user controlled input before using that input to build a SQL statement. Unfortunately, the developer overlooked a crucial characteristic and used the wrong escaping routine. Looking at the vulnerable line, we see the following:
$sql = "SELECT * FROM " . WP_CALENDAR_CATEGORIES_TABLE . " WHERE category_id=".mysql_escape_string($_GET['category_id']);
As you can clearly see, the developer chose to utilize the mysql_escape_string() function to escape $_GET[?category_id] before using category_id to build a SQL statement. Looking at the documentation (http://php.net/manual/en/function.mysql-escape-string.php) for mysql_escape_string(), we see that the specific characters escaped are: null byte (0), newline (\n), carriage return (\r), backslash (\), single quote ('), double quote (") and substiture (SUB, or 32). In this case, none of these characters are required in order for SQL injection to be successful. The user controlled $_GET[?category_id'] is not enclosed in quotes, so there is no need to break out of quotes for SQL injection. For example, the attacker can pass the following:
http://path-to-server/calendar.php? category_id=1%20union%20select%201,2,3,4,5,6%20from%20users;
This would result in the following SQL statement:
SELECT * FROM WP_CALENDAR_CATEGORIES_TABLE WHERE category_id=1 union select 1,2,3,4,5,6 from users;
As you can see, the attacker can craft a valid SQL injection without using any of the characters escaped by mysql_escape_string(). The developers addressed this issue by casting the $_GET[?category_id'] to an int before using it in a SQL statement.
If you look closely... you'll see other, unpatched SQL injections with the same symptom littered throughout the code...
Vulnerable Code
...snip... get_results($sql); echo " ".__('Category added successfully','calendar')." "; } else if (isset($_GET['mode']) && isset($_GET['category_id']) && $_GET['mode'] == 'delete') { $sql = "DELETE FROM " . WP_CALENDAR_CATEGORIES_TABLE . " WHERE category_id=".mysql_escape_string($_GET['category_id']); $wpdb->get_results($sql); $sql = "UPDATE " . WP_CALENDAR_TABLE . " SET event_category=1 WHERE event_category=".mysql_escape_string($_GET['category_id']); $wpdb->get_results($sql); echo " ".__('Category deleted successfully','calendar')." "; } else if (isset($_GET['mode']) && isset($_GET['category_id']) && $_GET['mode'] == 'edit' && !isset($_POST['mode'])) { $sql = "SELECT * FROM " . WP_CALENDAR_CATEGORIES_TABLE . " WHERE category_id=".mysql_escape_string($_GET['category_id']); $cur_cat = $wpdb->get_row($sql); ?> <form name="catform" id="catform" class="wrap" method="post" action="/wp-admin/admin.php?page=calendar-categories"> <input type="hidden" name="category_id" value="category_id) ?>"> : <input type="text" name="category_name" class="input" size="30" maxlength="30" value="category_name) ?>"> : <input type="text" name="category_colour" class="input" size="10" maxlength="7" value="category_colour) ?>"> <input type="submit" name="save" class="button bold" value=" »"> get_results($sql); echo " ".__('Category edited successfully','calendar')." "; } $get_mode = 0; $post_mode = 0; if (isset($_GET['mode'])) { if ($_GET['mode'] == 'edit') { $get_mode = 1; } } if (isset($_POST['mode'])) { if ($_POST['mode'] == 'edit') { $post_mode = 1; } } if ($get_mode != 1 || $post_mode == 1) { ?> : : <input type="submit" name="save" class="button bold" value=" »"></form>