
Análisis técnico de CVE-2021-25801 en el analizador AVI de VLC, que demuestra una lectura fuera de los límites mediante fragmentos de subíndice manipulados y proporciona una prueba de concepto.
La vulnerabilidad se origina por una falla al verificar a fondo el tipo de chunk que se pasa a __Parse_indx al intentar leer un sub chunk indx, al que apunta un super chunk indx válido.
else if( p_indx->i_indextype == AVI_INDEX_OF_INDEXES ) //este es el valor esperado para el super índice
{
if ( !p_sys->b_seekable )
return;
avi_chunk_t ck_sub;
for( unsigned i = 0; i < p_indx->i_entriesinuse; i++ )
{
if( vlc_stream_Seek( p_demux->s,
p_indx->idx.super[i].i_offset ) ||
//mientras el chunk no sea nulo y el fourcc no sea 0 y queden al menos 7 bytes en el archivo, ChunkRead devolverá un valor
AVI_ChunkRead( p_demux->s, &ck_sub, NULL ) )
{
break;
}
//CVE-2021-25801
//el super índice apunta a un offset con el 13.º byte establecido en 0x01
//pero no se realiza ninguna comprobación sobre si realmente apunta a un chunk de campo indx válido
if( ck_sub.indx.i_indextype == AVI_INDEX_OF_CHUNKS )
__Parse_indx( p_demux, &p_index[i_stream], pi_last_offset, &ck_sub.indx );
AVI_ChunkClean( p_demux->s, &ck_sub );
}
El tipo de chunk del sub índice se determina mediante la función AVI_ChunkRead.
int AVI_ChunkRead( stream_t *s, avi_chunk_t *p_chk, avi_chunk_t *p_father )
{
int i_index;
if( !p_chk )
{
msg_Warn( (vlc_object_t*)s, "cannot read null chunk" );
return VLC_EGENERIC;
}
if( AVI_ChunkReadCommon( s, p_chk, p_father ) )
return VLC_EGENERIC;
if( p_chk->common.i_chunk_fourcc == VLC_FOURCC( 0, 0, 0, 0 ) )
{
msg_Warn( (vlc_object_t*)s, "found null fourcc chunk (corrupted file?)" );
return AVI_ZERO_FOURCC;
}
p_chk->common.p_father = p_father;
//Esta función lee el valor fourcc y lo compara contra una vtable para la función ChunkRead correspondiente
i_index = AVI_ChunkFunctionFind( p_chk->common.i_chunk_fourcc );
if( AVI_Chunk_Function[i_index].AVI_ChunkRead_function )
{
return AVI_Chunk_Function[i_index].AVI_ChunkRead_function( s, p_chk );
}
else if( ( ((char*)&p_chk->common.i_chunk_fourcc)[0] == 'i' &&
((char*)&p_chk->common.i_chunk_fourcc)[1] == 'x' ) ||
( ((char*)&p_chk->common.i_chunk_fourcc)[2] == 'i' &&
((char*)&p_chk->common.i_chunk_fourcc)[3] == 'x' ) )
{
p_chk->common.i_chunk_fourcc = AVIFOURCC_indx;
return AVI_ChunkRead_indx( s, p_chk );
}
msg_Warn( (vlc_object_t*)s, "unknown chunk: %4.4s (not loaded)",
(char*)&p_chk->common.i_chunk_fourcc );
return AVI_NextChunk( s, p_chk );
}
Si el super índice apunta a un chunk válido de un tipo más pequeño que el chunk indx esperado, podemos provocar una lectura fuera de los límites cuando se asignan las siguientes variables en __Parse_indx.
for( unsigned i = 0; i < p_indx->i_entriesinuse; i++ )
{
index.i_id = p_indx->i_id;
index.i_flags = p_indx->idx.field[i].i_size & 0x80000000 ? 0 : AVIIF_KEYFRAME;
index.i_pos = p_indx->i_baseoffset + p_indx->idx.field[i].i_offset - 8; //<-Aquí se produce la violación de acceso
index.i_length = p_indx->idx.field[i].i_size;
index.i_lengthtotal = index.i_length;
avi_index_Append( p_index, pi_max_offset, &index );
}
Utilizando un chunk fourcc strh de 0x34 bytes, pude pasar con éxito todas las comprobaciones previas necesarias para llegar al código vulnerable anterior, lo que resultó en una violación de acceso.
avi stream debug: <list 'AVI '>
avi stream debug: <list 'hdrl'>
avi stream debug: <list 'strl'>
avi stream warning: chunk LIST does not fit into parent 4060
avi stream debug: </list 'strl'>ffffffff
avi stream warning: chunk LIST does not fit into parent 4060
avi stream debug: </list 'hdrl'>ffffffff
avi stream debug: skipping movi chunk
avi stream debug: no more data at 4128
avi stream debug: </list 'AVI '>ffffffff
avi stream debug: no more data at 4128
avi stream debug: * LIST-root size:4128 pos:0
avi stream debug: + RIFF-AVI size:4124 pos:0
avi stream debug: | + LIST-hdrl size:4040 pos:12
avi stream debug: | | + avih size:56 pos:24
avi stream debug: | | + LIST-strl size:3964 pos:88
avi stream debug: | | | + strh size:56 pos:100
avi stream debug: | | | + strf size:44 pos:164
avi stream debug: | | | + indx size:3832 pos:216
avi stream debug: | + LIST-movi size:64 pos:4056
avi demux debug: AVIH: 1 stream, flags HAS_INDEX IS_INTERLEAVED TRUST_CKTYPE
avi demux debug: stream[0] rate:419430400 scale:16777216 samplesize:0
avi demux debug: stream[0] video(XVID) 640x360 0bpp 25.000000fps
main input debug: selecting program id=0
avi demux debug: loading subindex(0x1) 1870082273 entries
(7fc.9d4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0a77f838 ebx=0029df9f ecx=4f4e4d4c edx=53525150 esi=091f7000 edi=53525150
eip=6f772c43 esp=0a77f750 ebp=0a77f870 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010216
libavi_plugin+0x2c43:
6f772c43 8b7e04 mov edi,dword ptr [esi+4] ds:002b:091f7004=????????
El PoC proporcionado fue probado en Windows 11, con la versión 3.0.11 de VLC.