CVE-2021-43814 (published 2021-12-13T20:15Z)

Rizin is a UNIX-like reverse engineering framework and command-line toolset. In versions up to and including 0.3.1 there is a heap-based out of bounds write in parse_die() when reversing an AMD64 ELF binary with DWARF debug info. When a malicious AMD64 ELF binary is opened by a victim user, Rizin may crash or execute unintended actions. No workaround are known and users are advised to upgrade.

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-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/rizinorg/rizin
commit_details: repo_url: https://github.com/rizinorg/rizin
author: Florian Märkl
author_date: 2021-12-10 15:43:12+01:00
msg: Fix oob write for dwarf with abbrev with count 0 (Fix #2083) (#2086)
repository: repo_url: https://github.com/rizinorg/rizin
repo_name: rizinorg/rizin
description: UNIX-like reverse engineering framework and command-line toolset.
repo_language: C
owner: rizinorg
file_changes: filename: dwarf.c
change_type: ModificationType.MODIFY
diff: @@ -1220,9 +1220,13 @@ static int init_die(RzBinDwarfDie *die, ut64 abbr_code, ut64 attr_count) {
 	if (!die) {
 		return -1;
 	}
-	die->attr_values = calloc(sizeof(RzBinDwarfAttrValue), attr_count);
-	if (!die->attr_values) {
-		return -1;
+	if (attr_count) {
+		die->attr_values = calloc(sizeof(RzBinDwarfAttrValue), attr_count);
+		if (!die->attr_values) {
+			return -1;
+		}
+	} else {
+		die->attr_values = NULL;
 	}
 	die->abbrev_code = abbr_code;
 	die->capacity = attr_count;
@@ -1726,25 +1730,27 @@ static const ut8 *parse_die(const ut8 *buf, const ut8 *buf_end, RzBinDwarfDebugI
 	size_t i;
 	const char *comp_dir = NULL;
 	ut64 line_info_offset = UT64_MAX;
-	for (i = 0; i < abbrev->count - 1; i++) {
-		memset(&die->attr_values[i], 0, sizeof(die->attr_values[i]));
+	if (abbrev->count) {
+		for (i = 0; i < abbrev->count - 1; i++) {
+			memset(&die->attr_values[i], 0, sizeof(die->attr_values[i]));
 
-		buf = parse_attr_value(buf, buf_end - buf, &abbrev->defs[i],
-			&die->attr_values[i], hdr, debug_str, debug_str_len, big_endian);
+			buf = parse_attr_value(buf, buf_end - buf, &abbrev->defs[i],
+				&die->attr_values[i], hdr, debug_str, debug_str_len, big_endian);
 
-		RzBinDwarfAttrValue *attribute = &die->attr_values[i];
+			RzBinDwarfAttrValue *attribute = &die->attr_values[i];
 
-		if (attribute->attr_name == DW_AT_comp_dir && (attribute->attr_form == DW_FORM_strp || attribute->attr_form == DW_FORM_string) && attribute->string.content) {
-			comp_dir = attribute->string.content;
-		}
-		if (attribute->attr_name == DW_AT_stmt_list) {
-			if (attribute->kind == DW_AT_KIND_CONSTANT) {
-				line_info_offset = attribute->uconstant;
-			} else if (attribute->kind == DW_AT_KIND_REFERENCE) {
-				line_info_offset = attribute->reference;
+			if (attribute->attr_name == DW_AT_comp_dir && (attribute->attr_form == DW_FORM_strp || attribute->attr_form == DW_FORM_string) && attribute->string.content) {
+				comp_dir = attribute->string.content;
+			}
+			if (attribute->attr_name == DW_AT_stmt_list) {
+				if (attribute->kind == DW_AT_KIND_CONSTANT) {
+					line_info_offset = attribute->uconstant;
+				} else if (attribute->kind == DW_AT_KIND_REFERENCE) {
+					line_info_offset = attribute->reference;
+				}
 			}
+			die->count++;
 		}
-		die->count++;
 	}
 
 	// If this is a compilation unit dir attribute, we want to cache it so the line info parsing

programming_language: Lua
filename: crash
change_type: ModificationType.MODIFY
diff: @@ -25,3 +25,11 @@ nth vaddr bind type lib name
 []
 EOF
 RUN
+
+NAME=ELF/Dwarf: abbrev empty
+FILE=bins/elf/dwarf_fuzzed_abbrev_empty
+CMDS=<<EOF
+aaa
+EOF
+EXPECT=
+RUN

programming_language: Shell