PhpSpreadsheet's OLE reader follows sector chains from attacker-controlled XLS/OLE metadata without detecting cycles or enforcing a maximum chain length. A tiny malformed .xls/OLE file can set the small-block depot sector chain to point back to itself. During normal XLS detection, OLERead::read() appends the same sector data repeatedly until the PHP process exhausts memory.
This is reachable from Reader\Xls::canRead() and therefore from automatic spreadsheet type detection. Applications that accept attacker-controlled spreadsheet uploads can suffer denial of service from a very small file.
OLERead::read() loads the input and builds sector chains from attacker-controlled OLE header and allocation-table values:
src/PhpSpreadsheet/Shared/OLERead.php:82 reads the entire file after validating only the OLE magic.src/PhpSpreadsheet/Shared/OLERead.php:84-97 reads sector-chain metadata from the file header.src/PhpSpreadsheet/Shared/OLERead.php:132-146 builds bigBlockChain and then follows the small-block depot chain.The vulnerable loop is:
$sbdBlock = $this->sbdStartBlock;
$this->smallBlockChain = '';
while ($sbdBlock != -2) {
$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
$this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
$pos += 4 * $bbs;
$sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
}
There is no visited-sector set, no maximum iteration count, no EOF bound, and no check that the next sector differs from a previously visited sector. If the allocation table maps sector 0 to sector 0, the loop appends the same sector data forever until memory is exhausted.
The issue is reachable during normal reader detection/loading:
src/PhpSpreadsheet/Reader/XlsBase.php:153-165 calls OLERead::read() from canRead().src/PhpSpreadsheet/Reader/Xls.php:376-383 calls OLERead::read() from loadOLE().src/PhpSpreadsheet/IOFactory.php:181-213...1.30.62.1.182.4.73.10.75.8.1Exploitability
AV:NAC:LPR:NUI:NScope
S:UImpact
C:NI:NA:H7.5/CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H