An OOM was found in SkBmpRLECodec constructor of SkBmpRLECodec.cpp, allows attackers to cause a denial of service via a craft bmp file with a very large RLE size field which is more bigger than the real size of the file.

  • CVE ID: CVE-2017-0548;
  • Android ID: A-33251605;
  • Severity: High;
  • Updated Google devices: All;
  • Updated AOSP versions: 7.0, 7.1.1.

Description

https://android.googlesource.com/platform/external/skia/+/d1fb426850d72867298383bf029862acc9d52598/src/codec/SkBmpCodec.cpp#121

1
2
3
4
5
6
totalBytes = get_int(hBuffer.get(), 2);
offset = get_int(hBuffer.get(), 10);
if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
SkCodecPrintf("Error: invalid starting location for pixel data\n");
return false;
}

While proceeding a bmp file, the SkBmpCodec::ReadHeader function use totalBytes value and offset value from the file on line 121-122 without a vaild checking. It means that attackers can fully control the two value. In this post, 0xffffffff are passed to totalBytes and 0x0 are passed to offset.

https://android.googlesource.com/platform/external/skia/+/d1fb426850d72867298383bf029862acc9d52598/src/codec/SkBmpCodec.cpp#458

1
2
3
4
5
if (totalBytes <= offset && kRLE_BmpInputFormat == inputFormat) {
SkCodecPrintf("Error: RLE requires valid input size.\n");
return false;
}
const size_t RLEBytes = totalBytes - offset;

The (totalBytes - offset) calculation is 0xffffffff(0xffffffff - 0x0), so the value of RLEBytes is a very large number, 0xffffffff. Then the RLEBytes value is passed to SkBmpRLECodec constructor on line 521.

https://android.googlesource.com/platform/external/skia/+/d1fb426850d72867298383bf029862acc9d52598/src/codec/SkBmpCodec.cpp#521

1
2
*codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, numColors,
bytesPerColor, offset - bytesRead, rowOrder, RLEBytes);

https://android.googlesource.com/platform/external/skia/+/d1fb426850d72867298383bf029862acc9d52598/src/codec/SkBmpRLECodec.cpp#27

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
uint16_t bitsPerPixel, uint32_t numColors,
uint32_t bytesPerColor, uint32_t offset,
SkCodec::SkScanlineOrder rowOrder,
size_t RLEBytes)
: INHERITED(info, stream, bitsPerPixel, rowOrder)
, fColorTable(nullptr)
, fNumColors(numColors)
, fBytesPerColor(bytesPerColor)
, fOffset(offset)
, fStreamBuffer(new uint8_t[RLEBytes])
, fRLEBytes(RLEBytes)
, fOrigRLEBytes(RLEBytes)
, fCurrRLEByte(0)
, fSampleX(1)
{}

Finally a large memory allocation is triggerd on line 27.

Attack vector

You can click here to get the poc.

The easily way to trigger this vulnerability is as follows.

1
BitmapFactory.decodeFile("CVE-2017-0548.bmp");

Patch

Fix the RLE size value.

https://android.googlesource.com/platform/external/skia/+/318e3505ac2436c62ec19fd27ebe9f8e7d174544

Acknowledgement

This vulnerability was credited to Zinuo Han from Chengdu Security Response Center of Qihoo 360 Technology Co. Ltd.

Timeline

2016-11-29: ele7enxxh reported the vulnerability to Google;
2017-01-12: Google rated it as a High vulnerability;
2017-03-01: Google assigned CVE-2017-0548 for this vulnerability;
2017-04-03: Google released the patch and disclosed the details of CVE-2017-0548 on Android Security Bulletin-April 2017.