Buffer Overflow in WriteImage
francobel opened this issue · 2 comments
Issue Type
Before opening an issue, please search and see if it has already been raised.
-
Bug Report
-
Feature Request
-
Successfully reproduced against the latest version of NW.js?
Please use our mailing list or Gitter chatroom to ask questions. The issue tracker is only for bugs and feature requests, in English only. Please note that issues without a repro or code snippet are less likely to be resolved.
Summary
The WriteImage function calls a Chromium function downstream that is vulnerable to a heap buffer overflow. (https://github.com/nwjs/nw.js/blob/nw88/src/api/nw_clipboard_api.cc#L249)
Details
The vulnerable Chromium function, JPEGCodec::Decode
in file jpeg_codec.cc is used downstream after the WriteImage
function is called. The JPEGCodec::Decode
function is used to decompress jpegs and create a raw bitmap version of the image.
In JPEGCodec::Decode
, the values for cinfo->output_width
and cinfo->output_height
are retrieved directly from a jpeg file's header.
cinfo->output_width
and cinfo->output_height
can be manipulated by editing the header of the jpeg file being processed. They are two bytes each in the image's header so their values can range from 0x0000 to 0xFFFF. These variables are multiplied with cinfo->output_components
which always has a value of 4 (Lines 249 & 254).
249: int row_read_stride = cinfo->output_width * cinfo->output_components;
250:
251: // Create memory for a decoded image and write decoded lines to the memory
252: // without conversions same as JPEGCodec::Encode().
253: int row_write_stride = row_read_stride;
254: output->resize(row_write_stride * cinfo->output_height);
When these three values are multiplied together they can exceed the limit of a 32-bit unsigned integer, leading to an integer overflow vulnerability. This product is used to resize the output vector, which will store the decompressed jpeg (Line 254). When the resize function's argument overflows, the vector becomes too small to store the decompressed data.
254: output->resize(row_write_stride * cinfo->output_height);
The program writes the decompressed image to the output vector using the jpeg_read_scanlines
function. The function ends up writing to out-of-bounds memory due to the vector's small size (Lines 257 & 258). This causes data in memory adjacent to the output vector to be overwritten.
256: for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) {
257: unsigned char* rowptr = &(*output)[row * row_write_stride];
258: if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1))
259: return false;
260: }
Impact
An NW.js program that implements the WriteImage function is vulnerable to a buffer overflow. An attacker is in control of the image's height, width, and contents. This allows an attacker to craft an exploit to overwrite data in the heap with data they control.
Relevant Chromium Update: https://chromium-review.googlesource.com/c/chromium/src/+/5402199
Hi thanks for the detailed write up. The fix for this is included in v125 and at the time of writing NW.js's latest is at 126.0.6478.57
.