by Luka Treiber, 0patch Team
Based on the vulnerability report from David Landave we developed micropatches for heap buffer overflow CVE-2017-17969 and use-after-free 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 [email protected].
Article Link: http://0patch.blogspot.com/2018/02/two-interesting-micropatches-for-7-zip.html