============================================================ # Security vulnerabilities in Wine when handling EMF files # ============================================================ When processing the EMF file format, there are many missing checks on user supplied EMF records from EMF files, which result in out-of-bounds reads, out-of-bounds writes and heap-buffer overflow vulnerabilities. This can result in arbitrary execution of attacker code when the vulnerabilities are weaponized. The victim user needs to open a malicious EMF file in a picture viewer such as STDUviewer (http://www.stdutility.com/stduviewer.html => http://www.stdutility.com/download/stduviewer.zip) to trigger the issues. * All issues were tested on an up-to-date Ubuntu 17.10 and Wine 2.0.2 (17. Apr. 2018): $ cat /etc/issue Ubuntu 17.10 \n \l $ uname -a Linux test 4.13.0-38-generic #43-Ubuntu SMP Wed Mar 14 15:20:44 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux $ wine --version wine-2.0.2 (Ubuntu 2.0.2-2ubuntu1) $ lsb_release -rd Description: Ubuntu 17.10 Release: 17.10 $ apt-cache policy wine-stable wine-stable: Installed: 2.0.2-2ubuntu1 Candidate: 2.0.2-2ubuntu1 Version table: *** 2.0.2-2ubuntu1 500 500 http://de.archive.ubuntu.com/ubuntu artful/universe amd64 Packages 500 http://de.archive.ubuntu.com/ubuntu artful/universe i386 Packages 100 /var/lib/dpkg/status * Instead of using STDUviewer, the attached gdiplus_trigger.cpp can be used to trigger the bugs: // compile it $ x86_64-w64-mingw32-g++ gdiplus_trigger.cpp -o gdiplus_trigger.exe -lgdiplus -fno-exceptions -fno-rtti -municode -Wall // trigger one of the bugs: $ wine gdiplus_trigger.exe /path/to/emf/file.emf * below source is from https://source.winehq.org/git/wine.git/ 9 Apr. 2018, last commit: dd2624a24fba400bf59b9396e496d16c43d399d9) ## Example vulnerabilities: (1) Out-of-bounds read in dlls/gdiplus/metafile.c:2528: The parameters data, dataSize and recordType are completely user controlled and flow into a memcpy() call in line 2528. If the length of data is less than dataSize, then this results in an out-of-bounds read. 2461 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile, 2462 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data) 2463 { .... 2471 2472 if (recordType >= 1 && recordType <= 0x7a) 2473 { 2474 /* regular EMF record */ 2475 if (metafile->playback_dc) 2476 { 2477 switch (recordType) 2478 { .... 2520 default: 2521 { 2522 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8); // <== dataSize is completely user controlled 2523 2524 if (record) 2525 { 2526 record->iType = recordType; 2527 record->nSize = dataSize + 8; 2528 memcpy(record->dParm, data, dataSize); // <== data is user controlled, memcpy performs OOB read 2529 2530 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table, 2531 record, metafile->handle_count) == 0) // (record is user controlled => source of many vulnerabilities) 2532 ERR("PlayEnhMetaFileRecord failed\n"); The file out_of_bounds_read.emf demostrates the issue. Open it in STDUViewerApp.exe or gdiplus_trigger.exe. ============================================================================== ============================================================================== (2) Heap-buffer overflow in dlls/gdi32/enhmetafile.c:1950: If the mr parameter points to a user controlled EMF record in an EMF file, then the structure pAlphaBlend at line 1931 is completely user controlled. This leads to a heap-buffer overflow due to a memcpy call in line 1950, after a successfull bits buffer has been allocated in the function CreateDIBSection() at line 1949. The allocation size of bits (coming from pbi->bmiHeader.biSizeImage) is also user controlled. 749 BOOL WINAPI PlayEnhMetaFileRecord( 750 HDC hdc, /* [in] device context in which to render EMF record */ 751 LPHANDLETABLE handletable, /* [in] array of handles to be used in rendering record */ 752 const ENHMETARECORD *mr, /* [in] EMF record to render */ 753 UINT handles /* [in] size of handle array */ 754 ) 755 { 756 int type; ... 764 type = mr->iType; ... 767 switch(type) 768 { ... 1929 case EMR_ALPHABLEND: 1930 { 1931 const EMRALPHABLEND *pAlphaBlend = (const EMRALPHABLEND *)mr; // <== mr is user controlled .... 1938 if(pAlphaBlend->offBmiSrc == 0) { 1939 FIXME("EMR_ALPHABLEND: offBmiSrc == 0\n"); 1940 } else { .... 1943 const BITMAPINFO *pbi = (const BITMAPINFO *)((const BYTE *)mr + pAlphaBlend->offBmiSrc); 1944 void *bits; .... 1949 hBmp = CreateDIBSection(hdc, pbi, pAlphaBlend->iUsageSrc, &bits, NULL, 0); // <= allocate bits buffer in CreateDIBSection 1950 memcpy(bits, (const BYTE *)mr + pAlphaBlend->offBitsSrc, pAlphaBlend->cbBitsSrc); // <= heap buffer overflow: mr, offBitsSrc and cbBitsSrc fields are user controlled. The file heap_buffer_overflow.emf demostrates the issue. ============================================================================== ============================================================================== (3) Out-of-bounds write in dlls/gdi32/enhmetafile.c:951 Again, mr is an EMF record from an attacker controlled file. Hence pCreatePen in line 950 is fully controlled and allows an almost arbitary write at line 951. 749 BOOL WINAPI PlayEnhMetaFileRecord( 750 HDC hdc, /* [in] device context in which to render EMF record */ 751 LPHANDLETABLE handletable, /* [in] array of handles to be used in rendering record */ 752 const ENHMETARECORD *mr, /* [in] EMF record to render */ 753 UINT handles /* [in] size of handle array */ 754 ) 755 { 756 int type; ... 764 type = mr->iType; ... 767 switch(type) 768 { ... 948 case EMR_CREATEPEN: 949 { 950 const EMRCREATEPEN *pCreatePen = (const EMRCREATEPEN *)mr; 951 (handletable->objectHandle)[pCreatePen->ihPen] = // <= full control over array index (pCreatePen->ihPen) 952 CreatePenIndirect(&pCreatePen->lopn); 953 break; The file out_of_bounds_write.emf demostrates the issue. ============================================================================== ============================================================================== There are many more such vulnerabilities, especially in PlayEnhMetaFileRecord() with different record types. Functions which are called by PlayMetaFileRecord() to process EMF record data further, contain vulnerabilities, too. These bugs were found with the help of a modified version of the kAFL fuzzer (https://github.com/RUB-SysSec/kAFL) If anything is unclear of if you need more information, please let me know! Best regards, Robert Gawlik (Ruhr-Universität Bochum)