From eede3aaf171c033ed4b70400b28263fe0ffd56f5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 21 Feb 2022 22:50:32 +0100 Subject: [PATCH] Fix #yogosha9033 --- htdocs/core/lib/website2.lib.php | 56 +++++ htdocs/website/index.php | 350 +++++++++++++++++-------------- 2 files changed, 246 insertions(+), 160 deletions(-) diff --git a/htdocs/core/lib/website2.lib.php b/htdocs/core/lib/website2.lib.php index fb11846a4fb..64111e295d2 100644 --- a/htdocs/core/lib/website2.lib.php +++ b/htdocs/core/lib/website2.lib.php @@ -601,3 +601,59 @@ function showWebsiteTemplates(Website $website) print ''; print ''; } + + +/** + * checkPHPCode + * + * @param string $phpfullcodestringold PHP old string + * @param string $phpfullcodestring PHP new string + * @return int Error or not + */ +function checkPHPCode($phpfullcodestringold, $phpfullcodestring) +{ + global $conf, $langs, $user; + + $error = 0; + + if (empty($phpfullcodestringold) && empty($phpfullcodestring)) { + return 0; + } + + // First check forbidden commands + $forbiddenphpcommands = array(); + if (empty($conf->global->WEBSITE_PHP_ALLOW_EXEC)) { // If option is not on, we disallow functions to execute commands + $forbiddenphpcommands = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI"); + } + if (empty($conf->global->WEBSITE_PHP_ALLOW_WRITE)) { // If option is not on, we disallow functions to write files + $forbiddenphpcommands = array_merge($forbiddenphpcommands, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "unlink", "mkdir", "rmdir", "symlink", "touch", "umask")); + } + foreach ($forbiddenphpcommands as $forbiddenphpcommand) { + if (preg_match('/'.$forbiddenphpcommand.'\s*\(/ms', $phpfullcodestring)) { + $error++; + setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpcommand), null, 'errors'); + break; + } + } + // This char can be used to execute RCE for example using with echo `ls` + $forbiddenphpchars = array(); + if (empty($conf->global->WEBSITE_PHP_ALLOW_DANGEROUS_CHARS)) { // If option is not on, we disallow functions to execute commands + $forbiddenphpchars = array("`"); + } + foreach ($forbiddenphpchars as $forbiddenphpchar) { + if (preg_match('/'.$forbiddenphpchar.'/ms', $phpfullcodestring)) { + $error++; + setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors'); + break; + } + } + + if (!$error && empty($user->rights->website->writephp)) { + if ($phpfullcodestringold != $phpfullcodestring) { + $error++; + setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors'); + } + } + + return $error; +} diff --git a/htdocs/website/index.php b/htdocs/website/index.php index 610607f0321..7765d963d8f 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -1407,175 +1407,245 @@ if ($action == 'updatecss' && $usercanedit) { // Html header file - $htmlheadercontent = ''; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_HTML_HEADER', 'none')); - /* We disable php code since htmlheader is never executed as an include but only read by fgets_content. - $htmlheadercontent.= "\n";*/ + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $htmlheadercontent .= preg_replace(array('/\n*/ims', '/<\/html>\n*/ims'), array('', ''), GETPOST('WEBSITE_HTML_HEADER', 'none')); + if (!$errorphpcheck) { + $htmlheadercontent = ''; - /*$htmlheadercontent.= "\n".'"."\n";*/ + /* We disable php code since htmlheader is never executed as an include but only read by fgets_content. + $htmlheadercontent.= "\n";*/ - $htmlheadercontent = trim($htmlheadercontent)."\n"; + $htmlheadercontent .= preg_replace(array('/\n*/ims', '/<\/html>\n*/ims'), array('', ''), GETPOST('WEBSITE_HTML_HEADER', 'none')); - $result = dolSaveHtmlHeader($filehtmlheader, $htmlheadercontent); - if (!$result) { + /*$htmlheadercontent.= "\n".'"."\n";*/ + + $htmlheadercontent = trim($htmlheadercontent)."\n"; + + $result = dolSaveHtmlHeader($filehtmlheader, $htmlheadercontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filehtmlheader, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filehtmlheader, null, 'errors'); } // Css file - $csscontent = ''; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_CSS_INLINE', 'none')); - $csscontent .= "\n"; + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $csscontent .= trim(GETPOST('WEBSITE_CSS_INLINE', 'none'))."\n"; + if (!$errorphpcheck) { + $csscontent = ''; - $csscontent .= '\n"; + $csscontent .= "\n"; - dol_syslog("Save css content into ".$filecss); + $csscontent .= trim(GETPOST('WEBSITE_CSS_INLINE', 'none'))."\n"; - $result = dolSaveCssFile($filecss, $csscontent); - if (!$result) { + $csscontent .= '\n"; + + dol_syslog("Save css content into ".$filecss); + + $result = dolSaveCssFile($filecss, $csscontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filecss, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filecss, null, 'errors'); } // Js file - $jscontent = ''; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_JS_INLINE', 'none')); - $jscontent .= "\n"; + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $jscontent .= trim(GETPOST('WEBSITE_JS_INLINE', 'none'))."\n"; + if (!$errorphpcheck) { + $jscontent = ''; - $jscontent .= '\n"; + $jscontent .= "\n"; - $result = dolSaveJsFile($filejs, $jscontent); - if (!$result) { + $jscontent .= trim(GETPOST('WEBSITE_JS_INLINE', 'none'))."\n"; + + $jscontent .= '\n"; + + $result = dolSaveJsFile($filejs, $jscontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filejs, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filejs, null, 'errors'); } // Robot file - $robotcontent = ''; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_ROBOT', 'restricthtml')); - /*$robotcontent.= "\n";*/ + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $robotcontent .= trim(GETPOST('WEBSITE_ROBOT', 'restricthtml'))."\n"; + if (!$errorphpcheck) { + $robotcontent = ''; - /*$robotcontent.= "\n".'"."\n";*/ + /*$robotcontent.= "\n";*/ - $result = dolSaveRobotFile($filerobot, $robotcontent); - if (!$result) { + $robotcontent .= trim(GETPOST('WEBSITE_ROBOT', 'restricthtml'))."\n"; + + /*$robotcontent.= "\n".'"."\n";*/ + + $result = dolSaveRobotFile($filerobot, $robotcontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filerobot, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filerobot, null, 'errors'); } // Htaccess file - $htaccesscontent = ''; - $htaccesscontent .= trim(GETPOST('WEBSITE_HTACCESS', 'restricthtml'))."\n"; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_HTACCESS', 'restricthtml')); - $result = dolSaveHtaccessFile($filehtaccess, $htaccesscontent); - if (!$result) { + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages + + if (!$errorphpcheck) { + $htaccesscontent = ''; + $htaccesscontent .= trim(GETPOST('WEBSITE_HTACCESS', 'restricthtml'))."\n"; + + $result = dolSaveHtaccessFile($filehtaccess, $htaccesscontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filehtaccess, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filehtaccess, null, 'errors'); } - // manifest.json file - $manifestjsoncontent = ''; + // Manifest.json file + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_MANIFEST_JSON', 'none')); - $manifestjsoncontent .= "\n"; + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $manifestjsoncontent .= trim(GETPOST('WEBSITE_MANIFEST_JSON', 'none'))."\n"; + if (!$errorphpcheck) { + $manifestjsoncontent = ''; - $manifestjsoncontent .= '\n"; + $manifestjsoncontent .= "\n"; - $result = dolSaveManifestJson($filemanifestjson, $manifestjsoncontent); - if (!$result) { + $manifestjsoncontent .= trim(GETPOST('WEBSITE_MANIFEST_JSON', 'none'))."\n"; + + $manifestjsoncontent .= '\n"; + + $result = dolSaveManifestJson($filemanifestjson, $manifestjsoncontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filemanifestjson, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filemanifestjson, null, 'errors'); } // README.md file - $readmecontent = ''; + $phpfullcodestringold = ''; + $phpfullcodestring = dolKeepOnlyPhpCode(GETPOST('WEBSITE_README', 'restricthtml')); - /*$readmecontent.= "\n";*/ + // Security analysis + $errorphpcheck = checkPHPCode($phpfullcodestringold, $phpfullcodestring); // Contains the setEventMessages - $readmecontent .= trim(GETPOST('WEBSITE_README', 'restricthtml'))."\n"; + if (!$errorphpcheck) { + $readmecontent = ''; - /*$readmecontent.= '"."\n";*/ + /*$readmecontent.= "\n";*/ - $result = dolSaveReadme($filereadme, $readmecontent); - if (!$result) { + $readmecontent .= trim(GETPOST('WEBSITE_README', 'restricthtml'))."\n"; + + /*$readmecontent.= '"."\n";*/ + + $result = dolSaveReadme($filereadme, $readmecontent); + if (!$result) { + $error++; + setEventMessages('Failed to write file '.$filereadme, null, 'errors'); + } + } else { $error++; - setEventMessages('Failed to write file '.$filereadme, null, 'errors'); } @@ -1999,57 +2069,17 @@ if ($usercanedit && (($action == 'updatesource' || $action == 'updatecontent' || $objectpage->content = GETPOST('PAGE_CONTENT', 'none'); - // Security analysis $phpfullcodestring = dolKeepOnlyPhpCode($objectpage->content); - // First check forbidden commands - $forbiddenphpcommands = array(); - if (empty($conf->global->WEBSITE_PHP_ALLOW_EXEC)) { // If option is not on, we disallow functions to execute commands - $forbiddenphpcommands = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI"); - } - if (empty($conf->global->WEBSITE_PHP_ALLOW_WRITE)) { // If option is not on, we disallow functions to write files - $forbiddenphpcommands = array_merge($forbiddenphpcommands, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "unlink", "mkdir", "rmdir", "symlink", "touch", "umask")); - } - foreach ($forbiddenphpcommands as $forbiddenphpcommand) { - if (preg_match('/'.$forbiddenphpcommand.'\s*\(/ms', $phpfullcodestring)) { - $error++; - setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpcommand), null, 'errors'); - if ($action == 'updatesource') { - $action = 'editsource'; - } - if ($action == 'updatecontent') { - $action = 'editcontent'; - } - } - } - // This char can be used to execute RCE for example using with echo `ls` - $forbiddenphpchars = array(); - if (empty($conf->global->WEBSITE_PHP_ALLOW_DANGEROUS_CHARS)) { // If option is not on, we disallow functions to execute commands - $forbiddenphpchars = array("`"); - } - foreach ($forbiddenphpchars as $forbiddenphpchar) { - if (preg_match('/'.$forbiddenphpchar.'/ms', $phpfullcodestring)) { - $error++; - setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors'); - if ($action == 'updatesource') { - $action = 'editsource'; - } - if ($action == 'updatecontent') { - $action = 'editcontent'; - } - } - } + // Security analysis + $error = checkPHPCode($phpfullcodestringold, $phpfullcodestring); - if (empty($user->rights->website->writephp)) { - if ($phpfullcodestringold != $phpfullcodestring) { - $error++; - setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors'); - if ($action == 'updatesource') { - $action = 'editsource'; - } - if ($action == 'updatecontent') { - $action = 'editcontent'; - } + if ($error) { + if ($action == 'updatesource') { + $action = 'editsource'; + } + if ($action == 'updatecontent') { + $action = 'editcontent'; } }