shell bypass 403
<?php /** * @package admintools * @copyright Copyright (c)2010-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ defined('_JEXEC') || die; class AtsystemFeatureUploadshield extends AtsystemFeatureAbstract { protected $loadOrder = 370; /** * Is this feature enabled? * * @return bool */ public function isEnabled() { if (!$this->container->platform->isFrontend()) { return false; } if ($this->skipFiltering) { return false; } return ($this->cparams->getValue('uploadshield', 1) == 1); } /** * Scans all uploaded files for PHP tags. This prevents uploading PHP files or crafted * images with raw PHP code in them which may lead to arbitrary code execution under * several common circumstances. It will also block files with null bytes in their * filenames or with double extensions which include PHP in them (e.g. .php.jpg). */ public function onAfterInitialise() { // Do we have uploaded files? $input = $this->input->files; $ref = new ReflectionProperty($input, 'data'); $ref->setAccessible(true); $filesHash = $ref->getValue($input); if (empty($filesHash)) { return; } $extraInfo = ''; foreach ($filesHash as $key => $temp_descriptor) { if (is_array($temp_descriptor) && !array_key_exists('tmp_name', $temp_descriptor)) { $descriptors = $temp_descriptor; } else { $descriptors[] = $temp_descriptor; } unset($temp_descriptor); foreach ($descriptors as $descriptor) { $files = []; if (is_array($descriptor['tmp_name'])) { foreach ($descriptor['tmp_name'] as $key => $value) { $files[] = [ 'name' => $descriptor['name'][$key], 'type' => $descriptor['type'][$key], 'tmp_name' => $descriptor['tmp_name'][$key], 'error' => $descriptor['error'][$key], 'size' => $descriptor['size'][$key], ]; } } else { $files[] = $descriptor; } foreach ($files as $fileDescriptor) { $tempNames = $fileDescriptor['tmp_name']; $intendedNames = $fileDescriptor['name']; if (!is_array($tempNames)) { $tempNames = [$tempNames]; } if (!is_array($intendedNames)) { $intendedNames = [$intendedNames]; } $len = count($tempNames); for ($i = 0; $i < $len; $i++) { $tempName = array_shift($tempNames); $intendedName = array_shift($intendedNames); $extraInfo = "File descriptor :\n"; $extraInfo .= print_r($fileDescriptor, true); $extraInfo .= "\n"; // Empty file name (ie the field has been submitted, but it has no actual value in here), simply skip if (!$tempName) { continue; } // 1. Null byte check if (strstr($intendedName, "\u0000")) { $extraInfo .= "Block reason: null byte\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // 2. PHP-in-extension check $explodedName = explode('.', $intendedName); $explodedName = array_reverse($explodedName); // 2a. File extension is .php if ((count($explodedName) > 1) && (strtolower($explodedName[0]) == 'php')) { $extraInfo .= "Block reason: file extension is .php\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // 2a. File extension is php.xxx if ((count($explodedName) > 2) && (strtolower($explodedName[1]) == 'php')) { $extraInfo .= "Block reason: file extension is in the form of .php.xxx\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // 2b. File extensions is php.xxx.yyy if ((count($explodedName) > 3) && (strtolower($explodedName[2]) == 'php')) { $extraInfo .= "Block reason: file extension is in the form of .php.xxx.yyy\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // For whatever reason we can't access file contents, skip it if (!@file_exists($tempName) || !@is_readable($tempName)) { continue; } // 3. Contents scanner $fp = @fopen($tempName, 'r'); if ($fp === false) { continue; } // Initialise $data = ''; $extension = strtolower($explodedName[0]); $possibleFileForShortTagSyntax = in_array($extension, [ 'inc', 'phps', 'class', 'php3', 'php4', 'txt', 'dat', 'tpl', 'tmpl', ]); // Process the file in 128Kb chunks while (!feof($fp)) { // Read 128Kb and add it to the existing data (the last 4 bytes of the previous scan) $buffer = @fread($fp, 131072); $data .= $buffer; // Do we have a regular PHP tag? if (stristr($buffer, '<?php')) { $extraInfo .= "Block reason: file contains PHP open tag\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // Do we have a (possibly concealed) PHAR file? if (strstr($buffer, '__HALT_COMPILER')) { $extraInfo .= "Block reason: file is a possibly concealed PHAR file\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } // If we have text file which may have the short tag (<?) in it... if ($possibleFileForShortTagSyntax) { // ...do I have a short tag? if (strstr($buffer, '<?')) { $extraInfo .= "Block reason: file contains PHP short tag\n"; $this->exceptionsHandler->blockRequest('uploadshield', null, $extraInfo); return; } } // Keep the last 4 bytes of data to make sure we can catch partial strings. $data = substr($data, -14); // WARNING: Do NOT try seek to an earlier position! Here's how it all works. // // We just need to keep the last four bytes in $data so we can append the next 128Kb. // This way if the start of the tag is in the previous block and the rest is in the next // 128Kb block we can still scan it. The value 14 is not random. __HALT_COMPILER is 15 characters // and the longest string we're trying to detect. If it existed in this block we'd have // already found it and blocked it. Therefore the only possibility is that this block // ended in any of the first 14 characters of that substring. // // Do NOT seek to an earlier file position. It would be a rather silly thing to do. } fclose($fp); } } } } } }