CVE-2024-24686,CVE-2024-24685,CVE-2024-24684
Multiple stack-based buffer overflow vulnerabilities exist in the readOFF functionality of libigl v2.5.0. A specially crafted .off file can lead to stack-based buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
libigl v2.5.0
libigl - https://libigl.github.io/
7.8 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-121 - Stack-based Buffer Overflow
libigl is a C++ geometry processing library that is designed to be simple to integrate into projects using a header-only construction for the code base. This library is widely utilized in industries ranging from Triple-A game development to 3D printing, and it can be found in many applications that require the geometry processing of various file formats.
In the readOFF
function, multiple stack based buffer overflow exists due to the unsafe usage of the fscanf
function to read strings into various buffers whose size is hardcoded to 1000 bytes
.
`
IGL_INLINE bool igl::readOFF(
FILE * off_file,
std::vector<std::vector<Scalar > > & V,
std::vector<std::vector<Index > > & F,
std::vector<std::vector<Scalar > > & N,
std::vector<std::vector<Scalar > > & C)
{
...
// First line is always OFF
[0] char header[1000];
...
[1] if(fscanf(off_file,"%s\n",header)!=1
|| !(
string(header).compare(0, OFF.length(), OFF)==0 ||
string(header).compare(0, COFF.length(), COFF)==0 ||
string(header).compare(0,NOFF.length(),NOFF)==0))
{
...
}
...
return true;
`
We can see above that at [0] a stack-based buffer called header
is defined with an hardcoded size of 1000 bytes
. The call to fscanf
at [1] is unsafe and if the first line of the header of the .off
files is longer than 1000 bytes it will overflow the header
buffer.
`
template <typename Scalar, typename Index>
IGL_INLINE bool igl::readOFF(
FILE * off_file,
std::vector<std::vector<Scalar > > & V,
std::vector<std::vector<Index > > & F,
std::vector<std::vector<Scalar > > & N,
std::vector<std::vector<Scalar > > & C)
{
...
for(int i = 0;i<number_of_vertices;)
{
[0] fgets(line, 1000, off_file);
double x,y,z,nx,ny,nz;
[1] if(sscanf(line, "%lg %lg %lg %lg %lg %lg",&x,&y,&z,&nx,&ny,&nz)>= 3)
{
...
}else if(
[2] fscanf(off_file,"%[#]",&tic_tac_toe)==1)
{
[3] char comment[1000];
[4] fscanf(off_file,"%[^\n]",comment);
}else
{
...
}
}
...
return true;
}
`
We can see above tha while iterating over vertices, a line is read from the .off
file at [0]. If this line doesn’t match the pattern at [1] then more data is read from [2] to test if a comment is starting (trying to read a #
). If so a full line is read at [4] into the comment
buffer defined at [3]. The size of the comment
buffer is hardcoded to be 1000 bytes
, as thus, if the line read into it is longer than 1000 characters, a stack-buffer overflow will occur.
`
template <typename Scalar, typename Index>
IGL_INLINE bool igl::readOFF(
FILE * off_file,
std::vector<std::vector<Scalar > > & V,
std::vector<std::vector<Index > > & F,
std::vector<std::vector<Scalar > > & N,
std::vector<std::vector<Scalar > > & C)
{
...
// Read faces
for(int i = 0;i<number_of_faces;)
{
std::vector<Index > face;
int valence;
[0] if(fscanf(off_file,"%d",&valence)==1)
{
...
}else if(
[1] fscanf(off_file,"%[#]",&tic_tac_toe)==1)
{
[2] char comment[1000];
[3] fscanf(off_file,"%[^\n]",comment);
}else
{
...
}
fclose(off_file);
return true;
}
`
We can see above tha while iterating over faces, the function tries to read an integer from the .off
file at [0]. If this fails, an attempt is done to read a #
[1]. On success more data is read from the file at [3] into the comment
buffer defined at [2]. The size of the comment
buffer is hardcoded to be 1000 bytes
, as thus, if the line read into it is longer than 1000 characters, a stack-buffer overflow will occur.
2023-11-22 - Initial Vendor Contact
2023-11-28 - Initial Vendor Contact
2023-11-30 - Request for confirmation
2023-12-11 - Advisories sent
2024-02-07 - Four more advisories sent, after the initial two
2024-02-27 - Request for status update
2024-04-10 - Request for status update
2ß24-05-15 - Request for status update via Github issue, no reply
2024-05-28 - Public Release
Discovered by Philippe Laulheret of Cisco Talos.