CVE-2020-7063
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its Phar extension creates and reads PHP archives as well as tar and zip archives, through the Phar and PharData classes.
An Information Exposure vulnerability (CVE-2020-7063) has been identified in the buildFromIterator() and buildFromDirectory() methods of PHP's Phar extension, which store every file they add to an archive with all-access permissions (mode 0666) regardless of the permissions of the original file, so that anyone who extracts the archive may get files that every local user can read and write. PHP tracks this issue as bug #79082.
Per MITRE CWE-281: The product does not preserve permissions or incorrectly preserves permissions when copying, restoring, or sharing objects, which can cause them to have less restrictive permissions than intended.
This issue affects all versions of PHP before 7.2.28, PHP 7.3 before 7.3.15 and PHP 7.4 before 7.4.3, which includes the End-of-Life PHP 5.6, 7.0 and 7.1 release lines through their final releases (5.6.40, 7.0.33 and 7.1.33).
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/phar) - Affected versions: <7.2.28, >=7.3.0 <7.3.15, >=7.4.0 <7.4.3
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 7.2.28, 7.3.15, 7.4.3
- NES for PHP 5.6.1 (based on PHP 5.6.40)
Vulnerability Info
This Medium-severity vulnerability is found in the Phar extension of all PHP versions before 7.2.28, 7.3 before 7.3.15 and 7.4 before 7.4.3, including the End-of-Life PHP 5.6, 7.0 and 7.1 release lines, which never received the upstream fix. The PHP project scores it 5.5 under CVSS 3.1 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N).
Archive formats such as tar and zip record a permission mode for each file, and tools that extract the archive apply that mode to the files they write. Phar::buildFromIterator(), and buildFromDirectory(), which uses the same code, add the files that an iterator or a directory listing returns. Each new archive entry starts with the extension's default file mode, PHAR_ENT_PERM_DEF_FILE (0666). In PHP 5.6.40, the code that copies a file into the archive sets its contents and size but never replaces that default:
data->internal_file->fp = NULL;
data->internal_file->fp_type = PHAR_UFP;
data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp);
data->fp = NULL;
php_stream_copy_to_stream_ex(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len);
data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize =
php_stream_tell(p_obj->fp) - data->internal_file->offset;As a result every file is stored as readable and writable by everyone, even when the source file was private, for example a configuration file with mode 0600. The reporter showed that files added with these methods came out of the archive with mode 0666 while the same file added with addFile() kept its restrictive mode. When such an archive is extracted, the files can be readable by every local user, which may expose secrets such as credentials, and writable by them as well.
The issue affects applications that build archives with buildFromIterator() or buildFromDirectory() from files that should stay private, when those archives are later extracted on a system with other local users, under any SAPI. It does not let a remote attacker read anything by itself: the exposure happens on the system where the archive is extracted, and the CVSS score accordingly rates it as a local issue that needs user interaction.
The fix copies the permission bits of each source file into its archive entry. When the source cannot be stat'ed, for example a stream, it applies the process umask to the default mode instead:
if (php_stream_stat(fp, &ssb) != -1) {
data->internal_file->flags = ssb.sb.st_mode & PHAR_ENT_PERM_MASK ;
} else {
#ifndef _WIN32
mode_t mask;
mask = umask(0);
umask(mask);
data->internal_file->flags &= ~mask;
#endif
}Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0 and 7.1 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, set the intended mode on each entry with PharFileInfo::chmod() after building the archive, or do not include private files in archives built with buildFromIterator() or buildFromDirectory().
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 7.2.28, 7.3.15, 7.4.3, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- dr (Loopia) (finder)
- Stanislav Malyshev (remediation)