Skip to content

Heap-based buffer overflow (via out-of-bounds read) on RINEX v2 header #30

@ctrlsill

Description

@ctrlsill

Bug report

Issue details

The GNSSTk library’s FileHandling module contains a heap buffer overflow vulnerability involving an out-of-bounds read when parsing RINEX observation files with incomplete satellite observation count records. This flaw arises during the conversion from RINEX version 2 to version 3, where the code expects observation counts matching the declared observation types. Missing auxiliary lines in the RINEX v2 header cause the vector holding observation counts for a satellite to be shorter than expected. The unchecked access to this vector index leads to an out-of-bounds read resulting in heap memory corruption. An attacker can exploit this to cause Denial of Service (DoS) or potentially arbitrary code execution (RCE).

Steps to reproduce

For demonstration purposes, the minimal test program Rinex3Header_read.cpp was used to perform simple reading of a RINEX v3 file, parsing its header and printing it out to the terminal.

#include "Rinex3ObsHeader.hpp"
#include "Rinex3ObsStream.hpp"

using namespace std;
using namespace gnsstk;

int main(int argc, char *argv[])
{
    // Initialize RINEX stream from a file  
    Rinex3ObsStream roffs(argv[1]);
    // Initialize RINEX header
    Rinex3ObsHeader roh;
    // Read RINEX header 
    roffs >> roh;
    // Print RINEX header
    roh.dump(cout);

   return 0;
}  

The issue is triggered when a specially crafted RINEX v2 observation file with a malformed header provided as input. File defines 10 observation types, which for format compatibility requires additional auxiliary lines are per PRN (as for satellite G07 below). The issue arises when record simply misses the auxiliary line, specifing less observations that expected (like for G09).

     2.10           OBSERVATION DATA    M (MIXED)           RINEX VERSION / TYPE
gLAB                gAGE                 17-MAR-10 12:14    PGM / RUN BY / DATE
MRKR                                                        MARKER NAME
gAGE                UPC: Technical University of Catalonia  OBSERVER / AGENCY
IR2200716006        ASHTECH UZ-12       CQ00                REC # / TYPE / VERS
482                 AOAD/M_T        NONE                    ANT # / TYPE
   4789028.4701    176610.0133   4195017.0310               APPROX POSITION XYZ
         0.9030         0.0000         0.0000               ANTENNA: DELTA H/E/N
    10    L1    L2    P1    P2    C1    S1    S2    D1    D2# / TYPES OF OBSERV
          C2                                                # / TYPES OF OBSERV
  2010     3     5     0     0     0.0000000     GPS        TIME OF FIRST OBS
   G07   815   815   815   815   815   815   815   815   815PRN / # OF OBS
         815                                                PRN / # OF OBS
   G09   246   246   246   246   246   246   246   246   246PRN / # OF OBS
                                                            END OF HEADER

Running the test program using a crafted file as input resulted in heap-buffer overflow detected by AddressSanitizer, caused by out-of-band read, that occurred in reallyGetRecord function, responsible for parsing the header.

Image

Root cause analysis

The root cause lies in insufficient validation of the vector size when accessing the satellite observation counts during header parsing and conversion. The vector holding counts (it->second) for each PRN is expected to have a length equal to the number of declared observation types (R2ObsTypes.size()). However, if the RINEX v2 input omits required auxiliary observation count lines for a satellite, this vector becomes shorter than expected. In reallyGetRecord (Rinex3ObsHeader.cpp), the code iterates over R2ObsTypes and performs vec.push_back operation without bounds checking on iterator i relative to it->second.size(). This unchecked access results in reading beyond the allocated buffer for it->second vector, triggering memory corruption.

Image

Debugging the program state a single instruction step before overflow occurrence, shows that heap memory address of it->second[9] exactly matches the 4-byte region pointed by AddressSanitizer.

Image

The presented scenario does not produce a segmentation fault despite unsafe memory access because reading only 4 bytes past a small vector often lands in valid heap memory, causing silent corruption rather than immediate crash. ASAN's instrumentation detects this by maintaining shadow memory that tracks valid/invalid byte ranges. Nevertheless, this remains a serious issue as such silent heap overflows can corrupt adjacent data or heap management structures, destabilizing the program.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions