Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

heap buffer overflow is found in movDemuxer::mov_read_trun #841

Closed
iwashiira opened this issue Mar 13, 2024 · 3 comments · Fixed by #851
Closed

heap buffer overflow is found in movDemuxer::mov_read_trun #841

iwashiira opened this issue Mar 13, 2024 · 3 comments · Fixed by #851

Comments

@iwashiira
Copy link
Contributor

iwashiira commented Mar 13, 2024

Our fuzzer found heap buffer overflow in movDemuxer. in the current master(75c9cb3).
PoC is here.

#include "bufferedReaderManager.h"
#include "vod_common.h"
#include "abstractDemuxer.h"
#include "movDemuxer.h"
#include <cstdint>
#include <fs/systemlog.h>

using namespace std;

BufferedReaderManager readManager(2, DEFAULT_FILE_BLOCK_SIZE, DEFAULT_FILE_BLOCK_SIZE + MAX_AV_PACKET_SIZE,
                                  DEFAULT_FILE_BLOCK_SIZE / 2);

int main(int argc, char* argv[]) {

    string fileName = argv[1];
    AbstractDemuxer* demuxer = new MovDemuxer(readManager);

    uint32_t fileBlockSize = demuxer->getFileBlockSize();
    demuxer->openFile(fileName);
    return 0;
}

Following is an output of ASAN.
vuln15.mov is in poc15.zip

=================================================================
==29495==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x615000000f00 at pc 0x55555596c667 bp 0x7fffffffd1e0 sp 0x7fffffffd1d0
WRITE of size 4 at 0x615000000f00 thread T0
    #0 0x55555596c666 in MovDemuxer::mov_read_trun(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x418666)
    #1 0x55555596a81b in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41681b)
    #2 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #3 0x55555596a4a3 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4164a3)
    #4 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #5 0x55555596ee65 in MovDemuxer::mov_read_moof(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41ae65)
    #6 0x55555596a615 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416615)
    #7 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #8 0x555555967635 in MovDemuxer::readHeaders() (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x413635)
    #9 0x55555596679a in MovDemuxer::openFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41279a)
    #10 0x5555558b6e45 in main (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x362e45)
    #11 0x7ffff6f70d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #12 0x7ffff6f70e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #13 0x5555557cc0d4 in _start (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x2780d4)

0x615000000f00 is located 0 bytes to the right of 512-byte region [0x615000000d00,0x615000000f00)
allocated by thread T0 here:
    #0 0x7ffff767a1e7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x55555599271f in __gnu_cxx::new_allocator<MOVStts>::allocate(unsigned long, void const*) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x43e71f)
    #2 0x55555598eb9a in std::allocator_traits<std::allocator<MOVStts> >::allocate(std::allocator<MOVStts>&, unsigned long) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x43ab9a)
    #3 0x55555598a67f in std::_Vector_base<MOVStts, std::allocator<MOVStts> >::_M_allocate(unsigned long) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x43667f)
    #4 0x555555984c59 in void std::vector<MOVStts, std::allocator<MOVStts> >::_M_realloc_insert<>(__gnu_cxx::__normal_iterator<MOVStts*, std::vector<MOVStts, std::allocator<MOVStts> > >) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x430c59)
    #5 0x55555597f506 in MOVStts& std::vector<MOVStts, std::allocator<MOVStts> >::emplace_back<>() (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x42b506)
    #6 0x55555596c5d4 in MovDemuxer::mov_read_trun(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4185d4)
    #7 0x55555596a81b in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41681b)
    #8 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #9 0x55555596a4a3 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4164a3)
    #10 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #11 0x55555596ee65 in MovDemuxer::mov_read_moof(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41ae65)
    #12 0x55555596a615 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416615)
    #13 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #14 0x5555559704fd in MovDemuxer::mov_read_stsd(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41c4fd)
    #15 0x55555596a6ce in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4166ce)
    #16 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #17 0x55555596a4a3 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4164a3)
    #18 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #19 0x55555596a4a3 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4164a3)
    #20 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #21 0x55555596a4a3 in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4164a3)
    #22 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #23 0x55555596d034 in MovDemuxer::mov_read_trak(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x419034)
    #24 0x55555596a7ac in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x4167ac)
    #25 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #26 0x55555596ebf3 in MovDemuxer::mov_read_moov(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41abf3)
    #27 0x55555596a63a in MovDemuxer::ParseTableEntry(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x41663a)
    #28 0x55555596aefb in MovDemuxer::mov_read_default(MovDemuxer::MOVAtom) (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x416efb)
    #29 0x555555967635 in MovDemuxer::readHeaders() (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x413635)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/vagrant/tsmuxer/for-build/build/tsMuxer/tsmuxer+0x418666) in MovDemuxer::mov_read_trun(MovDemuxer::MOVAtom)
Shadow bytes around the buggy address:
  0x0c2a7fff8190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff81a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff81b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff81c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff81d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2a7fff81e0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff81f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8210: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8220: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8230: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==29495==ABORTING

It is caused by two Bug

  • The first one is "buffer over-parse" in mov_read_stsd().

a.size = size - (m_processedBytes - start_pos);

a.size is a value calculated based on user input, but it does not take min with the size of stsd, that is, atom.size - ( m_processedBytes - start_pos).
When a.size is large, assuming a very large atom exists under stsd, all subsequent input would be subject to ParseEntryTable under mov_read_stsd.

  • The second one is ctts_count is not initialized with entries in mov_read_ctts.

tsMuxer/tsMuxer/movDemuxer.cpp

Lines 1189 to 1191 in 75c9cb3

const unsigned entries = get_be32();
st->ctts_data.resize(entries);
for (unsigned i = 0; i < entries; i++)

When calling in the order of mov_read_trun -> mov_read_ctts -> mov_read_trun, the vector of ctts_data is resized in mov_read_ctts but ctts_count remains, so a heap buffer overflow occurs at the following place in the second mov_read_trun.

tsMuxer/tsMuxer/movDemuxer.cpp

Lines 1100 to 1102 in 75c9cb3

sc->ctts_data.emplace_back();
sc->ctts_data[sc->ctts_count].count = 1;
sc->ctts_data[sc->ctts_count].duration = get_be32();

The first bug makes it easier for the second bug to ignite.

This heap overflow has the potential to destroy the heap to some extent at will.

Ricerca Security, Inc.

@iwashiira
Copy link
Contributor Author

This input seems to cause other memory leaks, so we will continue to investigate.

@iwashiira
Copy link
Contributor Author

The followingsizes also need to be compared with atom.size as well as first bug.

tsMuxer/tsMuxer/movDemuxer.cpp

Lines 1510 to 1519 in 75c9cb3

else if (st->type == IOContextTrackType::SUBTITLE)
{
const MOVAtom fake_atom(0, 0, size - (m_processedBytes - start_pos));
mov_read_glbl(fake_atom);
}
else
{
// other codec type, just skip (rtp, mp4s, tmcd ...)
skip_bytes(size - (m_processedBytes - start_pos));
}

@iwashiira
Copy link
Contributor Author

this heap buffer overflow is exploitable (reverse shell can be activated).

The ASAN of mov_read_trun has not completely disappeared, but at least the heap buffer overflow has been completely mitigated by PR #851 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant