Null Pointer Dereference Caused Segmentation Fault in gpac/gpac
Reported on
Mar 26th 2022
Description
Null pointer dereference caused segmentation fault
Proof of Concept
version
./bin/gcc/MP4Box -version
MP4Box - GPAC version 2.1-DEV-rev65-g718843df4-master
(c) 2000-2022 Telecom Paris distributed under LGPL v2.1+ - http://gpac.io
Please cite our work in your research:
GPAC Filters: https://doi.org/10.1145/3339825.3394929
GPAC: https://doi.org/10.1145/1291233.1291452
GPAC Configuration: --enable-debug
Features: GPAC_CONFIG_LINUX GPAC_64_BITS GPAC_HAS_IPV6 GPAC_HAS_SSL GPAC_HAS_SOCK_UN GPAC_MINIMAL_ODF GPAC_HAS_QJS GPAC_HAS_PNG GPAC_HAS_LINUX_DVB GPAC_DISABLE_3D
Command: ./bin/gcc/MP4Box -xmt poc
Result: Segmentation fault
bt:
Starting program: /home/ubuntu/fuzz/gpac/bin/gcc/MP4Box -xmt ./poc
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[iso file] Unknown box type url@ in parent dref
[iso file] Unknown box type mp1Dv in parent stsd
[iso file] extra box maxr found in hinf, deleting
[iso file] Unknown box type t0E18 in parent trak
[iso file] extra box maxr found in hinf, deleting
[iso file] Unknown box type 80rak in parent moov
[iso file] Incomplete box mdat - start 11495 size 803701
[iso file] Incomplete file while reading for dump - aborting parsing
[iso file] Unknown box type url@ in parent dref
[iso file] Unknown box type mp1Dv in parent stsd
[iso file] extra box maxr found in hinf, deleting
[iso file] Unknown box type t0E18 in parent trak
[iso file] extra box maxr found in hinf, deleting
[iso file] Unknown box type 80rak in parent moov
[iso file] Incomplete box mdat - start 11495 size 803701
[iso file] Incomplete file while reading for dump - aborting parsing
MPEG-4 BIFS Scene Parsing
[ODF] Reading bifs config: shift in sizes (not supported)
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7240bc7 in BS_ReadByte (bs=0x5555557d29b0) at utils/bitstream.c:362
362 res = bs->original[bs->position++];
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x1
RDX 0x5555557d29b0 ◂— 0x0
RDI 0x5555557d29b0 ◂— 0x0
RSI 0x0
R8 0x0
R9 0x0
R10 0x9
R11 0x7ffff72aa158 (gf_node_get_graph) ◂— push rbp
R12 0x5555557d4890 —▸ 0x5555557d4460 ◂— 0x0
R13 0x5555557d4460 ◂— 0x0
R14 0x1
R15 0x0
RBP 0x7fffffff8730 —▸ 0x7fffffff8760 —▸ 0x7fffffff8790 —▸ 0x7fffffff87e0 —▸ 0x7fffffff8850 ◂— ...
RSP 0x7fffffff8700 ◂— 0x0
RIP 0x7ffff7240bc7 (BS_ReadByte+166) ◂— movzx eax, byte ptr [rax]
────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────
► 0x7ffff7240bc7 <BS_ReadByte+166> movzx eax, byte ptr [rax]
0x7ffff7240bca <BS_ReadByte+169> mov byte ptr [rbp - 0x13], al
0x7ffff7240bcd <BS_ReadByte+172> mov rax, qword ptr [rbp - 0x28]
0x7ffff7240bd1 <BS_ReadByte+176> mov eax, dword ptr [rax + 0x50]
0x7ffff7240bd4 <BS_ReadByte+179> test eax, eax
0x7ffff7240bd6 <BS_ReadByte+181> je BS_ReadByte+336 <BS_ReadByte+336>
↓
0x7ffff7240c71 <BS_ReadByte+336> movzx eax, byte ptr [rbp - 0x13]
0x7ffff7240c75 <BS_ReadByte+340> jmp BS_ReadByte+896 <BS_ReadByte+896>
↓
0x7ffff7240ea1 <BS_ReadByte+896> mov rcx, qword ptr [rbp - 8]
0x7ffff7240ea5 <BS_ReadByte+900> xor rcx, qword ptr fs:[0x28]
0x7ffff7240eae <BS_ReadByte+909> je BS_ReadByte+916 <BS_ReadByte+916>
─────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────
In file: /home/ubuntu/fuzz/gpac/src/utils/bitstream.c
357 if (bs->position >= bs->size) {
358 if (bs->EndOfStream) bs->EndOfStream(bs->par);
359 if (!bs->overflow_state) bs->overflow_state = 1;
360 return 0;
361 }
► 362 res = bs->original[bs->position++];
363
364 if (bs->remove_emul_prevention_byte) {
365 if ((bs->nb_zeros==2) && (res==0x03) && (bs->position<bs->size) && (bs->original[bs->position]<0x04)) {
366 bs->nb_zeros = 0;
367 res = bs->original[bs->position++];
─────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffff8700 ◂— 0x0
01:0008│ 0x7fffffff8708 —▸ 0x5555557d29b0 ◂— 0x0
02:0010│ 0x7fffffff8710 ◂— 0x0
... ↓ 2 skipped
05:0028│ 0x7fffffff8728 ◂— 0xe537f499f4c1e900
06:0030│ rbp 0x7fffffff8730 —▸ 0x7fffffff8760 —▸ 0x7fffffff8790 —▸ 0x7fffffff87e0 —▸ 0x7fffffff8850 ◂— ...
07:0038│ 0x7fffffff8738 —▸ 0x7ffff7240edb (gf_bs_read_bit+36) ◂— movzx edx, al
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
► f 0 0x7ffff7240bc7 BS_ReadByte+166
f 1 0x7ffff7240edb gf_bs_read_bit+36
f 2 0x7ffff7240f67 gf_bs_read_int+64
f 3 0x7ffff73d9f47 BM_ParseCommand+87
f 4 0x7ffff73da181 gf_bifs_flush_command_list+296
f 5 0x7ffff73da561 gf_bifs_decode_command_list+339
f 6 0x7ffff75c1189 gf_sm_load_run_isom+1987
f 7 0x7ffff75a3123 gf_sm_load_run+42
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> where
#0 0x00007ffff7240bc7 in BS_ReadByte (bs=0x5555557d29b0) at utils/bitstream.c:362
#1 0x00007ffff7240edb in gf_bs_read_bit (bs=0x5555557d29b0) at utils/bitstream.c:428
#2 0x00007ffff7240f67 in gf_bs_read_int (bs=0x5555557d29b0, nBits=1) at utils/bitstream.c:461
#3 0x00007ffff73d9f47 in BM_ParseCommand (codec=0x5555557def00, bs=0x5555557d29b0, com_list=0x5555557f3960) at bifs/memory_decoder.c:898
#4 0x00007ffff73da181 in gf_bifs_flush_command_list (codec=0x5555557def00) at bifs/memory_decoder.c:956
#5 0x00007ffff73da561 in gf_bifs_decode_command_list (codec=0x5555557def00, ESID=8, data=0x5555557df340 '\314' <repeats 102 times>, "̔\224\224\224\224\224\224\224", <incomplete sequence \341>, data_length=8208, com_list=0x5555557df2c0) at bifs/memory_decoder.c:1036
#6 0x00007ffff75c1189 in gf_sm_load_run_isom (load=0x7fffffff89c0) at scene_manager/loader_isom.c:303
#7 0x00007ffff75a3123 in gf_sm_load_run (load=0x7fffffff89c0) at scene_manager/scene_manager.c:719
#8 0x0000555555582280 in dump_isom_scene ()
#9 0x00005555555789de in mp4boxMain ()
#10 0x00007ffff6dcdbf7 in __libc_start_main (main=0x55555556c140 <main>, argc=3, argv=0x7fffffffe458, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe448) at ../csu/libc-start.c:310
#11 0x000055555556c17a in _start ()
After Debugging,I found
src/bifs/memory_decoder.c:953
GF_Err gf_bifs_flush_command_list(GF_BifsDecoder *codec)
if (cbi->cb->bufferSize) {
bs = gf_bs_new((char*)cbi->cb->buffer, cbi->cb->bufferSize, GF_BITSTREAM_READ);
gf_bs_set_eos_callback(bs, BM_EndOfStream, codec);
e = BM_ParseCommand(codec, bs, cbi->cb->commandList);
gf_bs_del(bs);
}
after called gf_bs_new
, sets bs->origin to null
then using BM_ParseCommand
function
src/bifs/memory_decoder.c:887
GF_Err BM_ParseCommand(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
u8 go, type;
GF_Err e;
go = 1;
e = GF_OK;
GF_SceneGraph *cur_graph = codec->current_graph;
GF_Proto *cur_proto = codec->pCurrentProto;
codec->LastError = GF_OK;
while (go) {
type = gf_bs_read_int(bs, 2);
switch (type) {
case 0:
e = BM_ParseInsert(codec, bs, com_list);
break;
case 1:
e = BM_ParseDelete(codec, bs, com_list);
break;
case 2:
e = BM_ParseReplace(codec, bs, com_list);
break;
case 3:
e = BM_SceneReplace(codec, bs, com_list);
break;
}
if (e) break;
go = gf_bs_read_int(bs, 1);
}
while (gf_list_count(codec->QPs)) {
gf_bifs_dec_qp_remove(codec, GF_TRUE);
}
codec->current_graph = cur_graph;
codec->pCurrentProto = cur_proto;
return e;
}
in type = gf_bs_read_int(bs, 2);
src/utils/bitstream.c
GF_EXPORT
u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits)
{
u32 ret;
bs->total_bits_read+= nBits;
#ifndef NO_OPTS
if (nBits + bs->nbBits <= 8) {
bs->nbBits += nBits;
ret = (bs->current >> (8 - bs->nbBits) ) & bits_mask[nBits];
return ret;
}
#endif
ret = 0;
while (nBits-- > 0) {
ret <<= 1;
ret |= gf_bs_read_bit(bs);
}
return ret;
}
In gf_bs_read_bit
using BS_ReadByte
function
GF_EXPORT
u8 gf_bs_read_bit(GF_BitStream *bs)
{
if (bs->nbBits == 8) {
bs->current = BS_ReadByte(bs);
bs->nbBits = 0;
}
#ifdef NO_OPTS
{
s32 ret;
bs->current <<= 1;
bs->nbBits++;
ret = (bs->current & 0x100) >> 8;
return (u8) ret;
}
#else
return (u8) (bs->current & bit_mask[bs->nbBits++]) ? 1 : 0;
#endif
}
In src/utils/bitstream.c BS_ReadByte
/*fetch a new byte in the bitstream switch between packets*/
static u8 BS_ReadByte(GF_BitStream *bs)
{
Bool is_eos;
if (bs->bsmode == GF_BITSTREAM_READ) {
u8 res;
if (bs->position >= bs->size) {
if (bs->EndOfStream) bs->EndOfStream(bs->par);
if (!bs->overflow_state) bs->overflow_state = 1;
return 0;
}
res = bs->original[bs->position++];
if (bs->remove_emul_prevention_byte) {
if ((bs->nb_zeros==2) && (res==0x03) && (bs->position<bs->size) && (bs->original[bs->position]<0x04)) {
bs->nb_zeros = 0;
res = bs->original[bs->position++];
}
if (!res) bs->nb_zeros++;
else bs->nb_zeros = 0;
}
return res;
}
if (bs->cache_write)
bs_flush_write_cache(bs);
is_eos = gf_feof(bs->stream);
/*we are in FILE mode, test for end of file*/
if (!is_eos || bs->cache_read) {
u8 res;
Bool loc_eos=GF_FALSE;
assert(bs->position<=bs->size);
bs->position++;
res = gf_bs_load_byte(bs, &loc_eos);
if (loc_eos) goto bs_eof;
if (bs->remove_emul_prevention_byte) {
if ((bs->nb_zeros==2) && (res==0x03) && (bs->position<bs->size)) {
u8 next = gf_bs_load_byte(bs, &loc_eos);
if (next < 0x04) {
bs->nb_zeros = 0;
res = next;
bs->position++;
} else {
gf_bs_seek(bs, bs->position);
}
}
if (!res) bs->nb_zeros++;
else bs->nb_zeros = 0;
}
return res;
}
bs_eof:
if (bs->EndOfStream) {
bs->EndOfStream(bs->par);
if (!bs->overflow_state) bs->overflow_state = 1;
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to overread bitstream\n"));
}
assert(bs->position <= 1+bs->size);
return 0;
}
res = bs->original[bs->position++];
Here using bs->original which is a null pointer, dereference this null pointer caused segmentation fault
it's very similar to https://huntr.dev/bounties/851942a4-1d64-4553-8fdc-9fccd167864b/
@maintainer @admin it seems like I forgot to write the Impact,this vulnerablility is capable of crashing software, so i think it's can described as Dos.may i have CVE assigned in this case?
Sure, we can assign a CVE here. We do require the permission of the maintainer before we proceed with this.
@maintainer - are you happy for us to assign and publish a CVE for this report?
Yes, please do whatever is the best practice.