Unknown
VLC zlib_decompress_extra Double Free Vulnerability
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
Unknown
(1 user assessed)Unknown
(1 user assessed)Unknown
Unknown
Unknown
VLC zlib_decompress_extra Double Free Vulnerability
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
An issue was discovered in zlib_decompress_extra in modules/demux/mkv/util.cpp in VideoLAN VLC media player 3.x through 3.0.7. The Matroska demuxer, while parsing a malformed MKV file type, has a double free.
Add Assessment
Technical Analysis
CVE-2019-12874 VLC zlib_decompress_extra Double Free Vulnerability
VLC media player is a free and open-source portable cross-platform media player software developed by the VideoLAN project. VLC is available for desktop operating systems and mobile platforms, such as Android, iOS, iPadOS, Wizen, Windows 10 Mobile, and Windows Phone. It is also available on digital distribution platforms such as Apple’s App Store, Google Play, and Microsoft Store. It supports many audio and video compression methods and file formats, and can be used to stream media over computer networks.
A vulnerability was found in VLC’s lib_decompress_extra
function, where the p_track
parameter is freed and later deleted, which results a double free condition.
Technical Details
The lib_decompress_extra
function can be found in modules/demux/mkv/util.cpp:
int32_t zlib_decompress_extra( demux_t * p_demux, mkv_track_t & tk ) { int result; z_stream d_stream; size_t n = 0; uint8_t * p_new_extra = NULL; msg_Dbg(p_demux,"Inflating private data"); d_stream.zalloc = Z_NULL; d_stream.zfree = Z_NULL; d_stream.opaque = Z_NULL; if( inflateInit( &d_stream ) != Z_OK ) { msg_Err( p_demux, "Couldn't initiate inflation ignore track %u", tk.i_number ); return 1; } d_stream.next_in = tk.p_extra_data; d_stream.avail_in = tk.i_extra_data; do { n++; void *alloc = realloc(p_new_extra, n*1024); if( alloc == NULL ) { msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u", tk.i_number ); free(p_new_extra); inflateEnd( &d_stream ); return 1; } p_new_extra = static_cast<uint8_t *>( alloc ); d_stream.next_out = &p_new_extra[(n - 1) * 1024]; d_stream.avail_out = 1024; result = inflate(&d_stream, Z_NO_FLUSH); if( result != Z_OK && result != Z_STREAM_END ) { msg_Err( p_demux, "Zlib decompression failed. Result: %d", result ); inflateEnd( &d_stream ); free(p_new_extra); return 1; } } while ( d_stream.avail_out == 0 && d_stream.avail_in != 0 && result != Z_STREAM_END ); free( tk.p_extra_data ); tk.i_extra_data = d_stream.total_out; p_new_extra = static_cast<uint8_t *>( realloc(p_new_extra, tk.i_extra_data) ); if( !p_new_extra ) { msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u", tk.i_number ); inflateEnd( &d_stream ); return 1; } tk.p_extra_data = p_new_extra; inflateEnd( &d_stream ); return 0; }
The specific buggy block of code is narrowed down as follows:
free( tk.p_extra_data ); tk.i_extra_data = d_stream.total_out; p_new_extra = static_cast<uint8_t *>( realloc(p_new_extra, tk.i_extra_data) ); if( !p_new_extra ) { msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u", tk.i_number ); inflateEnd( &d_stream ); return 1; }
As you can see, tk.p_extra_data
is freed:
free( tk.p_extra_data );
And then if for some reason, the static casting for p_new_extra
fails, the function returns with 1. It is hard to say exactly how a realloc failure would occur, typically this is due to out of memory, so that means tk.i_extra_data
would have to be something at least bigger than 0x7fffffff on a 32-bit VLC application.
After the free, the zlib_decompress_extra
function returns to a function called matroska_segment_c::ParseTrackEntry
(found in modules/demux/mkv/matroska_segment_parse.cpp):
void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m ) { ... if( p_track->i_compression_type == MATROSKA_COMPRESSION_ZLIB && p_track->i_encoding_scope & MATROSKA_ENCODING_SCOPE_PRIVATE && p_track->i_extra_data && p_track->p_extra_data && zlib_decompress_extra( &sys.demuxer, *p_track ) ) { msg_Err(&sys.demuxer, "Couldn't handle the track %u compression", p_track->i_number ); delete p_track; return; } ...
Since the zlib_decompress_extra
function returns 1 due to a failure from realloc, it is possible all those if conditions are true, which results the program printing an error message, and then the double free:
delete p_track;
Due to the failure requirement for realloc, an exploit is unlikely.
Patch Information
In the patch, tk_p_extra_data
is set to a null pointer, which is safe for delete
:
msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u", tk.i_number ); inflateEnd( &d_stream ); + tk.p_extra_data = NULL; return 1; } return 1;
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- videolan
Products
- vlc media player
References
Advisory
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: