CVE-2016-3581
While parsing a specially crafted TIFF file, a parser confussion can lead to a heap buffer overflow resulting in out of bounds memory overwrite leading to arbitrary code execution.
Oracle Outside In IX sdk 8.5.1
http://www.oracle.com/technetwork/middleware/content-management/oit-all-085236.html
While parsing a specially crafted TIFF file with ExtraSamples tag present, a parser confusion can lead to insuficient heap memory allocation which later results in a buffer overflow corrupting the heap structures which can be abused to achieve arbitrary code execution. As per the file format documentation ExtraSamples influences how many actual bits per sample there are in the file.
Size of the allocation is based directly on on ImageWidth value (at offset 0x6c in the supplied
testcase) and is the allocation happens in the following basic block in libvs_tiff.so
(image base is
0xB74E1000):
`
.text:B74F3DE4 mov edx, [esp+11Ch+var_A8]
.text:B74F3DE8 mov eax, [edx+30h]
.text:B74F3DEB mov [ebp+4A8h], eax
.text:B74F3DF1 mov eax, [edx+30h] [1]
.text:B74F3DF4 mov [esp+11Ch+s], eax
.text:B74F3DF7 call _SYSNativeAlloc
`
At [1], the size of the allocation is read from a structure on the heap which comes from the file directly. In the supplied testcase (minimal.tiff) this will be 0x20 bytes, resulting in 0x28 byte chunk being allocated.
The overflow happens in the following code in libvs_tiff6.so
:
`
.text:B74EC5A8 add eax, [esp+26Ch+var_188]
.text:B74EC5AF movzx eax, byte ptr [eax+esi] [1]
.text:B74EC5B3 mov edx, [esp+26Ch+var_250]
.text:B74EC5B7 mov [edx+ecx], al [2]
.text:B74EC5BA
.text:B74EC5BA loc_B74EC5BA: ; CODE XREF: VwStreamRead+9F6j
.text:B74EC5BA mov ecx, [esp+26Ch+var_220]
.text:B74EC5BE movzx eax, word ptr [ecx+0D8h]
.text:B74EC5C5 add esi, eax [3]
.text:B74EC5C7 cmp [ecx+3CCh], esi [4]
.text:B74EC5CD jbe loc_B74EC09B
`
In the above code, at [1] a byte value is read from the heap and is written to a destination buffer
at [2]. At [2], edx
points at the begining of the previously allocated buffer (as discussed above)
and ecx
serves as offset, incremented by 1 in each iteration of the loop. This block of code is
executed in a bigger loop, the rest of which is ommited for brevity. At [3], esi
, which is a loop
counter (also an index into a buffer at [1]), is incremented by value in eax
which comes directly
from SamplesPerPixel value in the file (2 in the supplied minimal.tiff). Finally, at [4], the
esi
counter is compared to a max value in ecx+0x3CC
.
If the maximum value of the counter (ecx+0x3cc
at [4] above) is greater than the allocated
buffer a heap overflow can occur. Upper bound of this loop is calcualted in the following basic
block:
`
.text:B74E8AEF loc_B74E8AEF:
.text:B74E8AEF mov ecx, [esp+8Ch+arg_4]
.text:B74E8AF6 movzx eax, word ptr [ecx+60h] [1]
.text:B74E8AFA imul eax, [ecx+30h] [2]
.text:B74E8AFE movzx edx, word ptr [ecx+0D8h] [3]
.text:B74E8B05 imul eax, edx [4]
.text:B74E8B08 add eax, 7 [5]
.text:B74E8B0B shr eax, 3 [6]
.text:B74E8B0E mov [ecx+3CCh], eax [7]
.text:B74E8B14 cmp word ptr [ecx+0F8h], 0
.text:B74E8B1C jnz loc_B74
`
At [1], the initial value is comes from BitsPerSample value from the file
and is multiplied by a value coming from ImageWidth at [2]. At [3], a
SamplesPerPixel value is retrieved and is multiplied with previous result at [4].
After 7 is added at [5], the final value is divided by 8 at [6]. The final value
is written into heap structure pointed to by ecx
at offset 0x3CC at [7].
In short, the upper bound for the loop in which the overflow happens is:
`
max = (( BitsPerSample * ImageWidth * SamplesPerPixel ) + 7) >> 3
`
In the supplied testcase, the upper bound for the loop is 0x80, and since increment in each iteration is 2 this leads to a buffer overflow of 0x40 bytes where only 0x28 were allocated, leading to an application crash.
In summary, both the size of the allocation and size of the overwrite are under direct control.
The supplied testcase PoC.tiff
demonstrates a more controled buffer overwrite where adjecent
heap chunks are overwriten with ASCII A’s resulting in the following crash:
`
Breakpoint 4, 0xb74ec5ba in VwStreamRead () from /home/ea/oit_office/sdk/demo/libvs_tif6.so
edx 0x80d8920 135104800
ecx 0x0 0
esi 0x0 0
al 0x4b 75
Quit
(gdb) disable
(gdb) c
Continuing.
Export successful: 1 output file(s) created.
*** Error in `/home/ea/oit_office/sdk/demo/ixsample': double free or corruption (out): 0x080d8948 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x6ce09)[0xb7e20e09]
/usr/lib/libc.so.6(+0x74406)[0xb7e28406]
/usr/lib/libc.so.6(cfree+0x56)[0xb7e2c676]
/home/ea/oit_office/sdk/demo/libwv_core.so(SYSNativeFree+0x1f)[0xb7a507e7]
...
Program received signal SIGABRT, Aborted.
0xb7fdbbc0 in __kernel_vsyscall ()
(gdb) x/x 0x80d8920
0x80d8920: 0xaaaa41aa
(gdb) x/x 0x80d8920-4
0x80d891c: 0x00000029 <- size of the overflown buffer
(gdb) x/x 0x080d8948
0x80d8948: 0x41414141
(gdb) x/x 0x080d8948-4
0x80d8944: 0x41414141 <- size corrupted
(gdb)
`
Breakpoint 4 is placed in the loop where the overwrite happens and here shows the initial values.
The pointer to the allocated memory is 0x80d8920
. After the crash, by examining
the memory around it, we can see that the adjecent heap chunk has been overwriten.
The essential difference between the original TIFF file and the supplied minimal testcase is that the minimal testcase contains the ExtraSamples tag, which influences the calculations, as well as the different values for both BitsPerSample and SamplesPerPixel values.
This issue can be triggered by running the supplied testcase in the ixsample
application
supplied with the SDK.
2016-04-12 - Vendor Notification
2016-07-19 – Public Disclosure
Discovered by Aleksandar Nikolic of Cisco Talos.