SuiteCRM SQL Injection: Breaking an XSS-Oriented Input Sanitization Layer
A Code Review and Vulnerability Research Journey
As part of my security code-review practice, I wanted to analyze a real-world application with a relatively large attack surface, multiple user roles, and a history of publicly disclosed vulnerabilities.
I chose SuiteCRM because it is a mature, open-source CRM platform with a large PHP codebase and multiple modules, controllers, and user-controlled input paths.
During the review, I focused initially on SQL injection and, more specifically, on how SuiteCRM sanitizes user-controlled input before it reaches database queries.
What started as a straightforward review of SQL query construction eventually led me to an interesting input-validation issue involving an XSS-oriented sanitization layer.
SuiteCRM follows an MVC-oriented architecture. For the initial review, I focused on controllers and model/database interactions because these components provide useful entry points for tracing user-controlled input into SQL queries.
At first glance, several SQL query construction points appeared potentially interesting because user-controlled parameters seemed to reach query-building code.
But actually not. I found the application passes any user input firstly into a utils file,
user input → utils → antiXSS → SQL
So this file contains multiple layers of filtration and sanitization, so I found the SQLi preventing using two main functions.At this stage, I did not identify an obvious bypass through the first validation layer, and the second one is interesting for me because in this function, it uses an XSS prevention library called antiXSS (https://github.com/voku/anti-xss).
So how does an XSS prevention library prevent SQLi?
I’m digging into this function because it’s very interesting to me then i found the function it’s seems be secure
From this point i started the test
I first established a baseline by submitting a benign value (sad) and observing how it propagated through the application and into the generated query.
I then introduced a single quote (') as a minimal SQL metacharacter test. The application transformed or rejected the character, indicating that the input was being processed by the sanitization layer before reaching the query construction logic.
I then introduced a single quote (') as a minimal SQL metacharacter test.
The same behavior was observed with other quote characters.
At this point, the initial filtering mechanism appeared to be working as expected.
Get Ahmed Rabeaa Mosaa (SadC0d3r)’s stories in your inbox
Join Medium for free to get updates from this writer.
However, this did not prove that the complete input-handling mechanism was safe.
I extracted the validation code into sperated file to test it
<?php
define('sugarEntry', true);
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
require_once __DIR__ . '/../php_version.php';
require_once __DIR__ . '/../include/SugarObjects/SugarConfig.php';
require_once __DIR__ . '/../include/utils/security_utils.php';
require_once __DIR__ . '/../vendor/autoload.php';
use voku\helper\AntiXSS;
function securexss($uncleanString)
{
if (is_array($uncleanString)) {
$new = [];
foreach ($uncleanString as $key => $val) {
$new[$key] = securexss($val);
}
return $new;
}
static $xss_cleanup = [
'"' => '&',
'"' => '"',
"'" => ''',
'<' => '<',
'>' => '>',
'`' => '`'
];
$uncleanString = preg_replace(array('/javascript:/i', '/\0/', '/javascript:/i'),
array('java script:', '', 'java script:'), (string) $uncleanString);
$partialString = str_replace(array_keys($xss_cleanup), $xss_cleanup, $uncleanString);
$antiXss = new AntiXSS();
$antiXss->removeEvilAttributes(['style', 'onerror']);
return $antiXss->xss_clean($partialString);
}
echo securexss("sad") . '
';And i read the library code from github so i noted that
One behavior caught my attention when the input contained the < character.
The input was being processed according to HTML/XSS-oriented sanitization rules. As a result, the < character could be transformed while other parts of the input remained unchanged.
I then tested whether an SQL expression could survive this transformation while incorporating a character sequence that triggered the HTML-oriented processing.
So if i put a regular SQLi payload and tied with “ — < — “ the payload will bypass normally and the “< “ will be encoded only.
When the SQLi payload contain — < — the antixss lib decod all html encoded then encode < only
The root cause was not simply insufficient character filtering. The deeper issue was the use of an XSS-oriented sanitization routine as part of an SQL input protection mechanism.
XSS and SQL injection are different classes of vulnerabilities and require context-specific defenses. Transformations that are useful for HTML output cannot be treated as a substitute for parameterized SQL queries or context-aware database escaping.
By this bypass i greped all functions use this prevention techniques and i got more than 9 SQLi and there are the CVEs and waiting 5 to assign a CVE number
And the funny things this function is secure without using antiXSS library
Sometimes, adding more security layers does not make an application more secure. When those layers are used outside their intended context, they can create new attack paths instead.
وفي الاخر حابب اقول
سُبْحَانَكَ لَا عِلْمَ لَنَا إِلَّا مَا عَلَّمْتَنَا إِنَّكَ أَنْتَ الْعَلِيمُ الْحَكِيمُ










