Bug: [Vulnerability Report] Heap Buffer Overflow in Assimp FBX Importer
Dear Assimp Maintainers,
I am writing to report a heap buffer overflow vulnerability discovered in the FBX importer module of Assimp.
This vulnerability occurs in the aiMaterial::AddBinaryProperty function when loading a malicious FBX file.
Below are the details of the vulnerability.
- Environment
OS : Ubuntu 22.04
Version : Assimp 6.0.2 (Latest Release)
Build Type : Release Mode
Build Command:
cmake -S . -B build-afl \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=OFF \ -DASSIMP_BUILD_TESTS=OFF \ -DASSIMP_BUILD_SAMPLES=OFF \ -DASSIMP_BUILD_ASSIMP_TOOLS=OFF \ -DASSIMP_BUILD_DRACO=OFF \ -DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF \ -DASSIMP_BUILD_FBX_IMPORTER=ON \ -DASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT=OFF
Vulnerability Summary Type : Heap Buffer Overflow Location : code/Material/MaterialSystem.cpp (Line 523) Function : aiMaterial::AddBinaryProperty Vulnerable Code : strcpy(pcNew→mKey.data, pKey)
Root Cause Analysis
The vulnerability arises when adding a property to an aiMaterial object.
The code attempts to copy the property name (pKey) into a fixed-size buffer (pcNew->mKey.data, where AI_MAXLEN = 1024):
// code/Material/MaterialSystem.cpp pcNew->mKey.length = static_cast<ai_uint32>(::strlen(pKey));
// The ai_assert macro is compiled out (empty) in Release builds. ai_assert(AI_MAXLEN > pcNew->mKey.length);
// Vulnerability: No runtime length check exists in Release mode. // If pKey is larger than 1024 bytes, a heap buffer overflow occurs. strcpy(pcNew->mKey.data, pKey);
In Debug builds, ai_assert functions correctly and prevents the overflow.
However, in Release builds (typically used in production), ai_assert is removed during compilation.
Consequently, there is no runtime validation of the string length, allowing an attacker to trigger a heap buffer overflow by providing a property name longer than AI_MAXLEN.
- Proof of Concept (PoC)
I have attached:
a PoC FBX file (crash1.1) that triggers this vulnerability, and a test binary (fbx_fuzzer) built with ASAN enabled.
In the PoC, the Opacity property key has been manipulated to contain an abnormally long string.
Snippet of the PoC:
Material: ... { Properties70: { ... P: "OpacityAAAA... (long string) ...AAAA", "double", "Number", "",1 } } 5. ASAN Log
Running the PoC with AddressSanitizer (ASAN) confirms the heap buffer overflow:
Reading 15599 bytes from crash1.1
==114909==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000008ba0 at pc 0x557de6df4035 bp 0x7ffc9d7080b0 sp 0x7ffc9d707878 WRITE of size 1582 at 0x619000008ba0 thread T0 #0 0x557de6df4034 in strcpy (/root/assimp/build-afl/fbx_fuzzer+0x135034) (BuildId: a53ec1015b595255) #1 0x557de707be24 in aiMaterial::AddBinaryProperty(void const*, unsigned int, char const*, unsigned int, unsigned int, aiPropertyTypeInfo) /root/assimp/code/Material/MaterialSystem.cpp:523:5 #2 0x557de70eb8ad in aiMaterial::AddProperty(float const*, unsigned int, char const*, unsigned int, unsigned int) /root/assimp/include/assimp/material.inl:242:12 #3 0x557de70eb8ad in Assimp::FBX::FBXConverter::SetShadingPropertiesRaw(aiMaterial*, Assimp::FBX::PropertyTable const&, std::map<std::__cxx11::basic_string<char, std::char_traits, std::allocator>, Assimp::FBX::Texture const*, std::less<std::__cxx11::basic_string<char, std::char_traits, std::allocator>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits, std::allocator> const, Assimp::FBX::Texture const*>>> const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:2416:22 #4 0x557de70c9007 in Assimp::FBX::FBXConverter::ConvertMaterial(Assimp::FBX::Material const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1801:5 #5 0x557de715f5b0 in Assimp::FBX::FBXConverter::ConvertMaterialForMesh(aiMesh*, Assimp::FBX::Model const&, Assimp::FBX::MeshGeometry const&, int) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1745:27 #6 0x557de7157254 in Assimp::FBX::FBXConverter::ConvertMeshSingleMaterial(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiMatrix4x4t const&, aiNode*, aiNode*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1238:9 #7 0x557de7149d2c in Assimp::FBX::FBXConverter::ConvertMesh(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1026:20 #8 0x557de7134c04 in Assimp::FBX::FBXConverter::ConvertModel(Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:976:56 #9 0x557de711af50 in Assimp::FBX::FBXConverter::ConvertNodes(unsigned long, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:320:13 #10 0x557de70c755f in Assimp::FBX::FBXConverter::ConvertRootNode() /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:206:5 #11 0x557de70bc379 in Assimp::FBX::FBXConverter::FBXConverter(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:154:5 #12 0x557de71cc346 in Assimp::FBX::ConvertToAssimpScene(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:3812:18 #13 0x557de707e8c4 in Assimp::FBXImporter::InternReadFile(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, aiScene*, Assimp::IOSystem*) /root/assimp/code/AssetLib/FBX/FBXImporter.cpp:169:3 #14 0x557de6e7aa3a in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, Assimp::IOSystem*) /root/assimp/code/Common/BaseImporter.cpp:131:9 #15 0x557de6e57890 in Assimp::Importer::ReadFile(char const*, unsigned int) /root/assimp/code/Common/Importer.cpp:709:30 #16 0x557de6e5197e in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /root/assimp/code/Common/Importer.cpp:507:9 #17 0x557de6e4c71e in LLVMFuzzerTestOneInput /root/assimp/fuzz/fbx_fuzzer.cpp:16:34 #18 0x557de6e46f2d in ExecuteFilesOnyByOne /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c:305:7 #19 0x557de6e46d1d in LLVMFuzzerRunDriver /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c #20 0x557de6e468ad in main /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c:361:10 #21 0x74df4640ad8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #22 0x74df4640ae3f in __libc_start_main csu/../csu/libc-start.c:392:3 #23 0x557de6d84544 in _start (/root/assimp/build-afl/fbx_fuzzer+0xc5544) (BuildId: a53ec1015b595255)
0x619000008ba0 is located 0 bytes to the right of 1056-byte region [0x619000008780,0x619000008ba0) allocated by thread T0 here: #0 0x557de6e441cd in operator new(unsigned long) (/root/assimp/build-afl/fbx_fuzzer+0x1851cd) (BuildId: a53ec1015b595255) #1 0x557de707bc12 in aiMaterial::AddBinaryProperty(void const*, unsigned int, char const*, unsigned int, unsigned int, aiPropertyTypeInfo) /root/assimp/code/Material/MaterialSystem.cpp:510:47 #2 0x557de70eb8ad in aiMaterial::AddProperty(float const*, unsigned int, char const*, unsigned int, unsigned int) /root/assimp/include/assimp/material.inl:242:12 #3 0x557de70eb8ad in Assimp::FBX::FBXConverter::SetShadingPropertiesRaw(aiMaterial*, Assimp::FBX::PropertyTable const&, std::map<std::__cxx11::basic_string<char, std::char_traits, std::allocator>, Assimp::FBX::Texture const*, std::less<std::__cxx11::basic_string<char, std::char_traits, std::allocator>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits, std::allocator> const, Assimp::FBX::Texture const*>>> const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:2416:22 #4 0x557de70c9007 in Assimp::FBX::FBXConverter::ConvertMaterial(Assimp::FBX::Material const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1801:5 #5 0x557de715f5b0 in Assimp::FBX::FBXConverter::ConvertMaterialForMesh(aiMesh*, Assimp::FBX::Model const&, Assimp::FBX::MeshGeometry const&, int) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1745:27 #6 0x557de7157254 in Assimp::FBX::FBXConverter::ConvertMeshSingleMaterial(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiMatrix4x4t const&, aiNode*, aiNode*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1238:9 #7 0x557de7149d2c in Assimp::FBX::FBXConverter::ConvertMesh(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1026:20 #8 0x557de7134c04 in Assimp::FBX::FBXConverter::ConvertModel(Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:976:56 #9 0x557de711af50 in Assimp::FBX::FBXConverter::ConvertNodes(unsigned long, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:320:13 #10 0x557de70c755f in Assimp::FBX::FBXConverter::ConvertRootNode() /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:206:5 #11 0x557de70bc379 in Assimp::FBX::FBXConverter::FBXConverter(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:154:5 #12 0x557de71cc346 in Assimp::FBX::ConvertToAssimpScene(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:3812:18 #13 0x557de707e8c4 in Assimp::FBXImporter::InternReadFile(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, aiScene*, Assimp::IOSystem*) /root/assimp/code/AssetLib/FBX/FBXImporter.cpp:169:3 #14 0x557de6e7aa3a in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, Assimp::IOSystem*) /root/assimp/code/Common/BaseImporter.cpp:131:9 #15 0x557de6e57890 in Assimp::Importer::ReadFile(char const*, unsigned int) /root/assimp/code/Common/Importer.cpp:709:30 #16 0x557de6e5197e in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /root/assimp/code/Common/Importer.cpp:507:9 #17 0x557de6e4c71e in LLVMFuzzerTestOneInput /root/assimp/fuzz/fbx_fuzzer.cpp:16:34 #18 0x557de6e46f2d in ExecuteFilesOnyByOne /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c:305:7
SUMMARY: AddressSanitizer: heap-buffer-overflow (/root/assimp/build-afl/fbx_fuzzer+0x135034) (BuildId: a53ec1015b595255) in strcpy Shadow bytes around the buggy address: 0x0c327fff9120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c327fff9130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c327fff9140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c327fff9150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c327fff9160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c327fff9170: 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa fa fa 0x0c327fff9180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c327fff9190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c327fff91a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c327fff91b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c327fff91c0: 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 ==114909==ABORTING
The report shows a write past the end of a 1056-byte heap region originating from strcpy inside aiMaterial::AddBinaryProperty.
- Impact
Since this vulnerability causes heap corruption, it could potentially be exploited for:
denial of service (application crash) remote code execution (RCE), in scenarios where an attacker can supply crafted FBX files and influence heap layout.
I would also like to request a CVE ID to track this vulnerability.
Please let me know if you need any further information
Best regards,
GunP4ng
[email protected]Dear Assimp Maintainers,
I am writing to report a heap buffer overflow vulnerability discovered in the FBX importer module of Assimp.
This vulnerability occurs in the aiMaterial::AddBinaryProperty function when loading a malicious FBX file.
Below are the details of the vulnerability.
- Environment
OS : Ubuntu 22.04
Version : Assimp 6.0.2 (Latest Release)
Build Type : Release Mode
Build Command:
cmake -S . -B build-afl \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=OFF \ -DASSIMP_BUILD_TESTS=OFF \ -DASSIMP_BUILD_SAMPLES=OFF \ -DASSIMP_BUILD_ASSIMP_TOOLS=OFF \ -DASSIMP_BUILD_DRACO=OFF \ -DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF \ -DASSIMP_BUILD_FBX_IMPORTER=ON \ -DASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT=OFF
Vulnerability Summary Type : Heap Buffer Overflow Location : code/Material/MaterialSystem.cpp (Line 523) Function : aiMaterial::AddBinaryProperty Vulnerable Code : strcpy(pcNew→mKey.data, pKey)
Root Cause Analysis
The vulnerability arises when adding a property to an aiMaterial object.
The code attempts to copy the property name (pKey) into a fixed-size buffer (pcNew->mKey.data, where AI_MAXLEN = 1024):
// code/Material/MaterialSystem.cpp pcNew->mKey.length = static_cast<ai_uint32>(::strlen(pKey));
// The ai_assert macro is compiled out (empty) in Release builds. ai_assert(AI_MAXLEN > pcNew->mKey.length);
// Vulnerability: No runtime length check exists in Release mode. // If pKey is larger than 1024 bytes, a heap buffer overflow occurs. strcpy(pcNew->mKey.data, pKey);
In Debug builds, ai_assert functions correctly and prevents the overflow.
However, in Release builds (typically used in production), ai_assert is removed during compilation.
Consequently, there is no runtime validation of the string length, allowing an attacker to trigger a heap buffer overflow by providing a property name longer than AI_MAXLEN.
- Proof of Concept (PoC)
I have attached:
a PoC FBX file (crash1.1) that triggers this vulnerability, and a test binary (fbx_fuzzer) built with ASAN enabled.
In the PoC, the Opacity property key has been manipulated to contain an abnormally long string.
Snippet of the PoC:
Material: ... { Properties70: { ... P: "OpacityAAAA... (long string) ...AAAA", "double", "Number", "",1 } } 5. ASAN Log
Running the PoC with AddressSanitizer (ASAN) confirms the heap buffer overflow:
Reading 15599 bytes from crash1.1
==114909==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000008ba0 at pc 0x557de6df4035 bp 0x7ffc9d7080b0 sp 0x7ffc9d707878 WRITE of size 1582 at 0x619000008ba0 thread T0 #0 0x557de6df4034 in strcpy (/root/assimp/build-afl/fbx_fuzzer+0x135034) (BuildId: a53ec1015b595255) #1 0x557de707be24 in aiMaterial::AddBinaryProperty(void const*, unsigned int, char const*, unsigned int, unsigned int, aiPropertyTypeInfo) /root/assimp/code/Material/MaterialSystem.cpp:523:5 #2 0x557de70eb8ad in aiMaterial::AddProperty(float const*, unsigned int, char const*, unsigned int, unsigned int) /root/assimp/include/assimp/material.inl:242:12 #3 0x557de70eb8ad in Assimp::FBX::FBXConverter::SetShadingPropertiesRaw(aiMaterial*, Assimp::FBX::PropertyTable const&, std::map<std::__cxx11::basic_string<char, std::char_traits, std::allocator>, Assimp::FBX::Texture const*, std::less<std::__cxx11::basic_string<char, std::char_traits, std::allocator>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits, std::allocator> const, Assimp::FBX::Texture const*>>> const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:2416:22 #4 0x557de70c9007 in Assimp::FBX::FBXConverter::ConvertMaterial(Assimp::FBX::Material const&, Assimp::FBX::MeshGeometry const*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1801:5 #5 0x557de715f5b0 in Assimp::FBX::FBXConverter::ConvertMaterialForMesh(aiMesh*, Assimp::FBX::Model const&, Assimp::FBX::MeshGeometry const&, int) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1745:27 #6 0x557de7157254 in Assimp::FBX::FBXConverter::ConvertMeshSingleMaterial(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiMatrix4x4t const&, aiNode*, aiNode*) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1238:9 #7 0x557de7149d2c in Assimp::FBX::FBXConverter::ConvertMesh(Assimp::FBX::MeshGeometry const&, Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:1026:20 #8 0x557de7134c04 in Assimp::FBX::FBXConverter::ConvertModel(Assimp::FBX::Model const&, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:976:56 #9 0x557de711af50 in Assimp::FBX::FBXConverter::ConvertNodes(unsigned long, aiNode*, aiNode*, aiMatrix4x4t const&) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:320:13 #10 0x557de70c755f in Assimp::FBX::FBXConverter::ConvertRootNode() /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:206:5 #11 0x557de70bc379 in Assimp::FBX::FBXConverter::FBXConverter(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:154:5 #12 0x557de71cc346 in Assimp::FBX::ConvertToAssimpScene(aiScene*, Assimp::FBX::Document const&, bool) /root/assimp/code/AssetLib/FBX/FBXConverter.cpp:3812:18 #13 0x557de707e8c4 in Assimp::FBXImporter::InternReadFile(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, aiScene*, Assimp::IOSystem*) /root/assimp/code/AssetLib/FBX/FBXImporter.cpp:169:3 #14 0x557de6e7aa3a in Assimp::BaseImporter::ReadFile(Assimp::Importer*, std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&, Assimp::IOSystem*) /root/assimp/code/Common/BaseImporter.cpp:131:9 #15 0x557de6e57890 in Assimp::Importer::ReadFile(char const*, unsigned int) /root/assimp/code/Common/Importer.cpp:709:30 #16 0x557de6e5197e in Assimp::Importer::ReadFileFromMemory(void const*, unsigned long, unsigned int, char const*) /root/assimp/code/Common/Importer.cpp:507:9 #17 0x557de6e4c71e in LLVMFuzzerTestOneInput /root/assimp/fuzz/fbx_fuzzer.cpp:16:34 #18 0x557de6e46f2d in ExecuteFilesOnyByOne /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c:305:7 #19 0x557de6e46d1d in LLVMFuzzerRunDriver /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c #20 0x557de6e468ad in main /root/AFLplusplus/utils/aflpp_driver/aflpp_driver.c:361:10 #21 0x74df4640ad8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #22 0x74df4640ae3f in __libc_start_main csu/../csu/libc-start.c:392:3 #23 0x557de6d84544 in _start (/root/assimp/build-afl/fbx_fuzzer+0xc5544) (BuildId: a53ec1015b595255)
0x619000008ba0 is located 0 bytes to the right of 1056-byte region [0x619000008780,0x619000008ba0) allocated by thread T0 here: #0 0x557de6e441cd in operator new(unsigned long) (/root/assimp/build-afl/fbx_fuzzer+0x1851cd) (BuildId: a53ec1015b595255) #1 0x557de707bc12
Source: assimp/assimp