
गहन तकनीकी विश्लेषण CVE-2021-25804 का, VLC AVI पार्सर में एक कमजोरी। इसमें सुरक्षा शोधकर्ताओं के लिए मूल कारण, पैच अंतर और शोषण प्राइमिटिव शामिल हैं।
एक AVI फ़ाइल में कई ट्रैक हो सकते हैं। उदाहरण के लिए एक ऑडियो ट्रैक और एक वीडियो ट्रैक।
मनमानी संख्या में ट्रैक को पार्स करने के लिए VLC को पहले यह निर्धारित करना होगा कि कोई दिया गया ट्रैक समाप्त और प्ले करने योग्य है या नहीं। इस उद्देश्य के लिए दो वेरिएबल सेट किए गए हैं। दोनों का नाम i_track है।
हमारे उद्देश्यों के लिए वेरिएबल p_sys AVI फ़ाइल का प्रतिनिधित्व करता है। इस प्रकार "आधिकारिक" i_track वेरिएबल जो उन ट्रैक को संदर्भित करने के लिए उपयोग किया जाएगा जो समाप्त और प्ले करने योग्य निर्धारित किए गए हैं, p_sys->i_track है। इसे यहाँ एक मैक्रो के साथ प्रारंभिक किया गया है:
```
TAB_INIT(p_sys->i_track, p_sys->track);
//#define TAB_INIT( count, tab ) do {(count) = 0; (tab) = NULL; } while(0)
```
इस बीच, दूसरा i_track वेरिएबल सभी ट्रैक को गिनने के लिए सेट किया जाता है, भले ही वे समाप्त/प्ले करने योग्य हों या नहीं।
```
i_track = AVI_ChunkCount( p_hdrl, AVIFOURCC_strl, true );
```
दूसरा i_track 'strl' से शुरू होने वाले सभी चंक का प्रतिनिधित्व करता है। यह एक 'for' लूप को नियंत्रित करता है जो p_sys->i_track वेरिएबल को तभी सेट करता है जब वह श्रृंखला की जाँचों को पास करता है:
```
for( unsigned i = 0 ; i < i_track; i++ )
{
...
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
//#define TAB_APPEND( count, tab, p ) {TAB_APPEND_CAST( , count, tab, p )}
//TAB_APPEND_CAST( ,p_sys->i_track, p_sys->track, tk)
//if( (count) > 0 ) (tab) = cast realloc( tab, sizeof( *(tab) ) * ( (count) + 1 ) );
//else (tab) = cast malloc( sizeof( *(tab) ) );
//if( !(tab) ) abort();
//(tab)[count] = (p);
//(count)++; //p_sys->i_track incremented here
}
```
बाद के एक 'for' लूप के दौरान p_sys->i_track वेरिएबल का उपयोग AVI_ChunkFind को किये गए कॉल में चंक # के रूप में किया जाता है, जो दिए गए FOURCC चंक मान से मेल खाने वाले nth चंक को लौटाता है, हमारे मामले में 'strl':
```
for( unsigned i = 0 ; i < p_sys->i_track; i++ )
{
avi_track_t *tk = p_sys->track[i];
if( tk->fmt.i_cat != AUDIO_ES ||
tk->idx.i_size < 1 ||
tk->i_scale != 1 ||
tk->i_samplesize != 0 )
continue;
//CVE-2021-25804
avi_chunk_list_t *p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, i, true );
//subsequently changed to: avi_chunk_list_t *p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, tk->fmt.i_id, true );
avi_chunk_strf_t *p_strf = AVI_ChunkFind( p_strl, AVIFOURCC_strf, 0, false );
if( !p_strf || p_strf->i_cat != AUDIO_ES )
continue;
const WAVEFORMATEX *p_wf = p_strf->u.p_wf;
```
जबकि p_sys->i_track पूर्ण किए गए ट्रैक की सही संख्या गिनता है, यह इस बात का ट्रैक नहीं रखता कि इसे किस चंक संख्या पर सेट किया गया था। इसका अर्थ है कि यदि मान्य strl चंक से पहले कोई अमान्य strl चंक है, तो पहले चंक को ट्रैक ऑब्जेक्ट सेट करते समय सही ढंग से अनदेखा किया जाएगा, लेकिन जब ChunkFind को कॉल किया जाता है तो यह अभी भी पहले, अमान्य चंक का चयन करेगा।
CVE-2021-25804 बनने के बाद समस्या को ChunkFind चंक # वेरिएबल को i से tk->fmt.i_id में बदलकर पैच किया गया। tk->fmt.i_id पहले उस TAB_APPEND() कॉल के ठीक ऊपर सेट किया जाता है जिसे हमने पहले देखा:
```
tk->fmt.i_id = i;
if( p_strn && p_strn->p_str )
tk->fmt.psz_description = FromACP( p_strn->p_str );
//see es_format_truncated.c
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
```
ऐसा प्रतीत होता है कि यह समस्या को ठीक कर देता है क्योंकि tk->fmt.i_id i पर सेट है और इस प्रकार पूर्ण किया गया चंक वही होना चाहिए जो ChunkFind() द्वारा चुना जाए। हालांकि परीक्षण से पता चलता है कि वास्तव में ChunkFind() के अपराधी कॉल के समय tk->fmt.i_id का मान वास्तव में -1 पर सेट होता है। यह es_format_Init() के कॉल के दौरान सेट किया गया प्रारंभिककरण मान है।
```
void es_format_Init( es_format_t *fmt,
int i_cat, vlc_fourcc_t i_codec )
{
memset(fmt, 0, sizeof (*fmt));
fmt->i_cat = i_cat;
fmt->i_codec = i_codec;
fmt->i_profile = -1;
fmt->i_level = -1;
fmt->i_id = -1;
fmt->i_priority = ES_PRIORITY_SELECTABLE_MIN;
fmt->psz_language = NULL;
fmt->psz_description = NULL;
fmt->p_extra_languages = NULL;
if (fmt->i_cat == VIDEO_ES)
video_format_Init(&fmt->video, 0);
fmt->b_packetized = true;
fmt->p_extra = NULL;
}
```
यह प्रारंभिककरण उसी 'for' लूप में होता है जिसमें fmt->i_id को i पर सेट किया जाता है।
```
for( unsigned i = 0 ; i < i_track; i++ )
{
...
switch( p_strh->i_type )
{
case( AVIFOURCC_auds ):
{
es_format_Init( &tk->fmt, AUDIO_ES, 0 );
...
}
...
}
tk->fmt.i_id = i;
if( p_strn && p_strn->p_str )
tk->fmt.psz_description = FromACP( p_strn->p_str );
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
}
```