CVE-2023-49294 (published 2023-12-14T20:15Z)

Asterisk is an open source private branch exchange and telephony toolkit. In Asterisk prior to versions 18.20.1, 20.5.1, and 21.0.1, as well as certified-asterisk prior to 18.9-cert6, it is possible to read any arbitrary file even when the `live_dangerously` is not enabled. This allows arbitrary files to be read. Asterisk versions 18.20.1, 20.5.1, and 21.0.1, as well as certified-asterisk prior to 18.9-cert6, contain a fix for this issue.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/asterisk/asterisk
commit_details: repo_url: https://github.com/asterisk/asterisk
author: Ben Ford
author_date: 2023-11-13 11:08:50-06:00
msg: manager.c: Prevent path traversal with GetConfig.

When using AMI GetConfig, it was possible to access files outside of the
Asterisk configuration directory by using filenames with ".." and "./"
even while live_dangerously was not enabled. This change resolves the
full path and ensures we are still in the configuration directory before
attempting to access the file.
repository: repo_url: https://github.com/asterisk/asterisk
repo_name: asterisk/asterisk
description: The official Asterisk Project repository.
repo_language: C
owner: asterisk
file_changes: filename: manager.c
change_type: ModificationType.MODIFY
diff: @@ -3752,12 +3752,43 @@ void astman_live_dangerously(int new_live_dangerously)
 	live_dangerously = new_live_dangerously;
 }
 
+/**
+ * \brief Check if a file is restricted or not
+ *
+ * \return 0 on success
+ * \return 1 on restricted file
+ * \return -1 on failure
+ */
 static int restrictedFile(const char *filename)
 {
-	if (!live_dangerously && !strncasecmp(filename, "/", 1) &&
-		 strncasecmp(filename, ast_config_AST_CONFIG_DIR, strlen(ast_config_AST_CONFIG_DIR))) {
+	char *stripped_filename;
+	RAII_VAR(char *, path, NULL, ast_free);
+	RAII_VAR(char *, real_path, NULL, ast_free);
+
+	if (live_dangerously) {
+		return 0;
+	}
+
+	stripped_filename = ast_strip(ast_strdupa(filename));
+
+	/* If the file path starts with '/', don't prepend ast_config_AST_CONFIG_DIR */
+	if (stripped_filename[0] == '/') {
+		real_path = realpath(stripped_filename, NULL);
+	} else {
+		if (ast_asprintf(&path, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
+			return -1;
+		}
+		real_path = realpath(path, NULL);
+	}
+
+	if (!real_path) {
+		return -1;
+	}
+
+	if (!ast_begins_with(real_path, ast_config_AST_CONFIG_DIR)) {
 		return 1;
 	}
+
 	return 0;
 }
 
@@ -3770,6 +3801,7 @@ static int action_getconfig(struct mansession *s, const struct message *m)
 	const char *category_name;
 	int catcount = 0;
 	int lineno = 0;
+	int ret = 0;
 	struct ast_category *cur_category = NULL;
 	struct ast_variable *v;
 	struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
@@ -3779,9 +3811,13 @@ static int action_getconfig(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
-	if (restrictedFile(fn)) {
+	ret = restrictedFile(fn);
+	if (ret == 1) {
 		astman_send_error(s, m, "File requires escalated priveledges");
 		return 0;
+	} else if (ret == -1) {
+		astman_send_error(s, m, "Config file not found");
+		return 0;
 	}
 
 	cfg = ast_config_load2(fn, "manager", config_flags);

programming_language: C