CVE-2016-4332
HDF5 is a file format that is maintained by a non-profit organization, The HDF Group. HDF5 is designed to be used for storage and organization of large amounts of scientific data and is used to exchange data structures between applications in industries such as the GIS industry via libraries such as GDAL, OGR, or as part of software like ArcGIS.
The vulnerability exists due to the library’s failure to check if certain message types support a particular flag. When this flag is set, the library will cast the structure to an alternative structure and then assign to fields that aren’t supported by the message type. Due to the message type not being able to support this flag, the library will write outside the bounds of the heap buffer. This can lead to code execution under the context of the library.
hdf5-1.8.16.tar.bz2
tools/h5ls: Version 1.8.16
tools/h5stat: Version 1.8.16
tools/h5dump: Version 1.8.16
http://www.hdfgroup.org/HDF5/
http://www.hdfgroup.org/HDF5/release/obtainsrc.html
http://www.hdfgroup.org/ftp/HDF5/current/src/hdf5-1.8.16.tar.bz2
8.6 - CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
The HDF file format is intended to be a general file format that is self-describing for various types of data structures used in the scientific community [1]. These data structures are intended to be stored in two types of objects, Datasets and Groups. Paralleling the file-format to a file system, a Dataset can be interpreted as a file, and a Group can be interpreted as a directory that’s able to contain other Datasets or Groups. Associated with each entry, is metadata containing user-defined named attributes that can be used to describe the dataset.
Within the HDF file format, paths can be specified as the ‘/’-separated posix format. When iterating through the contents of a group, for each object the library will first populate an H5G_loc_t structure with information about the object’s location. Immediately afterwards, the library will fetch the metadata for the object using H5O_get_info. This is done by the following code located within src/H5O.c
src/H5O.c:3280
/* Find the object's location */
if(H5G_loc_find(&loc, obj_name, &obj_loc/*out*/, lapl_id, dxpl_id) < 0) //
XXX: assign location info about the object
HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "object not found")
loc_found = TRUE;
/* Get the object's info */
if(H5O_get_info(&obj_oloc, dxpl_id, TRUE, &oinfo) < 0) // XXX: get
metadata information about the object
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to get object info")
After reading the header of the object’s information into the “oh” variable, the library will use this information to store the object class. A little bit later, the library will use the version to determine how to process some of the attributes associated with the object. If the object’s version is H5O_VERSION_1, the library will then call H5O_msg_read_oh. This function will iterate through each of the message types in order to determine how to process them. The type that’s specified is H5O_MTIME_ID which gets passed to H5O_msg_read_oh.
src/H5O.c:2776
herr_t
H5O_get_info(const H5O_loc_t *loc, hid_t dxpl_id, hbool_t want_ih_info,
H5O_info_t *oinfo)
{
...
/* Get the object header */
if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC_READ))) // XXX: read
object header from file
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
...
if(NULL == (obj_class = H5O_obj_class_real(oh)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to determine object
class")
...
if(oh->version > H5O_VERSION_1) {
...
} /* end if */
else {
...
if((exists = H5O_msg_exists_oh(oh, H5O_MTIME_ID)) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "unable to check for MTIME
message")
if(exists > 0) {
/* Get "old style" modification time info */
if(NULL == H5O_msg_read_oh(loc->file, dxpl_id, oh, H5O_MTIME_ID,
&oinfo->ctime)) // XXX: call message decode
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't read MTIME
message")
Inside H5O_msg_read_oh, the application will use the type_id argument to determine which message type is being used for a message. This message type is used to determine which callback to use in order to handle the message. This process occurs within the macro H5O_LOAD_NATIVE at H5Omessage.c:545
src/H5Omessage.c:517
void *
H5O_msg_read_oh(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned type_id,
void *mesg)
{
const H5O_msg_class_t *type; /* Actual H5O class type for the ID */
unsigned idx; /* Message's index in object header */
void *ret_value = NULL;
...
for(idx = 0; idx < oh->nmesgs; idx++)
if(type == oh->mesg[idx].type)
break;
...
H5O_LOAD_NATIVE(f, dxpl_id, 0, oh, &(oh->mesg[idx]), NULL)
Inside the H5O_LOAD_NATIVE macro, the application will select a structure containing function pointers out of the msg->type field. This structure contains various functions that are used to decode the message. After calling the decode method, the library will check to see if the H5O_MSG_FLAG_SHAREABLE flag is set. If this flag is set then the macro H5O_UPDATE_SHARED is used to write into the pointer returned by the decode function.
src/H5Opkg.h:184
/* Load native information for a message, if it's not already present */
/* (Only works for messages with decode callback) */
#define H5O_LOAD_NATIVE(F, DXPL, IOF, OH, MSG, ERR) \
if(NULL == (MSG)->native) { \
const H5O_msg_class_t *msg_type = (MSG)->type; \
unsigned ioflags = (IOF); \
\
/* Decode the message */ \
HDassert(msg_type->decode); \
if(NULL == ((MSG)->native = (msg_type->decode)((F), (DXPL), (OH), (MSG)-
>flags, &ioflags, (MSG)->raw))) \
HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, ERR, "unable to decode
message") \
\
...
\
if((MSG)->flags & H5O_MSG_FLAG_SHAREABLE) { \
H5O_UPDATE_SHARED((H5O_shared_t *)(MSG)->native, H5O_SHARE_TYPE_HERE,
(F), msg_type->id, (MSG)->crt_idx, (OH)->chunk[0].addr) \
} /* end if */
\
Inside the decode function for the H5O_MTIME_ID structure, the application will make an allocation that is the size of a time_t field. This is located within src/H5Omtime.c:174.
src/H5Omtime.c:174
static void *
H5O_mtime_decode(H5F_t H5_ATTR_UNUSED *f, hid_t H5_ATTR_UNUSED dxpl_id, H5O_t
H5_ATTR_UNUSED *open_oh,
unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, const
uint8_t *p)
{
time_t *mesg, the_time;
int i;
struct tm tm;
void *ret_value = NULL; /* Return value */
...
if(NULL == (mesg = H5FL_MALLOC(time_t)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
*mesg = the_time;
/* Set return value */
ret_value = mesg;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5O_mtime_decode() */
After allocating space for the time_t structure, the application will return back to the H5O_LOAD_NATIVE macro. Once this is returned, the application will check to see if the flags have the H5O_MSG_FLAG_SHAREABLE bit set. If so, the application will mis-cast the pointer to the time_t structure to an H5O_shared_t, and then try to write to it using the H5O_UPDATE_SHARED macro.
src/H5Opkg.h:203
if((MSG)->flags & H5O_MSG_FLAG_SHAREABLE) { \
H5O_UPDATE_SHARED((H5O_shared_t *)(MSG)->native, H5O_SHARE_TYPE_HERE,
(F), msg_type->id, (MSG)->crt_idx, (OH)->chunk[0].addr) \
} /* end if */ \
\
src/H5Oprivate.h:114
#define H5O_UPDATE_SHARED(SH_MESG, SH_TYPE, F, MSG_TYPE, CRT_IDX, OH_ADDR) \
{ \
(SH_MESG)->type = (SH_TYPE); \
(SH_MESG)->file = (F); \
(SH_MESG)->msg_type_id = (MSG_TYPE); \
(SH_MESG)->u.loc.index = (CRT_IDX); \
(SH_MESG)->u.loc.oh_addr = (OH_ADDR); \
} /* end block */
Due to the H5O_shared_t being larger than the size of a time_t, the H5O_UPDATE_SHARED macro will write outside the bounds of the time_t structure that was allocated by H5O_mtime_decode. This will corrupt adjacent metadata in the heap, and can be used to corrupt more of the state of the application which can lead to code execution under the context of the application using the library. This H5O_shared_t structure is listed below.
src/H5Oprivate.h:230
typedef struct H5O_shared_t {
unsigned type; /* Type describing how message is shared
*/
H5F_t *file; /* File that message is located within */
unsigned msg_type_id; /* Message's type ID */
union {
H5O_mesg_loc_t loc; /* Object location info */
H5O_fheap_id_t heap_id; /* ID within the SOHM heap */
} u;
} H5O_shared_t;
This vulnerable pattern also occurs while decoding two other messages. These two messages are the H5O_MTIME_NEW_ID which calls H5O_mtime_new_decode, and H5O_STAB_ID which calls H5O_stab_decode. H5O_mtime_new_decode, in the following snippet, is also used to allocate a time_t structure that is smaller than an H5O_shared_t which can be used to trigger a similar style of overwrite.
src/H5Omtime.c:121
static void *
H5O_mtime_new_decode(H5F_t H5_ATTR_UNUSED *f, hid_t H5_ATTR_UNUSED dxpl_id, H5O_t
H5_ATTR_UNUSED *open_oh,
unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, const
uint8_t *p)
{
...
/* The return value */
if (NULL==(mesg = H5FL_MALLOC(time_t)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
*mesg = (time_t)tmp_time;
/* Set return value */
ret_value=mesg;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5O_mtime_new_decode() */
The other message, H5O_STAB_ID, which uses H5O_stab_decode uses the following H5O_stab_t structure. Due to the library mis-casting this structure to an H5O_shared_t, the library will write outside the bounds of the allocation of the H5O_stab_t.
src/H5Oprivate.h:531
typedef struct H5O_stab_t {
haddr_t btree_addr; /*address of B-tree */
haddr_t heap_addr; /*address of name heap */
} H5O_stab_t;
Similarly, the library will use H5FL_CALLOC(H5O_stab_t) to allocate space for the structure that gets overwritten.
src/H5Ostab.c:99
static void *
H5O_stab_decode(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, H5O_t H5_ATTR_UNUSED
*open_oh,
unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, const
uint8_t *p)
{
...
/* decode */
if(NULL == (stab = H5FL_CALLOC(H5O_stab_t)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
H5F_addr_decode(f, &p, &(stab->btree_addr));
H5F_addr_decode(f, &p, &(stab->heap_addr));
/* Set return value */
ret_value = stab;
These message IDs are located within src/H5Oprivate.h. H5O_MSG_FLAG_SHAREABLE is located
src/H5Oprivate.h:185
#define H5O_MTIME_ID 0x000e /* Modification time message. (Old) */
...
#define H5O_STAB_ID 0x0011 /* Symbol table message. */
#define H5O_MTIME_NEW_ID 0x0012 /* Modification time message. (New) */
src/H5Oprivate.h:70
/* Flags needed when encoding messages */
#define H5O_MSG_FLAG_CONSTANT 0x01u
#define H5O_MSG_FLAG_SHARED 0x02u
#define H5O_MSG_FLAG_DONTSHARE 0x04u
#define H5O_MSG_FLAG_FAIL_IF_UNKNOWN_AND_OPEN_FOR_WRITE 0x08u
#define H5O_MSG_FLAG_MARK_IF_UNKNOWN 0x10u
#define H5O_MSG_FLAG_WAS_UNKNOWN 0x20u
#define H5O_MSG_FLAG_SHAREABLE 0x40u
$ gdb -q --args bin/h5ls poc.hdf
(gdb) r
Starting program: bin/h5ls poc.hdf
Breakpoint 3, main (argc=0x2, argv=0x192f5376) at ../../../tools/h5ls/h5ls.c:2568
2568 {
(gdb) bp H5O_mtime_decode
Breakpoint 4 at 0x8175ebf: file ../../src/H5Omtime.c, line 177.
(gdb) c
Continuing.
cmpnd Type *ERROR*
Breakpoint 4, H5O_mtime_decode (f=0x8478498, dxpl_id=0xa000008,
open_oh=0x847adb8, mesg_flags=0x40, ioflags=0xbfffe168, p=0x847aeb0
"20110414214255") at ../../src/H5Omtime.c:177
177 {
(gdb) bp :246
Breakpoint 5 at 0x817615b: file ../../src/H5Omtime.c, line 246.
(gdb) c
Continuing.
Breakpoint 5, H5O_mtime_decode (f=0x8478498, dxpl_id=0xa000008,
open_oh=0x847adb8, mesg_flags=0x40, ioflags=0xbfffe168, p=0x847aeb0
"20110414214255") at ../../src/H5Omtime.c:246
246 if(NULL == (mesg = H5FL_MALLOC(time_t)))
(gdb) p sizeof(time_t)
$1 = 0x4
(gdb) n
248 *mesg = the_time;
(gdb) p mesg
$10 = (time_t *) 0x847bf88
(gdb) ba 0x847bf88+sizeof(time_t)
Hardware watchpoint 11: *(0x847bf88+sizeof(time_t))
(gdb) c
Continuing.
Hardware watchpoint 11: *(0x847bf88+sizeof(time_t))
Old value = 0x844ebf0
New value = 0x8478498
0x08172df0 in H5O_msg_read_oh (f=0x8478498, dxpl_id=0xa000008, oh=0x847adb8,
type_id=0xe, mesg=0xbfffe9a0) at ../../src/H5Omessage.c:545
545 H5O_LOAD_NATIVE(f, dxpl_id, 0, oh, &(oh->mesg[idx]), NULL)
(gdb) ub $pc L4
0x8172de7 <H5O_msg_read_oh+659>: mov 0x18(%eax),%eax
0x8172dea <H5O_msg_read_oh+662>: mov 0x8(%ebp),%edx
0x8172ded <H5O_msg_read_oh+665>: mov %edx,0x4(%eax) # XXX:
writes past the size of a time_t
=> 0x8172df0 <H5O_msg_read_oh+668>: mov 0x10(%ebp),%eax
(gdb) i r eax ebp
eax 0x847bf88 0x847bf88
ebp 0xbfffe198 0xbfffe198
(gdb) c
Continuing.
*** Error in `/home/vrt/build/hdf5-1.8.16-release/release/bin/h5ls': malloc():
memory corruption: 0x0847bf98 ***
Catchpoint 2 (signal SIGABRT), 0xb7ffecb0 in ?? ()
$ bin/h5ls poc.hdf
=================================================================
==30927==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb3d08b14 at pc 0xb6361e5b bp 0xbfda4258 sp 0xbfda4250
WRITE of size 4 at 0xb3d08b14 thread T0
#0 0xb6361e5a in H5O_msg_read_oh /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Omessage.c:545
#1 0xb60e96ee in H5O_get_info /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5O.c:2837
#2 0xb5cb2252 in H5G_loc_info_cb /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gloc.c:701
#3 0xb5d79bc4 in H5G_traverse_real /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gtraverse.c:640
#4 0xb5d74f1a in H5G_traverse /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gtraverse.c:860
#5 0xb5cb10de in H5G_loc_info /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gloc.c:746
#6 0xb60e336a in H5Oget_info_by_name /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5O.c:656
#7 0x81ec049 in traverse_cb /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:222
#8 0xb5c8e87a in H5G_iterate_cb /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gint.c:782
#9 0xb5ce9cd2 in H5G__node_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gnode.c:1026
#10 0xb54dcfe5 in H5B_iterate_helper /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5B.c:1175
#11 0xb54daa3b in H5B_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5B.c:1220
#12 0xb5d42a23 in H5G__stab_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gstab.c:565
#13 0xb5d0dd52 in H5G__obj_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gobj.c:707
#14 0xb5c8cd89 in H5G_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gint.c:843
#15 0xb60491f8 in H5Literate_by_name /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5L.c:1254
#16 0x81d87fc in traverse /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:315
#17 0x81e3735 in h5trav_visit /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:1164
#18 0x80de12b in visit_obj /home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/../../../tools/h5ls/h5ls.c:2390
#19 0x80d4e1f in main /home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/../../../tools/h5ls/h5ls.c:2880
#20 0xb5096a82 (/lib/i386-linux-gnu/libc.so.6+0x19a82)
#21 0x80ce814 in _start (/home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/.libs/lt-h5ls+0x80ce814)
0xb3d08b14 is located 0 bytes to the right of 4-byte region [0xb3d08b10,0xb3d08b14)
allocated by thread T0 here:
#0 0x80b7441 in malloc (/home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/.libs/lt-h5ls+0x80b7441)
#1 0xb60beeca in H5MM_malloc /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5MM.c:66
#2 0xb5b3744c in H5FL_malloc /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5FL.c:199
#3 0xb5b361c2 in H5FL_reg_malloc /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5FL.c:399
#4 0xb638d7c2 in H5O_mtime_decode /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Omtime.c:246
#5 0xb63610ec in H5O_msg_read_oh /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Omessage.c:545
#6 0xb60e96ee in H5O_get_info /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5O.c:2837
#7 0xb5cb2252 in H5G_loc_info_cb /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gloc.c:701
#8 0xb5d79bc4 in H5G_traverse_real /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gtraverse.c:640
#9 0xb5d74f1a in H5G_traverse /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gtraverse.c:860
#10 0xb5cb10de in H5G_loc_info /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gloc.c:746
#11 0xb60e336a in H5Oget_info_by_name /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5O.c:656
#12 0x81ec049 in traverse_cb /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:222
#13 0xb5c8e87a in H5G_iterate_cb /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gint.c:782
#14 0xb5ce9cd2 in H5G__node_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gnode.c:1026
#15 0xb54dcfe5 in H5B_iterate_helper /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5B.c:1175
#16 0xb54daa3b in H5B_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5B.c:1220
#17 0xb5d42a23 in H5G__stab_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gstab.c:565
#18 0xb5d0dd52 in H5G__obj_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gobj.c:707
#19 0xb5c8cd89 in H5G_iterate /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Gint.c:843
#20 0xb60491f8 in H5Literate_by_name /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5L.c:1254
#21 0x81d87fc in traverse /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:315
#22 0x81e3735 in h5trav_visit /home/vrt/build/hdf5-1.8.16/asan/tools/lib/../../../tools/lib/h5trav.c:1164
#23 0x80de12b in visit_obj /home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/../../../tools/h5ls/h5ls.c:2390
#24 0x80d4e1f in main /home/vrt/build/hdf5-1.8.16/asan/tools/h5ls/../../../tools/h5ls/h5ls.c:2880
#25 0xb5096a82 (/lib/i386-linux-gnu/libc.so.6+0x19a82)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/vrt/build/hdf5-1.8.16/asan/src/../../src/H5Omessage.c:545 H5O_msg_read_oh
Shadow bytes around the buggy address:
0x367a1110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x367a1120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x367a1130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x367a1140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x367a1150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x367a1160: fa fa[04]fa fa fa 00 fa fa fa 00 07 fa fa 00 04
0x367a1170: fa fa 00 04 fa fa 00 04 fa fa 00 04 fa fa 00 04
0x367a1180: fa fa fd fd fa fa 00 00 fa fa 00 00 fa fa 00 fa
0x367a1190: fa fa 00 01 fa fa 00 fa fa fa fd fd fa fa fd fa
0x367a11a0: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x367a11b0: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==30927==ABORTING
2016-05-08 - Discovery
2016-05-17 - Vendor Notification
2016-11-15 - Public Disclosure
[1] https://en.wikipedia.org/wiki/Hierarchical_Data_Format
[2] http://www.hdfgroup.org/HDF5/
Discovered by Cisco Talos