From 6c1dfcb9214ecbf010719a846c8b3b8ea38f2653 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 27 Mar 2019 16:06:47 +0100 Subject: add security fix backported from 9.4.1: [security] Bad chevrons rendering on dropdowns [security] Iframe and forms are rendered in rich text contents [security] Type juggling authentication bypass [security] Malicious images upload [security] Password token date was not reset [security] Prevent timed attack and enforce cookie security --- glpi-security1.patch | 432 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 glpi-security1.patch (limited to 'glpi-security1.patch') diff --git a/glpi-security1.patch b/glpi-security1.patch new file mode 100644 index 0000000..f33fc96 --- /dev/null +++ b/glpi-security1.patch @@ -0,0 +1,432 @@ +From c5314dd86d6560865670940b59ac0fbb97225bb4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?C=C3=A9dric=20Anne?= +Date: Tue, 26 Feb 2019 13:46:06 +0100 +Subject: [PATCH 01/10] Fix chevrons display in select2 rendering + +--- + inc/html.class.php | 10 ++++++++++ + js/common.js | 16 ++++++++++++---- + 2 files changed, 22 insertions(+), 4 deletions(-) + +diff --git a/inc/html.class.php b/inc/html.class.php +index 760ffe164b..e5907c8d5b 100644 +--- a/inc/html.class.php ++++ b/inc/html.class.php +@@ -4329,6 +4329,11 @@ static function jsAdaptDropdown($id, $params = []) { + if (typeof data.text === 'string' + && data.text.toUpperCase().indexOf(params.term.toUpperCase()) >= 0 + ) { ++ if (data.text.indexOf('>') !== -1 || data.text.indexOf('<') !== -1) { ++ // escape text, if it contains chevrons (can already be escaped prior to this point :/) ++ data.text = jQuery.fn.select2.defaults.defaults.escapeMarkup(data.text); ++ } ++ + return data; + } + return null; +@@ -4341,6 +4346,11 @@ static function jsAdaptDropdown($id, $params = []) { + if (child.text.toUpperCase().indexOf(params.term.toUpperCase()) != -1 + || data.text.toUpperCase().indexOf(params.term.toUpperCase()) != -1 + ) { ++ ++ if (child.text.indexOf('>') !== -1 || child.text.indexOf('<') !== -1) { ++ // escape text, if it contains chevrons (can already be escaped prior to this point :/) ++ child.text = jQuery.fn.select2.defaults.defaults.escapeMarkup(child.text); ++ } + filteredChildren.push(child); + } + }); +diff --git a/js/common.js b/js/common.js +index c08623434c..15cf04b200 100644 +--- a/js/common.js ++++ b/js/common.js +@@ -948,17 +948,25 @@ function markMatch (text, term) { + * Function that renders select2 results. + */ + var templateResult = function(result) { +- if (!result.id) { +- return result.text; ++ if (!result.text) { ++ return null; + } + + var _elt = $(''); + _elt.attr('title', result.title); + +- var markup=[result.text]; ++ var text = result.text; ++ if (text.indexOf('>') !== -1 || text.indexOf('<') !== -1) { ++ // escape text, if it contains chevrons (can already be escaped prior to this point :/) ++ text = jQuery.fn.select2.defaults.defaults.escapeMarkup(result.text); ++ }; ++ ++ if (!result.id) { ++ return text; ++ } + + var _term = query.term || ''; +- var markup = markMatch(result.text, _term); ++ var markup = markMatch(text, _term); + + if (result.level) { + var a=''; + +From 2c5d9f80f64a1f5ef4c62af8be5d24b812b75ecc Mon Sep 17 00:00:00 2001 +From: Johan Cwiklinski +Date: Tue, 5 Mar 2019 12:49:05 +0100 +Subject: [PATCH 02/10] Disallow all on attributes + +--- + inc/html.class.php | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/inc/html.class.php b/inc/html.class.php +index e5907c8d5b..f9aa6532dd 100644 +--- a/inc/html.class.php ++++ b/inc/html.class.php +@@ -3550,6 +3550,9 @@ static function initEditorSystem($name, $rand = '', $display = true, $readonly = + // init editor + tinyMCE.init({ + language: '$language', ++ invalid_elements: 'form,iframe,script,@[onclick|ondblclick|' ++ + 'onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|' ++ + 'onkeydown|onkeyup]', + browser_spellcheck: true, + mode: 'exact', + elements: '$name', + +From a330e5b49f46680cf9fb877fdac7a6e44eff9115 Mon Sep 17 00:00:00 2001 +From: Johan Cwiklinski +Date: Mon, 4 Mar 2019 16:15:04 +0100 +Subject: [PATCH 03/10] Strict check + +--- + inc/auth.class.php | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/inc/auth.class.php b/inc/auth.class.php +index 323b3b94a2..707d7ab314 100644 +--- a/inc/auth.class.php ++++ b/inc/auth.class.php +@@ -276,14 +276,14 @@ static function checkPassword($pass, $hash) { + $ok = password_verify($pass, $hash); + + } else if (strlen($hash)==32) { +- $ok = md5($pass) == $hash; ++ $ok = md5($pass) === $hash; + + } else if (strlen($hash)==40) { +- $ok = sha1($pass) == $hash; ++ $ok = sha1($pass) === $hash; + + } else { + $salt = substr($hash, 0, 8); +- $ok = ($salt.sha1($salt.$pass) == $hash); ++ $ok = ($salt.sha1($salt.$pass) === $hash); + } + + return $ok; + +From f8959e587db32361c3013898c9f223a4151ada33 Mon Sep 17 00:00:00 2001 +From: Johan Cwiklinski +Date: Tue, 12 Mar 2019 14:44:53 +0100 +Subject: [PATCH 04/10] Use exif if present to check if file is image, or + fallback with W on fileinfo + +--- + inc/config.class.php | 3 +++ + inc/document.class.php | 13 +++++++++++-- + tests/functionnal/Document.php | 20 +++++++------------- + tests/notanimage.jpg | 3 +++ + 4 files changed, 24 insertions(+), 15 deletions(-) + create mode 100644 tests/notanimage.jpg + +diff --git a/inc/config.class.php b/inc/config.class.php +index b648d53181..8797b5bd4c 100644 +--- a/inc/config.class.php ++++ b/inc/config.class.php +@@ -2456,6 +2456,9 @@ static function checkExtensions($list = null) { + 'CAS' => [ + 'required' => false, + 'class' => 'phpCAS' ++ ], ++ 'exif' => [ ++ 'required' => false + ] + ]; + } else { +diff --git a/inc/document.class.php b/inc/document.class.php +index 8072dac9f2..faca1a93ee 100644 +--- a/inc/document.class.php ++++ b/inc/document.class.php +@@ -1497,8 +1497,17 @@ static function getImageTag($string) { + * @return boolean + */ + public static function isImage($file) { +- $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); +- return (in_array($ext, ['jpg', 'jpeg', 'png', 'bmp', 'gif'])); ++ if (extension_loaded('exif')) { ++ $etype = exif_imagetype($file); ++ return in_array($etype, [IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_BMP]); ++ } else { ++ Toolbox::logWarning('For security reasons, you should consider using exif PHP extension to properly check images.'); ++ $fileinfo = finfo_open(FILEINFO_MIME_TYPE); ++ return in_array( ++ finfo_file($fileinfo, $file), ++ ['image/jpeg', 'image/png','image/gif', 'image/bmp'] ++ ); ++ } + } + + /** +diff --git a/tests/functionnal/Document.php b/tests/functionnal/Document.php +index a00ca4b57f..1757ebc8e6 100644 +--- a/tests/functionnal/Document.php ++++ b/tests/functionnal/Document.php +@@ -215,25 +215,19 @@ public function testGetImageTag() { + + protected function isImageProvider() { + return [ +- ['PNG', true], +- ['png', true], +- ['JPG', true], +- ['jpg', true], +- ['jpeg', true], +- ['JPEG', true], +- ['bmp', true], +- ['BMP', true], +- ['gif', true], +- ['GIF', true], +- ['SVG', false] ++ [__FILE__, false], ++ [__DIR__ . "/../../pics/add_dropdown.png", true], ++ [__DIR__ . "/../../pics/corners.gif", true], ++ [__DIR__ . "/../../pics/PICS-AUTHORS.txt", false], ++ [__DIR__ . "/../notanimage.jpg", false] + ]; + } + + /** + * @dataProvider isImageProvider + */ +- public function testIsImage($ext, $expected) { +- $this->variable(\Document::isImage('myfile.' . $ext))->isIdenticalTo($expected); ++ public function testIsImage($file, $expected) { ++ $this->boolean(\Document::isImage($file))->isIdenticalTo($expected); + } + + /** +diff --git a/tests/notanimage.jpg b/tests/notanimage.jpg +new file mode 100644 +index 0000000000..d2dbc0fe33 +--- /dev/null ++++ b/tests/notanimage.jpg +@@ -0,0 +1,3 @@ ++ +Date: Tue, 12 Mar 2019 10:59:54 +0100 +Subject: [PATCH 05/10] Password token date was not removed + +--- + inc/user.class.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/inc/user.class.php b/inc/user.class.php +index e6ebb3a88d..fdc9d8b0ac 100644 +--- a/inc/user.class.php ++++ b/inc/user.class.php +@@ -4355,7 +4355,7 @@ public function updateForgottenPassword(array $input) { + } + $input2 = [ + 'password_forget_token' => '', +- 'password_forget_token_date' => null, ++ 'password_forget_token_date' => 'NULL', + 'id' => $this->fields['id'] + ]; + $this->update($input2); + +From 1ae67932a3de9349fbe5f0cd4d10d9a81a811f9d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?C=C3=A9dric=20Anne?= +Date: Thu, 14 Mar 2019 17:22:48 +0100 +Subject: [PATCH 06/10] Fix ITIL image path + +--- + inc/ticket.class.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/inc/ticket.class.php b/inc/ticket.class.php +index b414f85583..c9608c84e9 100644 +--- a/inc/ticket.class.php ++++ b/inc/ticket.class.php +@@ -7249,7 +7249,7 @@ function showTimeline($rand) { + + echo "$filename"; +- if (Document::isImage($filename)) { ++ if (Document::isImage(GLPI_DOC_DIR . '/' . $item_i['filepath'])) { + echo "
"; + echo ""; + +From cc1e2b02288635a4692bef5d2a7598862eafa4d1 Mon Sep 17 00:00:00 2001 +From: Johan Cwiklinski +Date: Mon, 18 Mar 2019 19:27:04 +0100 +Subject: [PATCH 08/10] Update security-checker + +--- + composer.json | 2 +- + composer.lock | 21 +++++++++++---------- + 2 files changed, 12 insertions(+), 11 deletions(-) + +diff --git a/composer.json b/composer.json +index d1c3ee858b..66f6b52643 100644 +--- a/composer.json ++++ b/composer.json +@@ -43,7 +43,7 @@ + "patchwork/jsqueeze": "^2.0", + "atoum/atoum": "^3.3", + "atoum/telemetry-extension": "^1.0", +- "sensiolabs/security-checker": "^4.1", ++ "sensiolabs/security-checker": "^5.0", + "fzaninotto/Faker": "^1.7", + "jakub-onderka/php-parallel-lint": "^1.0" + }, +diff --git a/composer.lock b/composer.lock +index 467f8ce42d..4ce22d532d 100644 +--- a/composer.lock ++++ b/composer.lock +@@ -1,10 +1,10 @@ + { + "_readme": [ + "This file locks the dependencies of your project to a known state", +- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", ++ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], +- "content-hash": "4db9a468fea5706b030e4b631a08b28a", ++ "content-hash": "57b9b4901479f79936d11b74f56ff761", + "packages": [ + { + "name": "container-interop/container-interop", +@@ -2769,20 +2769,21 @@ + }, + { + "name": "sensiolabs/security-checker", +- "version": "v4.1.8", ++ "version": "v5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/security-checker.git", +- "reference": "dc270d5fec418cc6ac983671dba5d80ffaffb142" ++ "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1" + }, + "dist": { + "type": "zip", +- "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/dc270d5fec418cc6ac983671dba5d80ffaffb142", +- "reference": "dc270d5fec418cc6ac983671dba5d80ffaffb142", ++ "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/46be3f58adac13084497961e10eed9a7fb4d44d1", ++ "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", ++ "php": ">=5.5.9", + "symfony/console": "~2.7|~3.0|~4.0" + }, + "bin": [ +@@ -2791,12 +2792,12 @@ + "type": "library", + "extra": { + "branch-alias": { +- "dev-master": "4.1-dev" ++ "dev-master": "5.0-dev" + } + }, + "autoload": { +- "psr-0": { +- "SensioLabs\\Security": "" ++ "psr-4": { ++ "SensioLabs\\Security\\": "SensioLabs/Security" + } + }, + "notification-url": "https://packagist.org/downloads/", +@@ -2810,7 +2811,7 @@ + } + ], + "description": "A security checker for your composer.lock", +- "time": "2018-02-28T22:10:01+00:00" ++ "time": "2018-12-19T17:14:59+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + +From 150a94fc71230ca04fc00e2f2b6c40936cb3c060 Mon Sep 17 00:00:00 2001 +From: Frederico Gendorf +Date: Fri, 15 Mar 2019 11:50:29 -0300 +Subject: [PATCH 09/10] Fix user image display and upload; fixes #5604 + +--- + front/document.send.php | 2 +- + inc/user.class.php | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/front/document.send.php b/front/document.send.php +index d80ab14aad..87a7db6298 100644 +--- a/front/document.send.php ++++ b/front/document.send.php +@@ -69,7 +69,7 @@ + } + + if ($splitter[0] == "_pictures") { +- if (Document::isImage($_GET['file'])) { ++ if (Document::isImage(GLPI_DOC_DIR."/".$_GET['file'])) { + $send = true; + } + } +diff --git a/inc/user.class.php b/inc/user.class.php +index fdc9d8b0ac..67bfc71496 100644 +--- a/inc/user.class.php ++++ b/inc/user.class.php +@@ -735,7 +735,7 @@ function prepareInputForUpdate($input) { + $picture_path = GLPI_PICTURE_DIR . "/$sub/${filename}.$extension"; + self::dropPictureFiles($filename.".".$extension); + +- if (Document::isImage($input["_picture"]) ++ if (Document::isImage($fullpath) + && Document::renameForce($fullpath, $picture_path)) { + Session::addMessageAfterRedirect(__('The file is valid. Upload is successful.')); + // For display + +From 9ef29babf8ae57986b2f3c1480a07c4608599a64 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?C=C3=A9dric=20Anne?= +Date: Fri, 22 Mar 2019 11:05:24 +0100 +Subject: [PATCH 10/10] Fix escaping of optgroups in dropdowns; fixes #5646 + +--- + js/common.js | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/js/common.js b/js/common.js +index 15cf04b200..e083b3970e 100644 +--- a/js/common.js ++++ b/js/common.js +@@ -958,11 +958,13 @@ var templateResult = function(result) { + var text = result.text; + if (text.indexOf('>') !== -1 || text.indexOf('<') !== -1) { + // escape text, if it contains chevrons (can already be escaped prior to this point :/) +- text = jQuery.fn.select2.defaults.defaults.escapeMarkup(result.text); ++ text = jQuery.fn.select2.defaults.defaults.escapeMarkup(text); + }; + + if (!result.id) { +- return text; ++ // If result has no id, then it is used as an optgroup and is not used for matches ++ _elt.html(text); ++ return _elt; + } + + var _term = query.term || ''; -- cgit