CVE-2023-0302 (published 2023-01-15T01:15Z)

Failure to Sanitize Special Elements into a Different Plane (Special Element Injection) in GitHub repository radareorg/radare2 prior to 5.8.2.

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-74
cwe_name: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
description: The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
extended_description: Software or other automated logic has certain assumptions about what constitutes data and control respectively. It is the lack of verification of these assumptions for user-controlled input that leads to injection problems. Injection problems encompass a wide variety of issues -- all mitigated in very different ways and usually attempted in order to alter the control flow of the process. For this reason, the most effective way to discuss these weaknesses is to note the distinct features that classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed.
         

Fix information:
repo_url: https://github.com/radareorg/radare2
commit_details: repo_url: https://github.com/radareorg/radare2
author: pancake
author_date: 2023-01-06 12:55:15+01:00
msg: Fix ANSI Escape Sequence Injection vulns via DWARF ##vuln

* Reported by @solid-snail via huntrdev
* BountyID: 583133af-7ae6-4a21-beef-a4b0182cf82e
* Reproducer: dwarf_test_func_patched
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: meta.c
change_type: ModificationType.MODIFY
diff: @@ -111,12 +111,13 @@ static bool meta_set(RAnal *a, RAnalMetaType type, int subtype, ut64 from, ut64
 	item->subtype = subtype;
 	item->space = space;
 	free (item->str);
-	item->str = str ? strdup (str) : NULL;
-	if (str && !item->str) {
-		if (!node) { // If we just created this
-			free (item);
-		}
-		return false;
+	if (R_STR_ISNOTEMPTY (str)) {
+		item->str = strdup (str);
+		// this breaks the `ecHw` command
+		// (highlights word in current instruction, which uses ansi
+		// r_str_ansi_strip (item->str);
+	} else {
+		item->str = NULL;
 	}
 	R_DIRTY (a);
 	if (!node) {

programming_language: C
filename: dwarf.c
change_type: ModificationType.MODIFY
diff: @@ -431,6 +431,7 @@ static const ut8 *parse_line_header_source(RBinFile *bf, const ut8 *buf, const u
 	int i = 0;
 	size_t count;
 	const ut8 *tmp_buf = NULL;
+	char *fn = NULL;
 
 	if (mode == R_MODE_PRINT) {
 		print (" The Directory Table:\n");
@@ -464,10 +465,12 @@ static const ut8 *parse_line_header_source(RBinFile *bf, const ut8 *buf, const u
 
 	for (i = 0; i < 2; i++) {
 		while (buf + 1 < buf_end) {
-			const char *filename = (const char *)buf;
 			size_t maxlen = R_MIN ((size_t) (buf_end - buf - 1), 0xfff);
 			ut64 id_idx, mod_time, file_len;
-			size_t len = r_str_nlen (filename, maxlen);
+			free (fn);
+			fn = r_str_ndup ((const char *)buf, maxlen);
+			r_str_ansi_strip (fn);
+			size_t len = strlen (fn);
 
 			if (!len) {
 				buf++;
@@ -512,7 +515,7 @@ static const ut8 *parse_line_header_source(RBinFile *bf, const ut8 *buf, const u
 				}
 
 				if (hdr->file_names) {
-					hdr->file_names[count].name = r_str_newf("%s/%s", r_str_get (include_dir), filename);
+					hdr->file_names[count].name = r_str_newf("%s/%s", r_str_get (include_dir), fn);
 					hdr->file_names[count].id_idx = id_idx;
 					hdr->file_names[count].mod_time = mod_time;
 					hdr->file_names[count].file_len = file_len;
@@ -525,7 +528,8 @@ static const ut8 *parse_line_header_source(RBinFile *bf, const ut8 *buf, const u
 			}
 			count++;
 			if (mode == R_MODE_PRINT && i) {
-				print ("  %d     %" PFMT64d "       %" PFMT64d "         %" PFMT64d "          %s\n", entry_index++, id_idx, mod_time, file_len, filename);
+				print ("  %d     %" PFMT64d "       %" PFMT64d "         %" PFMT64d "          %s\n",
+						entry_index++, id_idx, mod_time, file_len, fn);
 			}
 		}
 		if (i == 0) {
@@ -544,6 +548,7 @@ static const ut8 *parse_line_header_source(RBinFile *bf, const ut8 *buf, const u
 	}
 
 beach:
+	free (fn);
 	sdb_free (sdb);
 
 	return buf;
@@ -677,7 +682,6 @@ static const ut8 *parse_line_header(
 
 static inline void add_sdb_addrline(Sdb *s, ut64 addr, const char *file, ut64 line, int mode, PrintfCallback print) {
 	const char *p;
-	char *fileline;
 	char offset[SDB_NUM_BUFSZ];
 	char *offset_ptr;
 
@@ -706,7 +710,10 @@ static inline void add_sdb_addrline(Sdb *s, ut64 addr, const char *file, ut64 li
 #else
 	p = file;
 #endif
-	fileline = r_str_newf ("%s|%"PFMT64d, p, line);
+	char *fileline = r_str_newf ("%s|%"PFMT64d, p, line);
+	r_str_ansi_strip (fileline);
+	r_str_replace_ch (fileline, '\n', 0, true);
+	r_str_replace_ch (fileline, '\t', 0, true);
 	offset_ptr = sdb_itoa (addr, 16, offset, sizeof (offset));
 	sdb_add (s, offset_ptr, fileline, 0);
 	sdb_add (s, fileline, offset_ptr, 0);
@@ -1666,7 +1673,15 @@ static const ut8 *parse_attr_value(const ut8 *obuf, int obuf_len,
 		break;
 	case DW_FORM_string:
 		value->kind = DW_AT_KIND_STRING;
-		value->string.content = *buf ? r_str_ndup ((const char *)buf, buf_end - buf) : NULL;
+		if (*buf) {
+			char *name = r_str_ndup ((const char *)buf, buf_end - buf);
+			r_str_ansi_strip (name);
+			r_str_replace_ch (name, '\n', 0, true);
+			r_str_replace_ch (name, '\t', 0, true);
+			value->string.content = name;
+		} else {
+			value->string.content = NULL;
+		}
 		if (value->string.content) {
 			buf += strlen (value->string.content) + 1;
 		}
@@ -1711,8 +1726,15 @@ static const ut8 *parse_attr_value(const ut8 *obuf, int obuf_len,
 		value->kind = DW_AT_KIND_STRING;
 		value->string.offset = dwarf_read_offset (hdr->is_64bit, &buf, buf_end);
 		if (debug_str && value->string.offset < debug_str_len) {
-			const char *ds = (const char *)(debug_str + value->string.offset);
-			value->string.content = strdup (ds); // r_str_ndup (ds, debug_str_len - value->string.offset);
+			char *ds = r_str_ndup ((const char *)(debug_str + value->string.offset), debug_str_len);
+			if (ds) {
+				r_str_ansi_strip (ds);
+				r_str_replace_ch (ds, '\n', 0, true);
+				r_str_replace_ch (ds, '\t', 0, true);
+				value->string.content = ds;
+			} else {
+				value->string.content = NULL;
+			}
 		} else {
 			value->string.content = NULL; // Means malformed DWARF, should we print error message?
 		}
@@ -1903,8 +1925,11 @@ static const ut8 *parse_die(const ut8 *buf, const ut8 *buf_end, RBinDwarfAbbrevD
 		// Or atleast it needs to rework becase there will be
 		// more comp units -> more comp dirs and only the last one will be kept
 		if (attribute->attr_name == DW_AT_comp_dir && is_valid_string_form) {
-			const char *name = attribute->string.content;
-			sdb_set (sdb, "DW_AT_comp_dir", name, 0);
+			char *name = strdup (attribute->string.content);
+			r_str_ansi_strip (name);
+			r_str_replace_ch (name, '\n', 0, true);
+			r_str_replace_ch (name, '\t', 0, true);
+			sdb_set_owned (sdb, "DW_AT_comp_dir", name, 0);
 		}
 		die->count++;
 	}

programming_language: Lua
filename: hud.c
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-/* radare - LGPL - Copyright 2008-2021 - pancake */
+/* radare - LGPL - Copyright 2008-2023 - pancake */
 
 #include <r_cons.h>
 #include <ctype.h>
@@ -9,6 +9,7 @@
 R_API char *r_cons_hud_file(const char *f) {
 	char *s = r_file_slurp (f, NULL);
 	if (s) {
+		r_str_ansi_strip (s);
 		char *ret = r_cons_hud_string (s);
 		free (s);
 		return ret;
@@ -29,6 +30,7 @@ R_API char *r_cons_hud_line_string(const char *s) {
 	}
 	r_str_replace_ch (o, '\r', 0, true);
 	r_str_replace_ch (o, '\t', 0, true);
+	r_str_ansi_strip (o);
 	RList *fl = r_list_new ();
 	int i;
 	if (!fl) {
@@ -66,6 +68,7 @@ R_API char *r_cons_hud_string(const char *s) {
 	if (!o) {
 		return NULL;
 	}
+	r_str_ansi_strip (o);
 	r_str_replace_ch (o, '\r', 0, true);
 	r_str_replace_ch (o, '\t', 0, true);
 	RList *fl = r_list_new ();

programming_language: C
filename: cmd_meta.c
change_type: ModificationType.MODIFY
diff: @@ -539,10 +539,10 @@ static int cmd_meta_comment(RCore *core, const char *input) {
 		break;
 	case '!':
 		{
-			char *out;
 			const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
-			out = r_core_editor (core, NULL, comment);
+			char *out = r_core_editor (core, NULL, comment);
 			if (out) {
+				r_str_ansi_strip (out);
 				//r_meta_set (core->anal->meta, R_META_TYPE_COMMENT, addr, 0, out);
 				r_core_cmdf (core, "CC-@0x%08"PFMT64x, addr);
 				//r_meta_del (core->anal->meta, input[0], addr, addr+1);
@@ -560,6 +560,7 @@ static int cmd_meta_comment(RCore *core, const char *input) {
 		char *text;
 		char *nc = strdup (newcomment);
 		r_str_unescape (nc);
+		r_str_ansi_strip (nc);
 		if (comment) {
 			text = malloc (strlen (comment) + strlen (newcomment) + 2);
 			if (text) {

programming_language: C
filename: cmd_print.c
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-/* radare - LGPL - Copyright 2009-2022 - pancake */
+/* radare - LGPL - Copyright 2009-2023 - pancake */
 
 #include <r_core.h>
 #include <limits.h>

programming_language: C
filename: str.c
change_type: ModificationType.MODIFY
diff: @@ -1957,14 +1957,16 @@ R_API size_t r_str_ansi_nlen(const char *str, size_t slen) {
 }
 
 // remove ansi escape codes from string, decolorizing it
+// TODO : optimize by just using two counter variables instead of strcpy()
 R_API size_t r_str_ansi_strip(char *str) {
 	size_t i = 0;
 	while (str[i]) {
 		size_t chlen = __str_ansi_length (str + i);
 		if (chlen > 1) {
-			r_str_cpy (str + i + 1, str + i + chlen);
+			r_str_cpy (str + i, str + i + chlen);
+		} else {
+			i++;
 		}
-		i++;
 	}
 	return i;
 }

programming_language: C
filename: dwarf
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,55 @@
+NAME="ansi injection via dwarf"
+FILE=bins/elf/dwarf_test_func_patched
+ARGS=-AA
+CMDS=<<EOF
+CL
+xc
+EOF
+EXPECT=<<EOF
+file: /_test.c
+line: 5
+addr: 0x00001159
+file: /_test.c
+line: 11
+addr: 0x00001184
+file: /_test.c
+line: 6
+addr: 0x00001165
+file: /_test.c
+line: 11
+addr: 0x00001186
+file: /_test.c
+line: 9
+addr: 0x00001170
+file: /_test.c
+line: 8
+addr: 0x00001168
+file: /_test.c
+line: 4
+addr: 0x00001149
+file: /_test.c
+line: 10
+addr: 0x0000117f
+- offset -  6061 6263 6465 6667 6869 6A6B 6C6D 6E6F  0123456789ABCDEF  comment
+0x00001060  f30f 1efa 31ed 4989 d15e 4889 e248 83e4  ....1.I..^H..H..  ; rip ; [16] -r-x section size 294 named .text ; arg3
+0x00001070  f050 5445 31c0 31c9 488d 3de9 0000 00ff  .PTE1.1.H.=.....
+0x00001080  1553 2f00 00f4 662e 0f1f 8400 0000 0000  .S/...f.........
+0x00001090  488d 3d79 2f00 0048 8d05 722f 0000 4839  H.=y/..H..r/..H9  ; sym.deregister_tm_clones
+0x000010a0  f874 1548 8b05 362f 0000 4885 c074 09ff  .t.H..6/..H..t..
+0x000010b0  e00f 1f80 0000 0000 c30f 1f80 0000 0000  ................
+0x000010c0  488d 3d49 2f00 0048 8d35 422f 0000 4829  H.=I/..H.5B/..H)  ; sym.register_tm_clones
+0x000010d0  fe48 89f0 48c1 ee3f 48c1 f803 4801 c648  .H..H..?H...H..H
+0x000010e0  d1fe 7414 488b 0505 2f00 0048 85c0 7408  ..t.H.../..H..t.
+0x000010f0  ffe0 660f 1f44 0000 c30f 1f80 0000 0000  ..f..D..........
+0x00001100  f30f 1efa 803d 052f 0000 0075 2b55 4883  .....=./...u+UH.  ; sym.__do_global_dtors_aux
+0x00001110  3de2 2e00 0000 4889 e574 0c48 8b3d e62e  =.....H..t.H.=..
+0x00001120  0000 e819 ffff ffe8 64ff ffff c605 dd2e  ........d.......
+0x00001130  0000 015d c30f 1f00 c30f 1f80 0000 0000  ...]............
+0x00001140  f30f 1efa e977 ffff fff3 0f1e fa55 4889  .....w.......UH.  ; sym.frame_dummy  ; dbg._func ; void _func(char * msg);
+0x00001150  e548 83ec 1048 897d f848 8b45 f848 89c7  .H...H.}.H.E.H.. ; arg1 ; const char *s
+EOF
+RUN
+
 NAME="open companion file for macho dwarf"
 FILE=bins/mach0/dwarf/a.out
 CMDS=<<EOF

programming_language: Shell