Skip to content

Bug: Heap-based Buffer Overflow in Assimp::MD3Importer::ValidateSurfaceHeaderOffsets #6070

@sae-as-me

Description

@sae-as-me

Affected Projects

assimp v5.4.3 (https://github.com/assimp/assimp)

Problem Type

CWE-122: Heap-based Buffer Overflow

Description

Summary

A heap-buffer-overflow vulnerability was discovered in the Assimp::MD3Importer::ValidateSurfaceHeaderOffsets function within the Assimp Library. This issue occurs when processing certain malformed files, leading to an out-of-bounds read and potential application crash.

Details

The vulnerability arises in the Assimp::MD3Importer::ValidateSurfaceHeaderOffsets function defined in code/AssetLib/MD3/MD3Loader.cpp at line 397. The function seems to lack judgment on pointer validity, resulting in the use of wild pointers (according to ASAN report).

void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface *pcSurf) {
    // Calculate the relative offset of the surface
    const int32_t ofs = int32_t((const unsigned char *)pcSurf - this->mBuffer);

    // Check whether all data chunks are inside the valid range
    if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize ||    //heap-buffer-overflow
            pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
            pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
            pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) {

        throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
    }

    // Check whether all requirements for Q3 files are met. We don't
    // care, but probably someone does.
    if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) {
        ASSIMP_LOG_WARN("MD3: Quake III triangle limit exceeded");
    }

    if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) {
        ASSIMP_LOG_WARN("MD3: Quake III shader limit exceeded");
    }

    if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) {
        ASSIMP_LOG_WARN("MD3: Quake III vertex limit exceeded");
    }

    if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) {
        ASSIMP_LOG_WARN("MD3: Quake III frame limit exceeded");
    }
}

PoC

Steps to reproduce:

  1. Clone the Assimp repository and build it using the following commands :
export CC='clang'
export CXX='clang++'
export CFLAGS='-fsanitize=address -O0 -g'
export CXXFLAGS='-fsanitize=address -O0 -g'
export LIB_FUZZING_ENGINE='-fsanitize=fuzzer'

cmake CMakeLists.txt -G "Ninja" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_ZLIB=ON \
                    -DASSIMP_BUILD_TESTS=OFF -DASSIMP_BUILD_ASSIMP_TOOLS=OFF \
                    -DASSIMP_BUILD_SAMPLES=OFF
cmake --build .
  1. Compile the fuzzer:
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE -std=c++11 -I$SRC/assimp/include \
		fuzz/assimp_fuzzer.cc -o $OUT/assimp_fuzzer  \
		./lib/libassimp.a ./contrib/zlib/libzlibstatic.a
  1. Run the fuzzer to trigger the segmentation fault:

Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo.zip

./assimp_fuzzer ./Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo

Report

Running: ./Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo
=================================================================
==2372==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61300000279c at pc 0x5f2b6bd70119 bp 0x7ffdd7941530 sp 0x7ffdd7941528
READ of size 4 at 0x61300000279c thread T0
    #0 0x5f2b6bd70118 in Assimp::MD3Importer::ValidateSurfaceHeaderOffsets(Assimp::MD3::Surface const*) /fuzz/project/assimp/code/AssetLib/MD3/MD3Loader.cpp:397:17
    #1 0x5f2b6bd7732a in Assimp::MD3Importer::InternReadFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, aiScene*, Assimp::IOSystem*) /fuzz/project/assimp/code/AssetLib/MD3/MD3Loader.cpp:826:9
    #2 0x5f2b6b8d2842 in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Assimp::IOSystem*) /fuzz/project/assimp/code/Common/BaseImporter.cpp:131:9
    #3 0x5f2b6b34ddf4 in Assimp::Importer::ReadFile(char const*, unsigned int) /fuzz/project/assimp/code/Common/Importer.cpp:709:30
    #4 0x5f2b6b34b6b2 in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /fuzz/project/assimp/code/Common/Importer.cpp:507:9
    #5 0x5f2b6b346d27 in LLVMFuzzerTestOneInput /fuzz/project/assimp/fuzz/assimp_fuzzer.cc:57:34
    #6 0x5f2b6b26d1a3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/assimp_fuzzer+0x3bb1a3) (BuildId: 2e85733d2820458344d0708d9620140031cd73e3)
    #7 0x5f2b6b256f1f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/assimp_fuzzer+0x3a4f1f) (BuildId: 2e85733d2820458344d0708d9620140031cd73e3)
    #8 0x5f2b6b25cc76 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/assimp_fuzzer+0x3aac76) (BuildId: 2e85733d2820458344d0708d9620140031cd73e3)
    #9 0x5f2b6b286a92 in main (/fuzz/fuzzers/assimp_fuzzer+0x3d4a92) (BuildId: 2e85733d2820458344d0708d9620140031cd73e3)
    #10 0x7fc7759d1d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #11 0x7fc7759d1e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #12 0x5f2b6b2517e4 in _start (/fuzz/fuzzers/assimp_fuzzer+0x39f7e4) (BuildId: 2e85733d2820458344d0708d9620140031cd73e3)

Address 0x61300000279c is a wild pointer inside of access range of size 0x000000000004.
SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzz/project/assimp/code/AssetLib/MD3/MD3Loader.cpp:397:17 in Assimp::MD3Importer::ValidateSurfaceHeaderOffsets(Assimp::MD3::Surface const*)
Shadow bytes around the buggy address:
  0x0c267fff84a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff84b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff84c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff84d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff84e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c267fff84f0: fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8520: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8530: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8540: 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
==2372==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugGlobal flag to mark a deviation from expected behaviourFuzzerBugs found by a fuzzerSecurity RiskBugs that could potentially be security vulnerabilities

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions