https://bugs.kde.org/show_bug.cgi?id=524551
Bug ID: 524551
Summary: [SECURITY] Integer overflow to heap out-of-bounds
write
Classification: Applications
Product: okular
Version First unspecified
Reported In:
Platform: Other
OS: Other
Status: REPORTED
Severity: grave
Priority: NOR
Component: DVI backend
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
### Summary
Okular's DVI generator plugin (`generators/dvi`) is vulnerable to a heap-based
buffer overflow (CWE-190 leading to CWE-787). An integer overflow during the
processing of a DVI `\special` command allows a 32-bit length field `a` to wrap
to a 0-byte allocation during `new char[a + 1]`. The subsequent unregulated
`strncpy` operation copies up to 4 GiB of attacker-controlled file data into
the minimal heap allocation, causing a deliberate and severe out-of-bounds
write. Recommended severity: **High**.
### Details
When a DVI file is loaded, `dviRenderer::setFile` processes document contents
by scanning the file for specials via `prescan`. Specifically, the code inside
`dviRenderer_prescan.cpp` explicitly handles opcodes `XXX1` through `XXX4` (239
through 242) starting at line 795:
```cpp
case XXX4: {
quint8 *beginningOfSpecialCommand = command_pointer - 1;
quint32 a = readUINT(ch - XXX1 + 1);
if (a > 0) {
char *cmd = new char[a + 1]; // Wraps to 0 when a == 0xFFFFFFFF
strncpy(cmd, reinterpret_cast<char *>(command_pointer), a);
command_pointer += a;
cmd[a] = '\0';
```
`bigEndianByteReader::readUINT` reads the designated length bytes directly from
the file buffer without upper-bound validation against the `end_pointer` or
structural thresholds.
The `a` variable is declared as `quint32`. If the 4-byte read returns
`0xFFFFFFFF` (4,294,967,295), standard arithmetic causes `a + 1` to wrap to
exactly `0` when allocating memory via `new char[0]`.
However, the `strncpy` and assignment logic blindly passes the pre-wrapped
32-bit `a` parameter to transfer bytes off the `command_pointer`. This causes a
catastrophic copy of practically unbounded length across the heap boundary.
Additionally, because `command_pointer + a` is never checked against
`end_pointer`, any large value of `a` can easily trigger out-of-bounds
sequential heap *reads* that bleed file-mapping internals to potential
PostScript rendering or hyperlinking consumers (which process the `cmd` data
array downstream).
This identical pattern is duplicated independently during the drawing phase in
`dviRenderer_draw.cpp:508-514`, making the vulnerability reachable multiple
times.
### PoC
1. Save a minimal trigger byte sequence indicating the `XXX4` opcode (hex
`F2`), followed by an unmanageably large length `FF FF FF FF`, inside a valid
`.dvi` body block.
2. Example hex representation snippet:
`... F2 FF FF FF FF <attacker_raw_payload_bytes> ...`
3. Load the resulting `.dvi` file in Okular.
4. The process aborts instantly (Denial of Service/Heap Corruption). Under
Address Sanitizer (ASAN), it registers an immediate `heap-buffer-overflow WRITE
of size 1` generated during the unbounded `strncpy` executed over the 0-byte
chunk.
### Impact
This constitutes an uncontrolled heap out-of-bounds write of remote,
unauthenticated file content. Given that `strncpy` NUL-pads to `n`, it cannot
be cleanly truncated with an early `\0` in the payload, forcing the write to
storm through the process's allocated heaps until it sequentially hits an
unmapped border (inevitably crashing as a Denial of Service).
Full remote code execution (RCE) is plausible if an attacker successfully
grooms the system heap prior through other rendering primitives to hijack
corrupted adjacent C++ object metadata, albeit very complex due to the
uncontrolled continuous bounds.
To resolve the vulnerability, switch arithmetic to use `size_t` correctly on
64-bit systems and employ `qMin<qint64>(a, end_pointer - command_pointer)` to
strictly limit interactions within bounds.
--
You are receiving this mail because:
You are watching all bug changes.