Talos Vulnerability Report

TALOS-2024-1930

libigl readNODE out-of-bounds write vulnerability

May 28, 2024
CVE Number

CVE-2024-22181

SUMMARY

An out-of-bounds write vulnerability exists in the readNODE functionality of libigl v2.5.0. A specially crafted .node file can lead to an out-of-bounds write. An attacker can provide a malicious file to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

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

PRODUCT URLS

libigl - https://libigl.github.io/

CVSSv3 SCORE

7.8 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-129 - Improper Validation of Array Index

DETAILS

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.

There exists an out-of-bounds write condition when parsing a .NODE file where the number of entries declared in the header is inconsistent with the number of entries actually present in the file. This can lead to the corruption of the vectors used to load this data in memory.

    IGL_INLINE bool igl::readNODE(
      const std::string node_file_name,
      Eigen::PlainObjectBase<DerivedV>& V,
      Eigen::PlainObjectBase<DerivedI>& I)
    {
    ...



      // Read header
      // n  number of points
      // dim  dimension
      // num_attr  number of attributes
      // num_bm  number of boundary markers
      int n,dim,num_attr,num_bm;
 [0]  int head_count = sscanf(line,"%d %d %d %d", &n, &dim, &num_attr, &num_bm);

        ...

      // resize output
 [1]  V.resize(n,dim);
      I.resize(n,1);

      int line_no = 0;
      int p = 0;
      while (fgets(line, LINE_MAX, node_file) != NULL) 
      {
        ...

 [2]    if(sscanf(l,"%d%n",&I(p),&offset) != 1)
        {
          fprintf(stderr,"readNODE Error: bad index (%d) in %s...\n",
            line_no,
            node_file_name.c_str());
          fclose(node_file);
          return false;
        }
        // adjust offset
        l += offset;

        // Read coordinates
        for(int d = 0;d < dim;d++)
        {
 [3]      if(sscanf(l,"%lf%n",&V(p,d),&offset) != 1)
          {
            fprintf(stderr,"readNODE Error: bad coordinates (%d) in %s...\n",
              line_no,
              node_file_name.c_str());
            fclose(node_file);
            return false;
          }
          // adjust offset
          l += offset;
        }
        ...

[4]     p++;
      }

      assert(p == V.rows());

      fclose(node_file);
      return true;
    }

The readNODE function resizes the V and I matrices at [1] based on the number of points declared in the .node file header at [0]. However the p variable in incremented at [4] is based on the number of lines processed and used as an index to the V and I matrices at [2] and [3]. This happens without verifying if p is within the proper range. As such, if more lines are present in the .node file than announced in the header, an out-of-bound write will occur when writing to I and V.

TIMELINE

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

Credit

Discovered by Philippe Laulheret of Cisco Talos.