Closed
Bug 1356650
Opened 8 years ago
Closed 6 years ago
Write beyond bounds caused by lack of validation of clipboard image header (and overflow) in nsImageFromClipboard ::GetEncodedImageStream()
Categories
(Core :: Widget: Win32, defect, P4)
Tracking
()
RESOLVED
DUPLICATE
of bug 1501482
People
(Reporter: q1, Unassigned)
References
Details
(Keywords: csectype-bounds, reporter-external, sec-moderate, Whiteboard: tpi:+)
Attachments
(2 files)
nsImageFromClipboard::GetEncodedImageStream() (widget\windows\nsImageClipboard.cpp) does not validate its input clipboard image header. Additionally, it does not check for overflow when computing a buffer size. These bugs can cause a read and/or write beyond bounds.
The bugs are between lines 230 and 235 (lack of validation of |bmiHeader.biWidth| and |bmiHeader.biHeight|), on line 235 (unguarded multiplication that can overflow), and on line 240 (lack of validation of |header->bmiHeader.biSize|):
219: nsresult
220: nsImageFromClipboard::GetEncodedImageStream (unsigned char * aClipboardData, const char * aMIMEFormat, nsIInputStream** aInputStream )
221: {
222: NS_ENSURE_ARG_POINTER (aInputStream);
223: NS_ENSURE_ARG_POINTER (aMIMEFormat);
224: nsresult rv;
225: *aInputStream = nullptr;
226:
227: // pull the size information out of the BITMAPINFO header and
228: // initialize the image
229: BITMAPINFO* header = (BITMAPINFO *) aClipboardData;
230: int32_t width = header->bmiHeader.biWidth;
231: int32_t height = header->bmiHeader.biHeight;
232: // neg. heights mean the Y axis is inverted and we don't handle that case
233: NS_ENSURE_TRUE(height > 0, NS_ERROR_FAILURE);
234:
235: unsigned char * rgbData = new unsigned char[width * height * 3 /* RGB */];
236:
237: if (rgbData) {
238: BYTE * pGlobal = (BYTE *) aClipboardData;
239: // Convert the clipboard image into RGB packed pixel data
240: rv = ConvertColorBitMap((unsigned char *) (pGlobal + header->bmiHeader.biSize), header, rgbData);
241: // if that succeeded, encode the bitmap as aMIMEFormat data. Don't return early or we risk leaking rgbData
242: if (NS_SUCCEEDED(rv)) {
243: nsAutoCString encoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type="));
244:
245: // Map image/jpg to image/jpeg (which is how the encoder is registered).
246: if (strcmp(aMIMEFormat, kJPGImageMime) == 0)
247: encoderCID.AppendLiteral("image/jpeg");
248: else
249: encoderCID.Append(aMIMEFormat);
250: nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get(), &rv);
251: if (NS_SUCCEEDED(rv)){
252: rv = encoder->InitFromData(rgbData, 0, width, height, 3 * width /* RGB * # pixels in a row */,
253: imgIEncoder::INPUT_FORMAT_RGB, EmptyString());
254: if (NS_SUCCEEDED(rv)) {
255: encoder.forget(aInputStream);
256: }
257: }
258: }
259: delete [] rgbData;
260: }
261: else
262: rv = NS_ERROR_OUT_OF_MEMORY;
263:
264: return rv;
265: } // GetImage
Attached is a POC that uses these bugs to cause line 235 to overflow, causing an underallocation and write beyond bounds of an attacker-specified amount of attacker-specified data.
Use the POC web page and code segment by creating a new Win32 (GUI-mode) project in Visual Studio. Replace the About() function with the POC code segment. Build the result. Start FF and attach the debugger to it, setting a BP on line 235, above. Load the POC web page. Now run the POC code and select help/about, then OK. Click on "Paste an image here!" and press ctrl/v. When the BP triggers, view the disassembly window to see the multiplication overflow, causing line 235 to allocate 0x8000 bytes for |rgbData| Then trace downstream into nsImageFromClipboard ::ConvertColorBitmap() and watch it write 0x28000 bytes beyond |rgbData|'s bounds.
The POC was tested on Win7 SP1 x64, but probably works on all Windows versions.
You can also cause the call chain beginning at line 252 (encoder->InitFromData(), above) to read beyond bounds by modifying the POC code to use the arguments:
GlobalAlloc(... + 0x3000);
...
|bmiHeader.biSizeImage| == 0x3000
|bmiHeader.biWidth| == 128
|bmiHeader.biHeight| == 128
With the debugger, you can trace control into nsPNGEncoder::InitFromData() with the bogus image height and width, thence into nsPNGEncoder::StartImageEncode(), and into png_set_IHDR() with the same bogus dimensions.
Eventually control ends up in nsPNGEncoder::AddImageFrame(), which then reads far beyond the end of the (0x3000-byte) |rgbData| buffer that GetEncodedImageStream() passed to it when it called InitFromData(); step line 298 in nsPNGEncoder::AddImageFrame() (image\encoders\png\nsPNGEncoder.cpp) to see this occur:
202: NS_IMETHODIMP
203: nsPNGEncoder::AddImageFrame(const uint8_t* aData,
204: uint32_t aLength, // (unused, req'd by JS)
205: uint32_t aWidth,
206: uint32_t aHeight,
207: uint32_t aStride,
208: uint32_t aInputFormat,
209: const nsAString& aFrameOptions)
210: {
...
294: } else if (aInputFormat == INPUT_FORMAT_RGB ||
295: aInputFormat == INPUT_FORMAT_RGBA) {
296: // simple RBG(A), no conversion needed
297: for (uint32_t y = 0; y < aHeight; y++) {
298: png_write_row(mPNG, (uint8_t*)&aData[y * aStride]);
299: }
Updated•8 years ago
|
Keywords: csectype-bounds,
sec-moderate
Updated•8 years ago
|
Group: core-security → dom-core-security
Updated•8 years ago
|
Flags: sec-bounty?
Updated•8 years ago
|
Flags: sec-bounty? → sec-bounty+
Updated•8 years ago
|
Priority: -- → P4
Whiteboard: tpi:+
Updated•7 years ago
|
Group: dom-core-security → layout-core-security
Updated•6 years ago
|
Group: layout-core-security → core-security-release
Comment 4•6 years ago
|
||
Bug 1501482 removed this code.
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → DUPLICATE
Updated•2 years ago
|
Group: core-security-release
Updated•1 year ago
|
Keywords: reporter-external
You need to log in
before you can comment on or make changes to this bug.
Description
•