CVE-2022-1237 (published 2022-04-06T10:15Z)

Improper Validation of Array Index in GitHub repository radareorg/radare2 prior to 5.6.8. This vulnerability is heap overflow and may be exploitable. For more general description of heap buffer overflow, see [CWE](https://cwe.mitre.org/data/definitions/122.html).

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-129
cwe_name: Improper Validation of Array Index
description: The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array.
extended_description: 
            
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/radareorg/radare2
commit_details: repo_url: https://github.com/radareorg/radare2
author: pancake
author_date: 2022-04-05 11:31:24+02:00
msg: Fix oobread segfaults in the NE bin parser ##crash

* Reported by @han0nly via huntr.dev
* Reproducers: seed1 seed2
* BountyID: ad3c9c4c-76e7-40c8-bd4a-c095acd8bb40
repository: repo_url: https://github.com/radareorg/radare2
repo_name: radareorg/radare2
description: UNIX-like reverse engineering framework and command-line toolset
repo_language: C
owner: radareorg
file_changes: filename: ne.c
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-/* radare - LGPL - Copyright 2019 - GustavoLCR */
+/* radare - LGPL - Copyright 2019-2022 - GustavoLCR */
 
 #include "ne.h"
 
@@ -83,10 +83,10 @@ RList *r_bin_ne_get_segments(r_bin_ne_obj_t *bin) {
 	RList *segments = r_list_newf (free);
 	for (i = 0; i < bin->ne_header->SegCount; i++) {
 		RBinSection *bs = R_NEW0 (RBinSection);
-		NE_image_segment_entry *se = &bin->segment_entries[i];
 		if (!bs) {
 			return segments;
 		}
+		NE_image_segment_entry *se = &bin->segment_entries[i];
 		bs->size = se->length;
 		bs->vsize = se->minAllocSz ? se->minAllocSz : 64000;
 		bs->bits = R_SYS_BITS_16;
@@ -425,7 +425,7 @@ RList *r_bin_ne_get_relocs(r_bin_ne_obj_t *bin) {
 		return NULL;
 	}
 
-	ut16 *modref = malloc (bin->ne_header->ModRefs * sizeof (ut16));
+	ut16 *modref = calloc (bin->ne_header->ModRefs, sizeof (ut16));
 	if (!modref) {
 		return NULL;
 	}
@@ -451,7 +451,8 @@ RList *r_bin_ne_get_relocs(r_bin_ne_obj_t *bin) {
 			continue;
 		}
 		off += 2;
-		while (off < start + length * sizeof (NE_image_reloc_item)) {
+		size_t buf_size = r_buf_size (bin->buf);
+		while (off < start + length * sizeof (NE_image_reloc_item) && off < buf_size) {
 			RBinReloc *reloc = R_NEW0 (RBinReloc);
 			if (!reloc) {
 				return NULL;
@@ -484,10 +485,11 @@ RList *r_bin_ne_get_relocs(r_bin_ne_obj_t *bin) {
 					break;
 				}
 				char *name;
-				if (rel.index > bin->ne_header->ModRefs) {
+				if (rel.index < 1 || rel.index > bin->ne_header->ModRefs) {
 					name = r_str_newf ("UnknownModule%d_%x", rel.index, off); // ????
 				} else {
-					offset = modref[rel.index - 1] + bin->header_offset + bin->ne_header->ImportNameTable;
+					int index = rel.index;
+					offset = modref[index - 1] + bin->header_offset + bin->ne_header->ImportNameTable;
 					name = __read_nonnull_str_at (bin->buf, offset);
 				}
 				if (rel.flags & IMPORTED_ORD) {
@@ -563,7 +565,14 @@ void __init(RBuffer *buf, r_bin_ne_obj_t *bin) {
 		return;
 	}
 	bin->buf = buf;
+	// XXX this is endian unsafe
 	r_buf_read_at (buf, bin->header_offset, (ut8 *)bin->ne_header, sizeof (NE_image_header));
+	if (bin->ne_header->FileAlnSzShftCnt > 8) {
+		bin->ne_header->FileAlnSzShftCnt = 8;
+	}
+	if (bin->ne_header->ModRefs * sizeof (ut16) >= r_buf_size (bin->buf)) {
+		bin->ne_header->ModRefs = r_buf_size (bin->buf) / sizeof (ut16);
+	}
 	bin->alignment = 1 << bin->ne_header->FileAlnSzShftCnt;
 	if (!bin->alignment) {
 		bin->alignment = 1 << 9;
@@ -571,8 +580,16 @@ void __init(RBuffer *buf, r_bin_ne_obj_t *bin) {
 	bin->os = __get_target_os (bin);
 
 	ut16 offset = bin->ne_header->SegTableOffset + bin->header_offset;
-	ut16 size = bin->ne_header->SegCount * sizeof (NE_image_segment_entry);
+	size_t size = bin->ne_header->SegCount * sizeof (NE_image_segment_entry);
+	if (offset >= r_buf_size (bin->buf)) {
+		return;
+	}
+	size_t remaining = r_buf_size (bin->buf) - offset;
+	size = R_MIN (remaining, size);
 	bin->segment_entries = calloc (1, size);
+	if (size >= remaining) {
+		bin->ne_header->SegCount = size / sizeof (NE_image_segment_entry);
+	}
 	if (!bin->segment_entries) {
 		return;
 	}

programming_language: C