CVE-2026-108359
Mở trang đầy đủCVSS 8.7 HIGH
PhpSpreadsheet ODS Reader: Unbounded number-rows-repeated → CPU & Memory Exhaustion (DoS)
Mô tả
Summary
When PhpSpreadsheet loads an OpenDocument Spreadsheet (.ods), Ods::processTableRow()
reads the table:number-rows-repeated attribute of a <table:table-row> with an unchecked
(int) cast, then materialises one cell per repeated row. This value is completely unbounded,
so a single ~1.4 KB file can instruct the reader to create up to millions of cells,
exhausting memory (fatal OOM). No authentication, no special configuration, and the default read
filter are required - any application that loads a user-supplied .ods is affected.
Affected versions
The number-rows-repeated expansion has existed in the ODS reader since its inception
(pre-dating the current release branches), so all maintained lines are affected.
| Branch | Affected |
|---|---|
| 1.x | ≤ 1.30.6 |
| 2.0–2.1 | ≤ 2.1.18 |
| 2.2–2.4 | ≤ 2.4.7 |
| 3.x | ≤ 3.10.7 |
| 4.x–5.x | ≤ 5.9.0 (latest) |
Root cause (source → sink)
src/PhpSpreadsheet/Reader/Ods.php:
// line 816 — row repeat, unchecked (int) cast
$rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
...
// line 1151 — row materialisation loop: NO upper bound
for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
$rID = $rowID + $rowAdjust;
$cell = $spreadsheet->getActiveSheet()->getCell($columnID . $rID); // allocates a Cell
$cell->setValueExplicit($dataValue, $type);
// ... no bounds check on $rowAdjust or $rID ...
}
Why columns are NOT vulnerable (already bounded):
The column-header loop at line 1326 shows the correct pattern with bounds checking:
// line 1326 (processTableColumnHeader): BOUNDED correctly
for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT; --$rowRepeats2) {
// HAS BOUNDS CHECK: && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT
}
Additionally, the cell-data column loop at line 1141 calls getCell() which throws exception
at Coordinate.php:448 when column exceeds MAX_COLUMN_INT (XFD / 16,384):
// Coordinate.php line 448
if ($columnIndex > self::MAX_COLUMN_INT) {
throw new Exception('Column string index can not be beyond XFD');
}
Result: Columns are capped at 16,384. Only rows are unbounded.
Impact
Memory exhaustion (single valued cell, repeated many rows):
A non-null cell value with a huge number-rows-repeated attribute materialises millions of
Cell objects → PHP fatal Allowed memory size ... exhausted.
Example:
- Payload:
<table:table-row table:number-rows-repeated="5000000">(5 million rows) - File size: 1.4 KB
- Memory consumed: ~256 MB (one Cell object per row)
- Result: OOM at default memory limit
Reproduction
Vulnerable payload (causes OOM):
<table:table table:name="Sheet1">
<table:table-row table:number-rows-repeated="5000000">
<table:table-cell office:value-type="float" office:value="1">
<text:p>1</text:p>
</table:table-cell>
</table:table-row>
</table:table>
Not vulnerable (for reference - throws exception):
<table:table table:name="Sheet1">
<table:table-row>
<table:table-cell office:value-type="float" office:value="1"
table:number-columns-repeated="1000000000">
<text:p>1</text:p>
</table:table-cell>
</table:table-row>
</table:table>
This column-repeat example throws Column string index can not be beyond XFD exception
(bounded defense, already in place).
Điểm và phân loại
- CVSS
- 8.7 HIGH v4.0
- Nguồn điểm
- GitHub Advisory
- Mức độ (theo nguồn)
- HIGH
- EPSS
- 0.66%
Vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Ảnh hưởng
| Package | Bản bị ảnh hưởng | Bản đã sửa |
|---|---|---|
composer phpoffice/phpspreadsheet |
>= 4.0.0, <= 5.9.0 |
5.10.0
|
composer phpoffice/phpspreadsheet |
>= 3.3.0, <= 3.10.7 |
3.10.8
|
composer phpoffice/phpspreadsheet |
>= 2.2.0, <= 2.4.7 |
2.4.8
|
composer phpoffice/phpspreadsheet |
>= 2.0.0, <= 2.1.18 |
2.1.19
|
composer phpoffice/phpspreadsheet |
<= 1.30.6 |
1.30.7
|
composer phpoffice/phpspreadsheet |
>= 4.0.0, < 5.10.0 |
5.10.0
|
composer phpoffice/phpspreadsheet |
>= 3.3.0, < 3.10.8 |
3.10.8
|
composer phpoffice/phpspreadsheet |
>= 2.2.0, < 2.4.8 |
2.4.8
|
composer phpoffice/phpspreadsheet |
>= 2.0.0, < 2.1.19 |
2.1.19
|
composer phpoffice/phpspreadsheet |
< 1.30.7 |
1.30.7
|
Vì sao được giữ
- CVSS 8.7 >= 8.0
Loại tác động
Không nhận ra loại tác động nào từ CWE, vector hay mô tả.
Dữ liệu riêng của nguồn
- OSV.dev · ID
- GHSA-j2w7-387v-m2rq
Tham khảo
- https://github.com/advisories/GHSA-j2w7-387v-m2rq
- https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-j2w7-387v-m2rq
- https://nvd.nist.gov/vuln/detail/CVE-2026-108359
- https://github.com/PHPOffice/PhpSpreadsheet/commit/299993894dce37aa3b64aa23e9a3f4bb77ab0e66
- https://github.com/PHPOffice/PhpSpreadsheet/commit/2de29384aa11223ee08136c0e6a9e9981bc1a2eb
- https://github.com/PHPOffice/PhpSpreadsheet/commit/980b5c294493856f1a25754f1e8a1d2185b93738
- https://github.com/PHPOffice/PhpSpreadsheet/commit/9a0b48622ebfe93fa48cb9bc09ce57303bb07af9
- https://github.com/PHPOffice/PhpSpreadsheet/commit/da13f40e8760d599b0e8a296017d4b028b20e86c
- https://github.com/PHPOffice/PhpSpreadsheet/releases/tag/1.30.7
- https://github.com/PHPOffice/PhpSpreadsheet/releases/tag/2.1.19
- https://github.com/PHPOffice/PhpSpreadsheet/releases/tag/2.4.8
- https://github.com/PHPOffice/PhpSpreadsheet/releases/tag/3.10.8
- https://github.com/PHPOffice/PhpSpreadsheet/releases/tag/5.10.0
- https://osv.dev/vulnerability/GHSA-j2w7-387v-m2rq
- https://github.com/PHPOffice/PhpSpreadsheet