nwjs/nw.js

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.

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.