CVE-2023-41101 (published 2023-11-17T06:15Z)

An issue was discovered in the captive portal in OpenNDS before version 10.1.3. get_query in http_microhttpd.c does not validate the length of the query string of GET requests. This leads to a stack-based buffer overflow in versions 9.x and earlier, and to a heap-based buffer overflow in versions 10.x and later. Attackers may exploit the issue to crash OpenNDS (Denial-of-Service condition) or to inject and execute arbitrary bytecode (Remote Code Execution). Affected OpenNDS before version 10.1.3 fixed in OpenWrt master and OpenWrt 23.05 on 23. November by updating OpenNDS to version 10.2.0.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/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/openNDS/openNDS
commit_details: repo_url: https://github.com/openNDS/openNDS
author: Rob White
author_date: 2023-08-25 12:56:59+01:00
msg: Fix - Buffer overflow causing segfault - CVE-2023-41101

Signed-off-by: Rob White <rob@blue-wave.net>
repository: repo_url: https://github.com/openNDS/openNDS
repo_name: openNDS/openNDS
description: openNDS (open Network Demarcation Service) is a high performance, small footprint, Captive Portal.  It provides a border control gateway between a public local area network and the Internet.
repo_language: C
owner: openNDS
file_changes: filename: http_microhttpd.c
change_type: ModificationType.MODIFY
diff: @@ -143,6 +143,7 @@ static int do_binauth(
 
 	// Get the client user agent
 	user_agent = safe_calloc(USER_AGENT);
+
 	MHD_get_connection_values(connection, MHD_HEADER_KIND, get_user_agent_callback, &user_agent);
 
 	if (user_agent == NULL) {
@@ -153,6 +154,7 @@ static int do_binauth(
 
 	// Get custom data string as passed in the query string
 	custom = safe_calloc(CUSTOM);
+
 	custom = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "custom");
 
 	if (!custom || strlen(custom) == 0) {
@@ -977,69 +979,76 @@ static int show_preauthpage(struct MHD_Connection *connection, const char *query
 	int ret;
 	struct MHD_Response *response;
 
-	preauthpath = safe_calloc(SMALL_BUF);
-	safe_asprintf(&preauthpath, "/%s/", config->preauthdir);
+	if ( strlen(query) < 1 ) {
+		// query string is blank, too long or corrupt
+		return send_error(connection, 511);
+	} else {	
+		preauthpath = safe_calloc(SMALL_BUF);
+		safe_asprintf(&preauthpath, "/%s/", config->preauthdir);
 
-	if (strcmp(preauthpath, config->fas_path) == 0) {
-		free (preauthpath);
+		if (strcmp(preauthpath, config->fas_path) == 0) {
+			free (preauthpath);
 
-		user_agent = safe_calloc(USER_AGENT);
-		enc_user_agent = safe_calloc(ENC_USER_AGENT);
+			user_agent = safe_calloc(USER_AGENT);
+			debug(LOG_DEBUG, "PreAuth: User Agent ptr is [ %d ]", &user_agent);
+			enc_user_agent = safe_calloc(ENC_USER_AGENT);
 
-		MHD_get_connection_values(connection, MHD_HEADER_KIND, get_user_agent_callback, &user_agent);
+			MHD_get_connection_values(connection, MHD_HEADER_KIND, get_user_agent_callback, &user_agent);
+			debug(LOG_DEBUG, "PreAuth: MHD User Agent ptr is [ %d ]", &user_agent);
 
-		if (user_agent == NULL) {
-			return send_error(connection, 403);
-		}
+			if (user_agent == NULL) {
+				return send_error(connection, 403);
+			}
 
-		uh_urlencode(enc_user_agent, ENC_USER_AGENT, user_agent, strlen(user_agent));
-		debug(LOG_DEBUG, "PreAuth: Encoded User Agent is [ %s ]", enc_user_agent);
+			uh_urlencode(enc_user_agent, ENC_USER_AGENT, user_agent, strlen(user_agent));
+			debug(LOG_DEBUG, "PreAuth: Encoded User Agent is [ %s ]", enc_user_agent);
 
-		enc_query = safe_calloc(ENC_QUERYSTR);
-		uh_urlencode(enc_query, ENC_QUERYSTR, query, strlen(query));
-		debug(LOG_DEBUG, "PreAuth: Encoded query: %s", enc_query);
+			enc_query = safe_calloc(ENC_QUERYSTR);
+			uh_urlencode(enc_query, ENC_QUERYSTR, query, strlen(query));
+			debug(LOG_DEBUG, "PreAuth: Encoded query: %s", enc_query);
 
-		msg = safe_calloc(HTMLMAXSIZE);
+			msg = safe_calloc(HTMLMAXSIZE);
 
-		if (!msg) {
-			ret = send_error(connection, 503);
-			free(msg);
-			free(enc_user_agent);
-			free(enc_query);
-			return ret;
-		}
+			if (!msg) {
+				ret = send_error(connection, 503);
+				free(msg);
+				free(enc_user_agent);
+				free(enc_query);
+				return ret;
+			}
 
-		cmd = safe_calloc(QUERYMAXLEN);
-		safe_asprintf(&cmd, "%s '%s' '%s' '%d' '%s'", config->preauth, enc_query, enc_user_agent, config->login_option_enabled, config->themespec_path);
-		rc = execute_ret_url_encoded(msg, HTMLMAXSIZE - 1, cmd);
-		free(cmd);
+			cmd = safe_calloc(QUERYMAXLEN);
+			safe_asprintf(&cmd, "%s '%s' '%s' '%d' '%s'", config->preauth, enc_query, enc_user_agent, config->login_option_enabled, config->themespec_path);
+			rc = execute_ret_url_encoded(msg, HTMLMAXSIZE - 1, cmd);
+			free(cmd);
 
-		if (rc != 0) {
-			debug(LOG_WARNING, "Preauth script - failed to execute: %s, Query[%s]", config->preauth, query);
-			free(msg);
-			free(enc_user_agent);
-			free(enc_query);
+			if (rc != 0) {
+				debug(LOG_WARNING, "Preauth script - failed to execute: %s, Query[%s]", config->preauth, query);
+				free(msg);
+				free(enc_user_agent);
+				free(enc_query);
 
-			return send_error(connection, 511);
-		}
+				return send_error(connection, 511);
+			}
 
-		// serve the script output (in msg)
-		response = MHD_create_response_from_buffer(strlen(msg), (char *)msg, MHD_RESPMEM_MUST_FREE);
+			// serve the script output (in msg)
+			response = MHD_create_response_from_buffer(strlen(msg), (char *)msg, MHD_RESPMEM_MUST_FREE);
 
-		if (!response) {
-			return send_error(connection, 503);
-		}
+			if (!response) {
+				return send_error(connection, 503);
+			}
 
-		MHD_add_response_header(response, "Content-Type", "text/html; charset=utf-8");
-		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
-		MHD_destroy_response(response);
+			MHD_add_response_header(response, "Content-Type", "text/html; charset=utf-8");
+			ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+			MHD_destroy_response(response);
 
-		free(enc_user_agent);
-		free(enc_query);
-		return ret;
-	} else {
-		free (preauthpath);
-		return send_error(connection, 404);
+			free(enc_user_agent);
+			free(enc_query);
+			return ret;
+		} else {
+			free (preauthpath);
+			return send_error(connection, 404);
+		}
 	}
 }
 
@@ -1230,6 +1239,7 @@ static int preauthenticated(struct MHD_Connection *connection, const char *url,
 		debug(LOG_DEBUG, "authdir url detected: %s", url);
 
 		redirect_url = safe_calloc(REDIRECT_URL);
+
 		redirect_url = get_redirect_url(connection);
 
 		if (redirect_url == NULL) {
@@ -1740,7 +1750,7 @@ static int get_query(struct MHD_Connection *connection, char **query, const char
 	char *query_str;
 	struct collect_query collect_query;
 	int i;
-	int j;
+	int j = 4;
 	int length = 0;
 
 	debug(LOG_DEBUG, " Getting query, separator is [%s].", separator);
@@ -1767,6 +1777,8 @@ static int get_query(struct MHD_Connection *connection, char **query, const char
 
 		if (i > 0) // q=foo&o=bar the '&' need also some space
 			length++;
+
+		debug(LOG_DEBUG, " cumulative element length is [%d]", length);
 	}
 
 	// don't miss the zero terminator
@@ -1787,21 +1799,22 @@ static int get_query(struct MHD_Connection *connection, char **query, const char
 
 		debug(LOG_DEBUG, " element [%d] is [%s]", i, elements[i]);
 
-		strncpy(*query + j, elements[i], length - j);
-		if (i == 0) {
-			// query_str is empty when i = 0 so safe to copy a single char into it
-			strcpy(query_str, "?");
-		} else {
-			if (QUERYMAXLEN - strlen(query_str) > length - j + 1) {
-				strncat(query_str, separator, QUERYMAXLEN - strlen(query_str));
+		if (length + j < QUERYMAXLEN) {
+
+			strncpy(*query + j, elements[i], length - j);
+
+			if (i == 0) {
+				// query_str is empty when i = 0 so safe to copy a single char into it
+				strcpy(query_str, "?");
+			} else {
+				if (QUERYMAXLEN - strlen(query_str) > length - j + 1) {
+					strncat(query_str, separator, QUERYMAXLEN - strlen(query_str));
+				}
 			}
-		}
 
-		// note: query string will be truncated if too long
-		if (QUERYMAXLEN - strlen(query_str) > length - j) {
 			strncat(query_str, *query, QUERYMAXLEN - strlen(query_str));
 		} else {
-			debug(LOG_WARNING, " Query string exceeds the maximum of %d bytes so has been truncated.", QUERYMAXLEN/2);
+			debug(LOG_WARNING, " Query string is too long, invalid or corrupt so is ignored.");
 		}
 
 		free(elements[i]);

programming_language: C
repo_url: https://github.com/openwrt/routing
commit_details: repo_url: https://github.com/openwrt/routing
author: Rob White
author_date: 2023-08-28 16:19:06+01:00
msg: opennds: update to version 10.1.3

Maintainer: Rob White rob@blue-wave.net

Compile tested: arm_cortex-a7_neon-vfpv4, mipsel_24kc, x86-64

Run tested: arm_cortex-a7_neon-vfpv4, mipsel_24kc, x86-64; on snapshot, 23.05, 22.03

Description:
opennds (10.1.3)

Security Advisory. This version contains fixes for multiple potential security vulnerabilities
Credit - Stanislav Dashevskyi - standash.github.io [standash]
It also contains some minor bug fixes

  * Fix - Buffer overflow causing segfault - CVE-2023-41101 [bluewavenet]
  * Fix - Memory leaks due to passing allocated buffer into safe_asprintf() - CVE-2023-41102 [bluewavenet]
  * Fix - Remove deprecated preauth option [bluewavenet]
  * Fix - missing free in show_preauth_page if MHD does not respond [bluewavenet]
  * Fix - more safe_asprintf memory leaks [bluewavenet]
  * Fix - missing free for mark_auth [bluewavenet]
  * Fix - memory leak after starting authmon daemon [bluewavenet]
  * Fix - memory leak in encode_and_redirect_to_splashpage [bluewavenet]
  * Fix - Community themespec, voucher css and logo image [bluewavenet]
  * Fix - ThemeSpec, path to logo in page footer [bluewavenet]
  * Fix - ensure gatewayurl is urldecoded to fix broken css and images in themespec [bluewavenet]
  * Add - set default fas remote fqdn to disabled [bluewavenet]

Signed-off-by: Rob White <rob@blue-wave.net>
repository: repo_url: https://github.com/openwrt/routing
repo_name: openwrt/routing
description: OpenWrt Routing Packages
repo_language: Shell
owner: openwrt
file_changes: filename: Makefile
change_type: ModificationType.MODIFY
diff: @@ -6,12 +6,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=opennds
-PKG_VERSION:=10.1.2
+PKG_VERSION:=10.1.3
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/opennds/opennds/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=818c24a8704e584665b493857086bccc3c55629977841ddffc185f7d082ef279
+PKG_HASH:=8016a8d20643a2dfc7e8d9e8012e300db6996aeede5ea8a895cb1b95e52f202a
 PKG_BUILD_DIR:=$(BUILD_DIR)/openNDS-$(PKG_VERSION)
 
 PKG_MAINTAINER:=Rob White <rob@blue-wave.net>

programming_language: Shell