Submit #764644: xlnt-community xlnt commit bceb706 and before Heap-based Buffer Overflowinfo

Titelxlnt-community xlnt commit bceb706 and before Heap-based Buffer Overflow
Beschreibung### Description We discovered a Heap-buffer-overflow (Read) vulnerability in xlnt. The crash occurs in xlnt::detail::compound_document_istreambuf::xsgetn when parsing an encrypted XLSX file (specifically reading the Agile Encryption info). The ASAN report indicates a READ of size 4 occurring exactly 0 bytes after a 32-byte allocated region. The allocation stack shows this region is a std::vector<int> created in follow_chain, which represents the sector chain of a stream. This suggests the stream buffer implementation attempts to access a sector index past the end of the chain vector. Vendor confirmed and create a pull request [#147](https://github.com/xlnt-community/xlnt/pull/147). ### Environment - OS: Linux x86_64 - Complier: Clang - Build Configuration: Release mode with ASan enabled. ### Vulnerability Details - Target: xlnt - Vulnerability Type: CWE-125: Out-of-bounds Read - Function: xlnt::detail::compound_document_istreambuf::xsgetn - Location: source/detail/cryptography/compound_document.cpp:132:44 - Caller: std::istream::read -> xml::parser::next_body - Allocation Site: xlnt::detail::compound_document::follow_chain at compound_document.cpp:723 - Root Cause Analysis: The compound_document_istreambuf class manages reading from a stream that is fragmented across multiple sectors in an OLE container. The sector chain is stored in a std::vector<int>. In xsgetn, the code iterates through these sectors to copy data into the destination buffer. The crash indicates that the loop or index increment logic in xsgetn does not correctly handle the end-of-chain condition, causing it to read a sector ID from the vector after the vector's end (iterator invalidation or index out of bounds). ### Reproduce 1. Build xlnt and harness with Release optimization and ASAN enabled. <details> <summary>harness.c</summary> ``` #include <xlnt/xlnt.hpp> #include <iostream> #include <string> #include <vector> int main(int argc, char **argv) { if (argc < 2) { return 0; } std::string filepath = argv[1]; try { xlnt::workbook wb; wb.load(filepath); if (wb.sheet_count() > 0) { auto ws = wb.active_sheet(); for (auto row : ws.rows(false)) { for (auto cell : row) { (void)cell.to_string(); } } } } catch (const xlnt::exception& e) { } catch (const std::exception& e) { } catch (...) { } return 0; } ``` </details> 2. Run with the crashing [file](https://github.com/oneafter/0128/blob/main/xl3/repro): ``` ./harness repro ``` <details> <summary>ASAN report</summary> ``` ==45864==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000002310 at pc 0x7f4e8f7506c4 bp 0x7ffd59ad3210 sp 0x7ffd59ad3208 READ of size 4 at 0x503000002310 thread T0 #0 0x7f4e8f7506c3 in xlnt::detail::compound_document_istreambuf::xsgetn(char*, long) /src/repro/xlnt/source/detail/cryptography/compound_document.cpp:132:44 #1 0x7f4e8f148140 in std::basic_streambuf<char, std::char_traits<char>>::sgetn(char*, long) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/streambuf:365:28 #2 0x7f4e8f148140 in std::istream::read(char*, long) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/bits/istream.tcc:711:40 #3 0x7f4e8fd6644d in xml::parser::next_body() /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:647:14 #4 0x7f4e8fd59f0f in xml::parser::next_(bool) /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:378:19 #5 0x7f4e8fd608af in xml::parser::next() /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:184:14 #6 0x7f4e8fd608af in xml::parser::next_expect(xml::parser::event_type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:288:9 #7 0x7f4e8f78dc7a in (anonymous namespace)::read_agile_encryption_info(std::istream&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:193:12 #8 0x7f4e8f78dc7a in (anonymous namespace)::read_encryption_info(std::istream&, std::__cxx11::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:279:22 #9 0x7f4e8f7887c9 in (anonymous namespace)::decrypt_xlsx(std::vector<unsigned char, std::allocator<unsigned char>> const&, std::__cxx11::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:324:28 #10 0x7f4e8f7887c9 in xlnt::detail::decrypt_xlsx(std::vector<unsigned char, std::allocator<unsigned char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:340:12 #11 0x7f4e8f78c63e in void xlnt::detail::xlsx_consumer::read_internal<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>(std::istream&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:354:28 #12 0x7f4e8f54341a in xlnt::workbook::load(std::istream&) /src/repro/xlnt/source/workbook/workbook.cpp:972:22 #13 0x7f4e8f541415 in xlnt::workbook::load(xlnt::path const&) /src/repro/xlnt/source/workbook/workbook.cpp:1020:5 #14 0x7f4e8f57a2c2 in void xlnt::workbook::load_internal<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/source/workbook/workbook.cpp:996:12 #15 0x7f4e8f57a2c2 in xlnt::workbook::load(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/source/workbook/workbook.cpp:1007:12 #16 0x55e2da8922be in main /src/repro/xlnt/fuzz_xlnt.cpp:18:12 #17 0x7f4e8ecfc1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #18 0x7f4e8ecfc28a in __libc_start_main csu/../csu/libc-start.c:360:3 #19 0x55e2da7ae584 in _start (/src/repro/xlnt/fuzz_xlnt+0x2d584) (BuildId: 0ed0b9c74d771dd96d8c9fdc9cc1d67cc1986854) 0x503000002310 is located 0 bytes after 32-byte region [0x5030000022f0,0x503000002310) allocated by thread T0 here: #0 0x55e2da88fa31 in operator new(unsigned long) (/src/repro/xlnt/fuzz_xlnt+0x10ea31) (BuildId: 0ed0b9c74d771dd96d8c9fdc9cc1d67cc1986854) #1 0x7f4e8f75ef60 in std::__new_allocator<int>::allocate(unsigned long, void const*) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27 #2 0x7f4e8f75ef60 in std::allocator_traits<std::allocator<int>>::allocate(std::allocator<int>&, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20 #3 0x7f4e8f75ef60 in std::_Vector_base<int, std::allocator<int>>::_M_allocate(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20 #4 0x7f4e8f75ef60 in void std::vector<int, std::allocator<int>>::_M_realloc_insert<int const&>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int>>>, int const&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/vector.tcc:459:33 #5 0x7f4e8f73961d in std::vector<int, std::allocator<int>>::push_back(int const&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:1292:4 #6 0x7f4e8f73961d in xlnt::detail::compound_document::follow_chain(int, std::vector<int, std::allocator<int>> const&) /src/repro/xlnt/source/detail/cryptography/compound_document.cpp:723:15 #7 0x7f4e8f74f407 in xlnt::detail::compound_document_istreambuf::xsgetn(char*, long) /src/repro/xlnt/source/detail/cryptography/compound_document.cpp:125:38 #8 0x7f4e8f148140 in std::basic_streambuf<char, std::char_traits<char>>::sgetn(char*, long) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/streambuf:365:28 #9 0x7f4e8f148140 in std::istream::read(char*, long) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/bits/istream.tcc:711:40 #10 0x7f4e8fd6644d in xml::parser::next_body() /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:647:14 #11 0x7f4e8fd59f0f in xml::parser::next_(bool) /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:378:19 #12 0x7f4e8fd608af in xml::parser::next() /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:184:14 #13 0x7f4e8fd608af in xml::parser::next_expect(xml::parser::event_type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/repro/xlnt/third-party/libstudxml/libstudxml/parser.cxx:288:9 #14 0x7f4e8f78dc7a in (anonymous namespace)::read_agile_encryption_info(std::istream&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:193:12 #15 0x7f4e8f78dc7a in (anonymous namespace)::read_encryption_info(std::istream&, std::__cxx11::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:279:22 #16 0x7f4e8f7887c9 in (anonymous namespace)::decrypt_xlsx(std::vector<unsigned char, std::allocator<unsigned char>> const&, std::__cxx11::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>> const&) /src/repro/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:324:28 #17 0x7f4e8f7887c9 in xlnt::detail::decrypt_xlsx(std::vector<unsigned char, std::allocator<unsigned char>> c
Quelle⚠️ https://github.com/xlnt-community/xlnt/issues/139
Benutzer Oneafter (UID 92781)
Einreichung21.02.2026 04:52 (vor 2 Monaten)
Moderieren06.03.2026 21:34 (14 days later)
StatusAkzeptiert
VulDB Eintrag349552 [xlnt-community xlnt bis 1.6.1 XLSX File Parser compound_document.cpp xsgetn Information Disclosure]
Punkte20

Do you need the next level of professionalism?

Upgrade your account now!