CVE-2021-36625: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: An SQL Injection vulnerability exists in Dolibarr ERP/CRM 13.0.2 (fixed version is 14.0.0) via a POST request to the country_id parameter in an UPDATE statement.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 8.8
Published: 2022-03-31T18:15Z
Repository: https://github.com/Dolibarr/dolibarr
Language: PHP
Files changed: htdocs/compta/bank/class/account.class.php, htdocs/compta/sociales/class/cchargesociales.class.php, htdocs/societe/card.php, htdocs/societe/class/societe.class.php
Commit date: 2021-07-05 18:46:27 +0200
Commit message: Fix sql injection
Diff: diff --git a/htdocs/compta/bank/class/account.class.php b/htdocs/compta/bank/class/account.class.php
index 46d1bb9368467..8619ff25daedc 100644
--- a/htdocs/compta/bank/class/account.class.php
+++ b/htdocs/compta/bank/class/account.class.php
@@ -827,12 +827,12 @@ class Account extends CommonObject
 
 		$sql .= ",min_allowed = ".($this->min_allowed != '' ? price2num($this->min_allowed) : "null");
 		$sql .= ",min_desired = ".($this->min_desired != '' ? price2num($this->min_desired) : "null");
 		$sql .= ",comment     = '".$this->db->escape($this->comment)."'";
 
-		$sql .= ",state_id = ".($this->state_id > 0 ? $this->state_id : "null");
-		$sql .= ",fk_pays = ".($this->country_id > 0 ? $this->country_id : "null");
+		$sql .= ",state_id = ".($this->state_id > 0 ? ((int) $this->state_id) : "null");
+		$sql .= ",fk_pays = ".($this->country_id > 0 ? ((int) $this->country_id) : "null");
 		$sql .= ",ics = '".$this->db->escape($this->ics)."'";
 		$sql .= ",ics_transfer = '".$this->db->escape($this->ics_transfer)."'";
 
 		$sql .= " WHERE rowid = ".((int) $this->id);
 
diff --git a/htdocs/compta/sociales/class/cchargesociales.class.php b/htdocs/compta/sociales/class/cchargesociales.class.php
index 8cfadd84f4f77..ee9a270b28383 100644
--- a/htdocs/compta/sociales/class/cchargesociales.class.php
+++ b/htdocs/compta/sociales/class/cchargesociales.class.php
@@ -258,17 +258,17 @@ class Cchargesociales
 		// Put here code to add a control on parameters values
 
 		// Update request
 		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
 		$sql .= ' libelle = '.(isset($this->libelle) ? "'".$this->db->escape($this->libelle)."'" : "null").',';
-		$sql .= ' deductible = '.(isset($this->deductible) ? $this->deductible : "null").',';
-		$sql .= ' active = '.(isset($this->active) ? $this->active : "null").',';
+		$sql .= ' deductible = '.(isset($this->deductible) ? ((int) $this->deductible) : "null").',';
+		$sql .= ' active = '.(isset($this->active) ? ((int) $this->active) : "null").',';
 		$sql .= ' code = '.(isset($this->code) ? "'".$this->db->escape($this->code)."'" : "null").',';
-		$sql .= ' fk_pays = '.(isset($this->fk_pays) ? $this->fk_pays : "null").',';
+		$sql .= ' fk_pays = '.((isset($this->fk_pays) && $this->fk_pays > 0) ? ((int) $this->fk_pays) : "null").',';
 		$sql .= ' module = '.(isset($this->module) ? "'".$this->db->escape($this->module)."'" : "null").',';
 		$sql .= ' accountancy_code = '.(isset($this->accountancy_code) ? "'".$this->db->escape($this->accountancy_code)."'" : "null");
-		$sql .= ' WHERE id='.$this->id;
+		$sql .= ' WHERE id='.((int) $this->id);
 
 		$this->db->begin();
 
 		$resql = $this->db->query($sql);
 		if (!$resql) {
diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php
index e7e8c10d20c33..ba4023dbba94c 100644
--- a/htdocs/societe/card.php
+++ b/htdocs/societe/card.php
@@ -552,11 +552,11 @@ if (empty($reshook)) {
 						$error++; $errors[] = $langs->trans("ErrorBadUrl", $object->webservices_url);
 					}
 				}
 
 				// We set country_id, country_code and country for the selected country
-				$object->country_id = GETPOST('country_id') != '' ?GETPOST('country_id') : $mysoc->country_id;
+				$object->country_id = GETPOST('country_id', 'int') != '' ? GETPOST('country_id', 'int') : $mysoc->country_id;
 				if ($object->country_id) {
 					$tmparray = getCountry($object->country_id, 'all');
 					$object->country_code = $tmparray['code'];
 					$object->country = $tmparray['label'];
 				}
diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php
index a213cca557762..eff2ba39c21fd 100644
--- a/htdocs/societe/class/societe.class.php
+++ b/htdocs/societe/class/societe.class.php
@@ -892,11 +892,11 @@ class Societe extends CommonObject
 			}
 			$sql .= ") VALUES ('".$this->db->escape($this->name)."', '".$this->db->escape($this->name_alias)."', ".$this->db->escape($this->entity).", '".$this->db->idate($now)."'";
 			$sql .= ", ".(!empty($user->id) ? ((int) $user->id) : "null");
 			$sql .= ", ".(!empty($this->typent_id) ? ((int) $this->typent_id) : "null");
 			$sql .= ", ".(!empty($this->canvas) ? "'".$this->db->escape($this->canvas)."'" : "null");
-			$sql .= ", ".$this->status;
+			$sql .= ", ".((int) $this->status);
 			$sql .= ", ".(!empty($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null");
 			$sql .= ", 0";
 			$sql .= ", ".(int) $this->fk_incoterms;
 			$sql .= ", '".$this->db->escape($this->location_incoterms)."'";
 			$sql .= ", ".(!empty($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
@@ -1367,17 +1367,17 @@ class Societe extends CommonObject
 			$sql .= ",address = '".$this->db->escape($this->address)."'";
 
 			$sql .= ",zip = ".(!empty($this->zip) ? "'".$this->db->escape($this->zip)."'" : "null");
 			$sql .= ",town = ".(!empty($this->town) ? "'".$this->db->escape($this->town)."'" : "null");
 
-			$sql .= ",fk_departement = '".(!empty($this->state_id) ? $this->state_id : '0')."'";
-			$sql .= ",fk_pays = '".(!empty($this->country_id) ? $this->country_id : '0')."'";
+			$sql .= ",fk_departement = ".((!empty($this->state_id) && $this->state_id > 0) ? ((int) $this->state_id) : 'null');
+			$sql .= ",fk_pays = ".((!empty($this->country_id) && $this->country_id > 0) ? ((int) $this->country_id) : 'null');
 
 			$sql .= ",phone = ".(!empty($this->phone) ? "'".$this->db->escape($this->phone)."'" : "null");
 			$sql .= ",fax = ".(!empty($this->fax) ? "'".$this->db->escape($this->fax)."'" : "null");
 			$sql .= ",email = ".(!empty($this->email) ? "'".$this->db->escape($this->email)."'" : "null");
-			$sql .= ", socialnetworks = '".$this->db->escape(json_encode($this->socialnetworks))."'";
+			$sql .= ",socialnetworks = '".$this->db->escape(json_encode($this->socialnetworks))."'";
 			$sql .= ",url = ".(!empty($this->url) ? "'".$this->db->escape($this->url)."'" : "null");
 
 			$sql .= ",parent = ".($this->parent > 0 ? $this->parent : "null");
 
 			$sql .= ",note_private = ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");

Vulnerable code: 		$sql .= ",state_id = ".($this->state_id > 0 ? $this->state_id : "null");
		$sql .= ",fk_pays = ".($this->country_id > 0 ? $this->country_id : "null");
		$sql .= ' deductible = '.(isset($this->deductible) ? $this->deductible : "null").',';
		$sql .= ' active = '.(isset($this->active) ? $this->active : "null").',';
		$sql .= ' fk_pays = '.(isset($this->fk_pays) ? $this->fk_pays : "null").',';
		$sql .= ' WHERE id='.$this->id;
				$object->country_id = GETPOST('country_id') != '' ?GETPOST('country_id') : $mysoc->country_id;
			$sql .= ", ".$this->status;
			$sql .= ",fk_departement = '".(!empty($this->state_id) ? $this->state_id : '0')."'";
			$sql .= ",fk_pays = '".(!empty($this->country_id) ? $this->country_id : '0')."'";
			$sql .= ", socialnetworks = '".$this->db->escape(json_encode($this->socialnetworks))."'";
Fixed code: 		$sql .= ",state_id = ".($this->state_id > 0 ? ((int) $this->state_id) : "null");
		$sql .= ",fk_pays = ".($this->country_id > 0 ? ((int) $this->country_id) : "null");
		$sql .= ' deductible = '.(isset($this->deductible) ? ((int) $this->deductible) : "null").',';
		$sql .= ' active = '.(isset($this->active) ? ((int) $this->active) : "null").',';
		$sql .= ' fk_pays = '.((isset($this->fk_pays) && $this->fk_pays > 0) ? ((int) $this->fk_pays) : "null").',';
		$sql .= ' WHERE id='.((int) $this->id);
				$object->country_id = GETPOST('country_id', 'int') != '' ? GETPOST('country_id', 'int') : $mysoc->country_id;
			$sql .= ", ".((int) $this->status);
			$sql .= ",fk_departement = ".((!empty($this->state_id) && $this->state_id > 0) ? ((int) $this->state_id) : 'null');
			$sql .= ",fk_pays = ".((!empty($this->country_id) && $this->country_id > 0) ? ((int) $this->country_id) : 'null');
			$sql .= ",socialnetworks = '".$this->db->escape(json_encode($this->socialnetworks))."'";