Based on the vulnerability report from Dave we developed micropatches for heap buffer overflow CVE-2017-17969 and insufficient exception handling CVE-2018-5996 in 7-Zip version 16.04.
CVE-2017-17969
By diffing source code of the patched version 18.00 and the vulnerable version 16.04 we found the following changes related to CVE-2017-17969 (highlighted lines mark patched code).
001: // ShrinkDecoder.cpp 163: lastSym = sym; 164: unsigned cur = sym; 165: unsigned i = 0; 166: 167: while (cur >= 256) 168: { 169: _stack[i++] = _suffixes[cur]; 170: cur = _parents[cur]; 171: // don't change that code: 172: // Orphan Check and self-linked Orphan check (_stack overflow check); 173: if (cur == kEmpty || i >= kNumItems) 174: break; 175: } 176: 177: if (cur == kEmpty || i >= kNumItems) 178: break; 179:
We disassembled 7z.dll version 16.04 and found the above changes affect two offsets - 0x000a5ab4 and 0x000a5abb (both marked in red):
We then created micropatch code resembling source code lines 173 - 174 and 177 - 178 by using two patchlets, the first one extending (piggybacking on) an existing conditional statement (the end of the while loop - jnb loc_100A5A9C) and the second one introducing an additional conditional statement:
; CVE-2017-17969 patch for 7z.dll 16.04 MODULE_PATH "C:\0patch\Patches\7zip\16.4\7z.dll" PATCH_ID 316 PATCH_FORMAT_VER 2 VULN_ID 3295 PLATFORM win32 patchlet_start PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a5ab4 JUMPOVERBYTES 5 N_ORIGINALBYTES 5 ;piggybacking jnb ;3D 00 01 00 00 cmp eax, 100h ;73 CE jnb short loc_100AAF28 ;if (cur == kEmpty || i >= kNumItems) ; break; code_start cmp eax, 100h jne skip1 ;cur != kEmpty? STC ;set piggyback condition for jnb to NOT jump call PIT_ExploitBlocked jmp end skip1: cmp esi, 2000h jb skip2 ;i < kNumItems? STC ;set piggyback condition for jnb to NOT jump call PIT_ExploitBlocked jmp end skip2: cmp eax, 100h ;original code end: code_end patchlet_end patchlet_start PATCHLET_ID 2 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a5abb PIT 7z.dll!0xa5bc4 JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start cmp eax, 100h je block ; cur == kEmpty? cmp esi, 2000h jge block ; i >= kNumItems? jmp skip block: call PIT_ExploitBlocked jmp PIT_0xa5bc4 ; break skip: code_end patchlet_end
CVE-2018-5996
The source code patch for CVE-2018-5996 introduced the following changes (highlighted lines mark patched code):
001: // Rar3Decoder.h 194: bool m_IsSolid; 195: bool _errorMode;
001: // Rar3Decoder.cpp 089: CDecoder::CDecoder(): 090: _window(0), 091: _winPos(0), 092: _wrPtr(0), 093: _lzSize(0), 094: _writtenFileSize(0), 095: _vmData(0), 096: _vmCode(0), 097: m_IsSolid(false), 098: _errorMode(false) 099: { 100: Ppmd7_Construct(&_ppmd); 101: } 827: HRESULT CDecoder::CodeReal(ICompressProgressInfo *progress) 828: { 829: _writtenFileSize = 0; 830: _unsupportedFilter = false; 831: 832: if (!m_IsSolid) 833: { 834: _lzSize = 0; 835: _winPos = 0; 836: _wrPtr = 0; 837: for (int i = 0; i < kNumReps; i++) 838: _reps[i] = 0; 839: _lastLength = 0; 840: memset(m_LastLevels, 0, kTablesSizesSum); 841: TablesRead = false; 842: PpmEscChar = 2; 843: PpmError = true; 844: InitFilters(); 845: _errorMode = false; 846: } 847: 848: if (_errorMode) 849: return S_FALSE; 850: 851: if (!m_IsSolid || !TablesRead) 852: { 853: bool keepDecompressing; 854: RINOK(ReadTables(keepDecompressing)); 855: if (!keepDecompressing) 856: return S_OK; 857: } 890: return S_OK; 891: } 929: catch(const CInBufferException &e) { _errorMode = true; return e.ErrorCode;} 930: catch(...) { _errorMode = true; return S_FALSE; }
When developing a micropatch for this vulnerability, we had to patch the CDecoder class that had to be extended by a new member - the _errorMode variable. This was the first time we attempted something like that with 0patch. So we either had to make room for the new variable (increase the allocated object memory block) or find existing unused space within the object's memory layout. By searching for cross references to CDecoder member variables (in offset range around the m_IsSolid variable 1c6d - 1c7c) we found offsets 1C6Fh, 1C79h, 1C7Ah, 1C7Bh to be unused. Based on this we selected and assigned the first one of the available offsets - 1C6Fh - to the new _errorMode variable.
Our micropatch contains five patchlets that correspond to five code offsets in 7z.dll that had to be patched:
- Patchlet 1: We micropatched offset 0x000a0388 (the CDecoder::CDecoder constructor) with code resembling line 195 of RarDecoder.h and line 98 of Rar3Decoder.cpp.
- Patchlets 2 and 3: We micropatched offsets 0x000a25bf and 0x000a25de with code resembling lines 845 and 848-849.
- Patchlets 4 and 5: We micropatched offsets 0x000a22ee and 0x000a22f6 with code resembling changes to lines 929-930.
; CVE-2018-5996 patch for 7z.dll 16.04 MODULE_PATH "C:\0patch\Patches\7zip\16.4\7z.dll" PATCH_ID 315 PATCH_FORMAT_VER 2 VULN_ID 3296 PLATFORM win32 ; added variables: ; _errorMode (this+1C6Dh) ; patchlet_start PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a0388 JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start mov [esi+1C6Fh], bl ; _errorMode=0 //ebx set to 0 at offset a030a code_end patchlet_end patchlet_start PATCHLET_ID 2 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a22ee JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start mov [esi+1C6Fh], bl ; _errorMode=0 //ebx set to 0 at offset a2278 code_end patchlet_end patchlet_start PATCHLET_ID 3 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a22f6 PIT 7z.dll!0x000A2452 JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start cmp [esi+1C6Fh],bl ; _errorMode=0 //ebx set to 0 at offset a2278 jz skip call PIT_ExploitBlocked jmp PIT_0x000A2452 skip: code_end patchlet_end
patchlet_start PATCHLET_ID 4 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a25bf JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start mov eax, [ebp+08h] ; catch(const CInBufferException &e) { mov byte [eax+1C6Fh], 1 ; _errorMode = true; ; return e.ErrorCode;} code_end patchlet_end patchlet_start PATCHLET_ID 5 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000a25de JUMPOVERBYTES 0 N_ORIGINALBYTES 5 code_start mov eax, [ebp+08h] ; catch(...) { mov byte [eax+1C6Fh], 1 ; _errorMode = true; ; return S_FALSE; } code_end patchlet_end
If you're using some other version of 7-Zip and would like to have micropatches for it, please contact us at support@0patch.com.